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,107 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Alert Notification profile client.
*
* 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 ANPC_API_H
#define ANPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup ALERT_NOTIFICATION_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered */
enum
{
ANPC_ANS_SNAC_HDL_IDX, /*!< \brief Supported new alert category */
ANPC_ANS_NA_HDL_IDX, /*!< \brief New alert */
ANPC_ANS_NA_CCC_HDL_IDX, /*!< \brief New alert CCC descriptor */
ANPC_ANS_SUAC_HDL_IDX, /*!< \brief Supported unread alert category */
ANPC_ANS_UAS_HDL_IDX, /*!< \brief Unread alert status */
ANPC_ANS_UAS_CCC_HDL_IDX, /*!< \brief Unread alert status CCC descriptor */
ANPC_ANS_ANCP_HDL_IDX, /*!< \brief Alert notification control point */
ANPC_ANS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Alert Notification service.
* Parameter pHdlList must point to an array of length \ref ANPC_ANS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void AnpcAnsDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Send a command to the alert notification control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param command Control point command.
* \param catId Alert category ID.
*
* \return None.
*/
/*************************************************************************************************/
void AnpcAnsControl(dmConnId_t connId, uint16_t handle, uint8_t command, uint8_t catId);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref ANPC_ANS_HDL_LIST_LEN.
* If the ATT handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t AnpcAnsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* ALERT_NOTIFICATION_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* ANPC_API_H */
@@ -0,0 +1,238 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Alert Notification profile client.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "app_api.h"
#include "anpc_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Alert Notification service
*/
/* Characteristics for discovery */
/*! Supported new alert category */
static const attcDiscChar_t anpcAnsSnac =
{
attSnacChUuid,
ATTC_SET_REQUIRED
};
/*! New alert */
static const attcDiscChar_t anpcAnsNa =
{
attNaChUuid,
ATTC_SET_REQUIRED
};
/*! New alert CCC descriptor */
static const attcDiscChar_t anpcAnsNaCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Supported unread alert category */
static const attcDiscChar_t anpcAnsSuac =
{
attSuacChUuid,
ATTC_SET_REQUIRED
};
/*! Unread alert status */
static const attcDiscChar_t anpcAnsUas =
{
attUasChUuid,
ATTC_SET_REQUIRED
};
/*! Unread alert status CCC descriptor */
static const attcDiscChar_t anpcAnsUasCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Alert notification control point */
static const attcDiscChar_t anpcAnsAncp =
{
attAncpChUuid,
ATTC_SET_REQUIRED
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *anpcAnsDiscCharList[] =
{
&anpcAnsSnac, /*! Supported new alert category */
&anpcAnsNa, /*! New alert */
&anpcAnsNaCcc, /*! New alert CCC descriptor */
&anpcAnsSuac, /*! Supported unread alert category */
&anpcAnsUas, /*! Unread alert status */
&anpcAnsUasCcc, /*! Unread alert status CCC descriptor */
&anpcAnsAncp /*! Alert notification control point */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(ANPC_ANS_HDL_LIST_LEN == ((sizeof(anpcAnsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Alert Notification service. Parameter
* pHdlList must point to an array of length ANPC_ANS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void AnpcAnsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attAnsSvcUuid,
ANPC_ANS_HDL_LIST_LEN, (attcDiscChar_t **) anpcAnsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Send a command to the alert notification control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param command Control point command.
* \param catId Alert category ID.
*
* \return None.
*/
/*************************************************************************************************/
void AnpcAnsControl(dmConnId_t connId, uint16_t handle, uint8_t command, uint8_t catId)
{
uint8_t buf[2];
if (handle != ATT_HANDLE_NONE)
{
buf[0] = command;
buf[1] = catId;
AttcWriteReq(connId, handle, sizeof(buf), buf);
}
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length ANPC_ANS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t AnpcAnsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t *p;
uint16_t catIdMask;
uint8_t catId;
uint8_t numAlert;
uint8_t status = ATT_SUCCESS;
uint8_t buf[19];
/* Suppress unused variable compile warning */
(void)catIdMask; (void)catId; (void)numAlert;
/* new alert */
if (pMsg->handle == pHdlList[ANPC_ANS_NA_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(catId, p);
BSTREAM_TO_UINT8(numAlert, p);
/* null terminate string before printing */
memcpy(buf, p, pMsg->valueLen - 2);
buf[pMsg->valueLen - 2] = '\0';
APP_TRACE_INFO2("New alert cat:%d num:%d", catId, numAlert);
APP_TRACE_INFO1("Msg:%s", buf);
}
/* unread alert status */
else if (pMsg->handle == pHdlList[ANPC_ANS_UAS_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(catId, p);
BSTREAM_TO_UINT8(numAlert, p);
APP_TRACE_INFO2("Unread alert status cat:%d num:%d", catId, numAlert);
}
/* supported new alert category */
else if (pMsg->handle == pHdlList[ANPC_ANS_SNAC_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
if (pMsg->valueLen == 1)
{
BSTREAM_TO_UINT8(catIdMask, p);
}
else
{
BSTREAM_TO_UINT16(catIdMask, p);
}
APP_TRACE_INFO1("Supported new alert category: 0x%04x", catIdMask);
}
/* supported unread alert category */
else if (pMsg->handle == pHdlList[ANPC_ANS_SUAC_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
if (pMsg->valueLen == 1)
{
BSTREAM_TO_UINT8(catIdMask, p);
}
else
{
BSTREAM_TO_UINT16(catIdMask, p);
}
APP_TRACE_INFO1("Supported unread alert category: 0x%04x", catIdMask);
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,206 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Asset Tracking profile client.
*
* 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.
*/
/*************************************************************************************************/
#ifndef ATPC_API_H
#define ATPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup ASSET_TRACKING_PROFILE_CLIENT
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered. */
enum
{
ATPC_CTE_ENABLE_HDL, /*!< \brief Constant Tone Extension enable. */
ATPC_CTE_MIN_LEN_HDL, /*!< \brief Constant Tone Extension minimum length. */
ATPC_CTE_ADV_MIN_TX_CNT_HDL, /*!< \brief Constant Tone Extension minimum transmit count. */
ATPC_CTE_ADV_TX_DURATION_HDL, /*!< \brief Constant Tone Extension transmit duration. */
ATPC_CTE_ADV_INTERVAL_HDL, /*!< \brief Constant Tone Extension interval. */
ATPC_CTE_ADV_EXT_PHY_HDL, /*!< \brief Constant Tone Extension PHY. */
ATPC_CTE_HDL_LIST_LEN, /*!< \brief Handle list length. */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Constant Tone Extension service.
* Parameter pHdlList must point to an array of length \ref ATPC_CTE_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension enable attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param enable Enable.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteEnable(dmConnId_t connId, uint16_t handle, uint8_t enable);
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension minimum length attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param minLen Minimum length.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteMinLen(dmConnId_t connId, uint16_t handle, uint8_t minLen);
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension minimum transmit count attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param txCount Minimum transmit count.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteMinTxCount(dmConnId_t connId, uint16_t handle, uint8_t txCount);
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension transmit duration attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param duration Transmit duration.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteTxDuration(dmConnId_t connId, uint16_t handle, uint8_t duration);
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension interval attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param interval Interval.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteInterval(dmConnId_t connId, uint16_t handle, uint16_t interval);
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension PHY attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param phy PHY.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWritePhy(dmConnId_t connId, uint16_t handle, uint8_t phy);
/*************************************************************************************************/
/*!
* \brief Set the antenna identifiers for a connection ID.
*
* \param connId Connection identifier.
* \param numAntenna Number of antenna and len of pAntennaIds in bytes.
* \param pAntennaIds Array containing identifiers of antenna for this connection.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcSetAntennaIds(dmConnId_t connId, uint8_t numAntenna, uint8_t *pAntennaIds);
/*************************************************************************************************/
/*!
* \brief Enable AoA CTE Rx request over ACL.
*
* \param connId Connection identifier.
* \param handle CTE enable attribute handle.
* \param length Request length.
* \param interval Request interval.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteAclEnableReq(dmConnId_t connId, uint16_t handle, uint8_t length, uint16_t interval);
/*************************************************************************************************/
/*!
* \brief Disable AoA CTE Rx request over ACL.
*
* \param connId Connection identifier.
* \param handle CTE enable attribute handle.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteAclDisableReq(dmConnId_t connId, uint16_t handle);
/*************************************************************************************************/
/*!
* \brief Called by application to notify the ATPC of System Events.
*
* \param pEvt Pointer to the Event.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcProcMsg(wsfMsgHdr_t *pEvt);
/*! \} */ /* ASSET_TRACKING_PROFILE_CLIENT */
#ifdef __cplusplus
};
#endif
#endif /* ATPC_API_H */
@@ -0,0 +1,418 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Asset Tracking profile client.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "app_api.h"
#include "atpc_api.h"
#include "svc_cte.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Asset tracking profile connection states. */
#define ATPC_CONN_STATE_ENABLING 0
#define ATPC_CONN_STATE_DISABLING 1
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! Asset Tracking Profile Client connection control block. */
typedef struct
{
uint16_t enableHandle; /*! CTE enable attribute handle. */
uint8_t state; /*! Connection state. */
uint8_t length; /*! Min CTE length. */
uint16_t interval; /*! CTE interval. */
bool_t numAntenna; /*! Number of antenna and len of pAntennaIds in bytes. */
uint8_t *pAntennaIds; /*! Array containing identifiers of antenna for this connection. */
} atpcConnCb_t;
/*! Asset Tracking Profile Server control block. */
typedef struct
{
atpcConnCb_t connCb[DM_CONN_MAX]; /*! Connection control block. */
uint8_t switchSampleRates; /*! Supported Switching Sampling Rates. */
uint8_t switchPatternMaxLen; /*! Max Length of Switching Pattern. */
uint8_t numAntennae; /*! Number of Antennae. */
uint8_t cteMaxLen; /*! Max CTE Length. */
} atpcCb_t;
static atpcCb_t atpcCb;
/*! Constant Tone Extension enable. */
static const attcDiscChar_t atpcCteEnable =
{
attCteEnChUuid,
ATTC_SET_REQUIRED
};
/*! Constant Tone Extension minimum length. */
static const attcDiscChar_t atpcCteMinLen =
{
attCteMinLenChUuid,
ATTC_SET_REQUIRED
};
/*! Constant Tone Extension minimum transmit count. */
static const attcDiscChar_t atpcCteTxCnt =
{
attCteTxCntChUuid,
ATTC_SET_REQUIRED
};
/*! Constant Tone Extension transmit duration. */
static const attcDiscChar_t atpcCteDurationc =
{
attCteTxDurChUuid,
ATTC_SET_REQUIRED
};
/*! Constant Tone Extension interval. */
static const attcDiscChar_t atpcCteInterval =
{
attCteIntChUuid,
ATTC_SET_REQUIRED
};
/*! Constant Tone Extension PHY. */
static const attcDiscChar_t atpcCtePhy =
{
attCtePhyChUuid,
ATTC_SET_REQUIRED
};
/*! List of characteristics to be discovered; order matches handle index enumeration. */
static const attcDiscChar_t *atpcCteDiscCharList[] =
{
&atpcCteEnable, /*! Constant Tone Extension enable. */
&atpcCteMinLen, /*! Constant Tone Extension minimum length. */
&atpcCteTxCnt, /*! Constant Tone Extension minimum transmit count. */
&atpcCteDurationc, /*! Constant Tone Extension transmit duration. */
&atpcCteInterval, /*! Constant Tone Extension interval. */
&atpcCtePhy, /*! Constant Tone Extension PHY. */
};
/*! sanity check: make sure handle list length matches characteristic list length. */
WSF_CT_ASSERT(ATPC_CTE_HDL_LIST_LEN == ((sizeof(atpcCteDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Called by application to notify the profile of System Events.
*
* \param pEvt Pointer to the Event.
*
* \return None.
*/
/*************************************************************************************************/
static void atpcProcWriteRsp(attEvt_t *pEvt)
{
dmConnId_t connId = (dmConnId_t) pEvt->hdr.param;
if ((pEvt->hdr.status == ATT_SUCCESS) && (pEvt->handle == atpcCb.connCb[connId-1].enableHandle))
{
atpcConnCb_t *pCcb = &atpcCb.connCb[connId-1];
switch (pCcb->state)
{
case ATPC_CONN_STATE_ENABLING:
DmConnCteRxSampleStart(connId, HCI_CTE_SLOT_DURATION_2_US, pCcb->numAntenna, pCcb->pAntennaIds);
break;
case ATPC_CONN_STATE_DISABLING:
DmConnCteRxSampleStop(connId);
break;
default:
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Constant Tone Extension service.
* Parameter pHdlList must point to an array of length \ref ATPC_CTE_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attCteSvcUuid,
ATPC_CTE_HDL_LIST_LEN, (attcDiscChar_t **) atpcCteDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension enable attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param enable Enable.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteEnable(dmConnId_t connId, uint16_t handle, uint8_t enable)
{
WSF_ASSERT(handle != ATT_HANDLE_NONE);
AttcWriteReq(connId, handle, sizeof(uint8_t), &enable);
}
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension minimum length attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param minLen Minimum length.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteMinLen(dmConnId_t connId, uint16_t handle, uint8_t minLen)
{
WSF_ASSERT(handle != ATT_HANDLE_NONE);
AttcWriteReq(connId, handle, sizeof(uint8_t), &minLen);
}
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension minimum transmit count attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param txCount Minimum transmit count.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteMinTxCount(dmConnId_t connId, uint16_t handle, uint8_t txCount)
{
WSF_ASSERT(handle != ATT_HANDLE_NONE);
AttcWriteReq(connId, handle, sizeof(uint8_t), &txCount);
}
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension transmit duration attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param duration Transmit duration.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteTxDuration(dmConnId_t connId, uint16_t handle, uint8_t duration)
{
WSF_ASSERT(handle != ATT_HANDLE_NONE);
AttcWriteReq(connId, handle, sizeof(uint8_t), &duration);
}
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension interval attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param interval Interval.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWriteInterval(dmConnId_t connId, uint16_t handle, uint16_t interval)
{
WSF_ASSERT(handle != ATT_HANDLE_NONE);
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t *p = buf;
UINT16_TO_BSTREAM(p, interval);
AttcWriteReq(connId, handle, sizeof(uint16_t), buf);
}
/*************************************************************************************************/
/*!
* \brief Write to the Constant Tone Extension PHY attribute.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param phy PHY.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteWritePhy(dmConnId_t connId, uint16_t handle, uint8_t phy)
{
WSF_ASSERT(handle != ATT_HANDLE_NONE)
AttcWriteReq(connId, handle, sizeof(uint8_t), &phy);
}
/*************************************************************************************************/
/*!
* \brief Enable AoA CTE Rx request over ACL.
*
* \param connId Connection identifier.
* \param handle CTE enable attribute handle.
* \param length Request length.
* \param interval Request interval.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteAclEnableReq(dmConnId_t connId, uint16_t handle, uint8_t length, uint16_t interval)
{
atpcConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
WSF_ASSERT(handle != ATT_HANDLE_NONE)
/* Store the enable handle and CTE configuration */
pCcb = &atpcCb.connCb[connId-1];
pCcb->enableHandle = handle;
pCcb->length = length;
pCcb->interval = interval;
pCcb->state = ATPC_CONN_STATE_ENABLING;
AtpcCteWriteEnable(connId, handle, CTE_ENABLE_ACL_BIT);
}
/*************************************************************************************************/
/*!
* \brief Disable AoA CTE Rx request over ACL.
*
* \param connId Connection identifier.
* \param handle CTE enable attribute handle.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcCteAclDisableReq(dmConnId_t connId, uint16_t handle)
{
atpcConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
WSF_ASSERT(handle != ATT_HANDLE_NONE)
/* Set the enableHandle to ATT_HANDLE_NONE to prevent CTE rx req on ATTC_WRITE_RSP. */
pCcb = &atpcCb.connCb[connId-1];
pCcb->enableHandle = handle;
pCcb->state = ATPC_CONN_STATE_DISABLING;
AtpcCteWriteEnable(connId, handle, CTE_ENABLE_NONE);
}
/*************************************************************************************************/
/*!
* \brief Set the antenna identifiers for a connection ID.
*
* \param connId Connection identifier.
* \param numAntenna Number of antenna and len of pAntennaIds in bytes.
* \param pAntennaIds Array containing identifiers of antenna for this connection.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcSetAntennaIds(dmConnId_t connId, uint8_t numAntenna, uint8_t *pAntennaIds)
{
atpcConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
WSF_ASSERT(numAntenna <= atpcCb.numAntennae);
pCcb = &atpcCb.connCb[connId-1];
pCcb->numAntenna = numAntenna;
pCcb->pAntennaIds = pAntennaIds;
}
/*************************************************************************************************/
/*!
* \brief Called by application to notify the ATPC of System Events.
*
* \param pEvt Pointer to the Event.
*
* \return None.
*/
/*************************************************************************************************/
void AtpcProcMsg(wsfMsgHdr_t *pEvt)
{
atpcConnCb_t *pCcb = &atpcCb.connCb[pEvt->param - 1];
dmEvt_t *pDmEvt = (dmEvt_t*) pEvt;
switch (pEvt->event)
{
case DM_RESET_CMPL_IND:
DmReadAntennaInfo();
break;
case DM_READ_ANTENNA_INFO_IND:
atpcCb.cteMaxLen = pDmEvt->readAntennaInfo.cteMaxLen;
atpcCb.switchSampleRates = pDmEvt->readAntennaInfo.switchSampleRates;
atpcCb.numAntennae = pDmEvt->readAntennaInfo.numAntennae;
atpcCb.switchPatternMaxLen = pDmEvt->readAntennaInfo.switchPatternMaxLen;
break;
case ATTC_WRITE_RSP:
atpcProcWriteRsp((attEvt_t *)pEvt);
break;
case DM_CONN_CTE_RX_SAMPLE_START_IND:
if ((pEvt->status == HCI_SUCCESS) && (pCcb->state == ATPC_CONN_STATE_ENABLING))
{
DmConnCteReqStart((dmConnId_t) pEvt->param, pCcb->interval, pCcb->length, HCI_CTE_TYPE_REQ_AOA);
}
break;
case DM_CONN_CTE_RX_SAMPLE_STOP_IND:
if ((pEvt->status == HCI_SUCCESS) && (pCcb->state == ATPC_CONN_STATE_DISABLING))
{
DmConnCteReqStop((dmConnId_t) pEvt->param);
}
break;
case DM_CTE_REQ_FAIL_IND:
break;
default:
break;
}
}
@@ -0,0 +1,79 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Asset Tracking profile server.
*
* 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 ATPS_API_H
#define ATPS_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup ASSET_TRACKING_PROFILE_SERVER
* \{ */
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Asset Tracking Profile server initialization.
*
* \return None.
*/
/*************************************************************************************************/
void AtpsInit(void);
/*************************************************************************************************/
/*!
* \brief Set the antenna identifiers for a connection ID.
*
* \param connId Connection identifier.
* \param numAntenna Number of antenna and len of pAntennaIds in bytes.
* \param pAntennaIds Array containing identifiers of antenna for this connection.
*
* \return None.
*/
/*************************************************************************************************/
void AtpsSetAntennaIds(dmConnId_t connId, uint8_t numAntenna, uint8_t *pAntennaIds);
/*************************************************************************************************/
/*!
* \brief Called by application to notify the Asset Tracking Profile server of DM Events.
*
* \param pEvt Pointer to the DM Event.
*
* \return None.
*/
/*************************************************************************************************/
void AtpsProcDmMsg(dmEvt_t *pEvt);
/*! \} */ /* ASSET_TRACKING_PROFILE_SERVER */
#ifdef __cplusplus
};
#endif
#endif /* ATPS_API_H */
@@ -0,0 +1,430 @@
/*!
* \file
*
* \brief Asset Tracking profile server.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "hci_defs.h"
#include "app_api.h"
#include "atps_api.h"
#include "svc_cte.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Asset tracking profile connection states. */
#define ATPS_STATE_ACL_DISABLED 0
#define ATPS_STATE_ACL_STARTING 1
#define ATPS_STATE_ACL_ENABLED 2
#define ATPS_STATE_ACL_STOPPING 3
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! Asset Tracking Profile Server connection control block. */
typedef struct
{
uint8_t state; /*! Connection state. */
uint8_t enableBits; /*! Constant Tone Extension enable. */
uint8_t minLen; /*! Constant Tone Extension minimum length. */
uint8_t minTxCount; /*! Constant Tone Extension minimum transmit count. */
uint8_t duration; /*! Constant Tone Extension transmit duration. */
uint16_t interval; /*! Constant Tone Extension interval. */
uint8_t phyType; /*! Constant Tone Extension PHY type. */
bool_t numAntenna; /*! Number of antenna and len of pAntennaIds in bytes. */
uint8_t *pAntennaIds; /*! Array containing identifiers of antenna for this connection. */
} atpsConnCb_t;
/*! Asset Tracking Profile Server control block. */
typedef struct
{
atpsConnCb_t connCb[DM_CONN_MAX]; /*! Connection control block. */
uint8_t switchSampleRates; /*! Supported Switching Sampling Rates. */
uint8_t switchPatternMaxLen; /*! Max Length of Switching Pattern. */
uint8_t numAntennae; /*! Number of Antennae. */
uint8_t cteMaxLen; /*! Max CTE Length. */
} atpsCb_t;
static atpsCb_t atpsCb;
/*************************************************************************************************/
/*!
* \brief Process write enable attribute from locator.
*
* \param connId Connection identifier.
* \param enableBits Enable bitfield value from client.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t atpsCteSetEnable(dmConnId_t connId, uint8_t enableBits)
{
atpsConnCb_t *pCcb;
bool_t enableAcl = !!(enableBits & CTE_ENABLE_ACL_BIT);
if (enableBits & CTE_ENABLE_ADV_BIT)
{
/* Connectionless advertising CTE not supported. */
return ATT_ERR_WRITE_REJ;
}
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
pCcb = &atpsCb.connCb[connId-1];
if (enableAcl)
{
if (pCcb->state == ATPS_STATE_ACL_DISABLED)
{
pCcb->state = ATPS_STATE_ACL_STARTING;
DmConnCteTxConfig(connId, HCI_CTE_TYPE_PERMIT_AOA_RSP_BIT, pCcb->numAntenna, pCcb->pAntennaIds);
/* Delay write response */
return ATT_RSP_PENDING;
}
}
else
{
if (pCcb->state == ATPS_STATE_ACL_ENABLED)
{
pCcb->state = ATPS_STATE_ACL_STOPPING;
DmConnCteRspStop(connId);
/* Delay write response */
return ATT_RSP_PENDING;
}
}
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process write minimum length attribute from locator.
*
* \param connId Connection identifier.
* \param minLen Minimum length value.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t atpsCteSetMinLen(dmConnId_t connId, uint8_t minLen)
{
atpsConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
if ((minLen < CTE_MIN_MIN_LEN) || (minLen > CTE_MAX_MIN_LEN) || (minLen > atpsCb.cteMaxLen))
{
return ATT_ERR_RANGE;
}
pCcb = &atpsCb.connCb[connId-1];
pCcb->minLen = minLen;
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process write advertising minimum tx count attribute from locator.
*
* \param connId Connection identifier.
* \param minLen Minimum tx count value.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t atpsCteSetAdvMinTxCnt(dmConnId_t connId, uint8_t minTxCount)
{
atpsConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
if ((minTxCount < CTE_MIN_MIN_TX_CNT) || (minTxCount > CTE_MAX_MIN_TX_CNT))
{
return ATT_ERR_RANGE;
}
pCcb = &atpsCb.connCb[connId-1];
pCcb->minTxCount = minTxCount;
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process write advertising minimum tx duration attribute from locator.
*
* \param connId Connection identifier.
* \param duration Minimum tx count value.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t atpsCteSetAdvTxDuration(dmConnId_t connId, uint8_t duration)
{
atpsConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
pCcb = &atpsCb.connCb[connId-1];
pCcb->duration = duration;
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process write advertising intergval attribute from locator.
*
* \param connId Connection identifier.
* \param interval Interval value.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t atpsCteSetAdvInterval(dmConnId_t connId, uint8_t interval)
{
atpsConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
if (interval < CTE_MIN_INTERVAL)
{
return ATT_ERR_RANGE;
}
pCcb = &atpsCb.connCb[connId-1];
pCcb->interval = interval;
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process write advertising PHY attribute from locator.
*
* \param connId Connection identifier.
* \param phyType PHY type.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t atpsCteSetAdvPhy(dmConnId_t connId, uint8_t phyType)
{
atpsConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
if (phyType > CTE_PHY_2M)
{
return ATT_ERR_RANGE;
}
pCcb = &atpsCb.connCb[connId-1];
pCcb->phyType = phyType;
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for Continuous Tone Extension, CTE, service.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t atpsCteWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
uint8_t status;
/* Only individual write requests with response are supported. */
if (operation != ATT_PDU_WRITE_REQ)
{
return ATT_ERR_NOT_SUP;
}
switch (handle)
{
case CTE_ENABLE_HDL:
status = atpsCteSetEnable(connId, *pValue);
break;
case CTE_MIN_LEN_HDL:
status = atpsCteSetMinLen(connId, *pValue);
break;
case CTE_ADV_MIN_TX_CNT_HDL:
status = atpsCteSetAdvMinTxCnt(connId, *pValue);
break;
case CTE_ADV_TX_DURATION_HDL:
status = atpsCteSetAdvTxDuration(connId, *pValue);
break;
case CTE_ADV_INTERVAL_HDL:
status = atpsCteSetAdvInterval(connId, *pValue);
break;
case CTE_ADV_EXT_PHY_HDL:
status = atpsCteSetAdvPhy(connId, *pValue);
break;
default:
status = ATT_ERR_HANDLE;
break;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Set the antenna identifiers for a connection ID.
*
* \param connId Connection identifier.
* \param numAntenna Number of antenna and len of pAntennaIds in bytes.
* \param pAntennaIds Array containing identifiers of antenna for this connection.
*
* \return None.
*/
/*************************************************************************************************/
void AtpsSetAntennaIds(dmConnId_t connId, uint8_t numAntenna, uint8_t *pAntennaIds)
{
atpsConnCb_t *pCcb;
WSF_ASSERT((connId > DM_CONN_ID_NONE) && (connId <= DM_CONN_MAX));
WSF_ASSERT(numAntenna <= atpsCb.numAntennae);
pCcb = &atpsCb.connCb[connId-1];
pCcb->numAntenna = numAntenna;
pCcb->pAntennaIds = pAntennaIds;
}
/*************************************************************************************************/
/*!
* \brief Called by application to notify the Asset Tracking Profile server of DM Events.
*
* \param pEvt Pointer to the DM Event.
*
* \return None.
*/
/*************************************************************************************************/
void AtpsProcDmMsg(dmEvt_t *pEvt)
{
uint8_t status;
atpsConnCb_t *pCcb = &atpsCb.connCb[pEvt->hdr.param - 1];
switch (pEvt->hdr.event)
{
case DM_RESET_CMPL_IND:
DmReadAntennaInfo();
break;
case DM_READ_ANTENNA_INFO_IND:
atpsCb.cteMaxLen = pEvt->readAntennaInfo.cteMaxLen;
atpsCb.switchSampleRates = pEvt->readAntennaInfo.switchSampleRates;
atpsCb.numAntennae = pEvt->readAntennaInfo.numAntennae;
atpsCb.switchPatternMaxLen = pEvt->readAntennaInfo.switchPatternMaxLen;
break;
case DM_CONN_OPEN_IND:
pCcb->state = ATPS_STATE_ACL_DISABLED;
break;
case DM_CONN_CTE_TX_CFG_IND:
if (pEvt->hdr.status == HCI_SUCCESS)
{
DmConnCteRspStart((dmConnId_t) pEvt->hdr.param);
}
else
{
AttsContinueWriteReq((dmConnId_t) pEvt->hdr.param, CTE_ENABLE_HDL, ATT_ERR_WRITE_REJ);
pCcb->state = ATPS_STATE_ACL_DISABLED;
}
break;
case DM_CONN_CTE_RSP_START_IND:
status = (pEvt->hdr.status == HCI_SUCCESS) ? ATT_SUCCESS : ATT_ERR_WRITE_REJ;
AttsContinueWriteReq((dmConnId_t) pEvt->hdr.param, CTE_ENABLE_HDL, status);
if (status == ATT_SUCCESS)
{
pCcb->enableBits |= CTE_ENABLE_ACL_BIT;
pCcb->state = ATPS_STATE_ACL_ENABLED;
}
else
{
pCcb->state = ATPS_STATE_ACL_DISABLED;
}
break;
case DM_CONN_CTE_RSP_STOP_IND:
status = (pEvt->hdr.status == HCI_SUCCESS) ? ATT_SUCCESS : ATT_ERR_WRITE_REJ;
AttsContinueWriteReq((dmConnId_t) pEvt->hdr.param, CTE_ENABLE_HDL, status);
if (status == ATT_SUCCESS)
{
pCcb->enableBits &= ~CTE_ENABLE_ACL_BIT;
pCcb->state = ATPS_STATE_ACL_DISABLED;
}
else
{
pCcb->state = ATPS_STATE_ACL_ENABLED;
}
break;
case DM_CTE_REQ_FAIL_IND:
if (pCcb->state == ATPS_STATE_ACL_STARTING)
{
pCcb->state = ATPS_STATE_ACL_DISABLED;
}
else if (pCcb->state == ATPS_STATE_ACL_STOPPING)
{
pCcb->state = ATPS_STATE_ACL_ENABLED;
}
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Asset Tracking Profile server initialization.
*
* \return None.
*/
/*************************************************************************************************/
void AtpsInit(void)
{
SvcCteCbackRegister(atpsCteWriteCback);
SvcCteAddGroup();
}
@@ -0,0 +1,138 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Battery service server.
*
* 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 BAS_API_H
#define BAS_API_H
#include "wsf_timer.h"
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup BATTERY_PROFILE
* \{ */
/**************************************************************************************************
Data Types
**************************************************************************************************/
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*! \brief Battery service configurable parameters */
typedef struct
{
wsfTimerTicks_t period; /*!< \brief Battery measurement timer expiration period in seconds */
uint16_t count; /*!< \brief Perform battery measurement after this many timer periods */
uint8_t threshold; /*!< \brief Send battery level notification to peer when below this level. */
} basCfg_t;
/*************************************************************************************************/
/*!
* \brief Initialize the battery service server.
*
* \param handlerId WSF handler ID of the application using this service.
* \param pCfg Battery service configurable parameters.
*
* \return None.
*/
/*************************************************************************************************/
void BasInit(wsfHandlerId_t handlerId, basCfg_t *pCfg);
/*************************************************************************************************/
/*!
* \brief Start periodic battery level measurement. This function starts a timer to perform
* periodic battery measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param battCccIdx Index of battery level CCC descriptor in CCC descriptor handle table.
*
* \return None.
*/
/*************************************************************************************************/
void BasMeasBattStart(dmConnId_t connId, uint8_t timerEvt, uint8_t battCccIdx);
/*************************************************************************************************/
/*!
* \brief Stop periodic battery level measurement.
*
* \param connId DM connection identifier.
*
* \return None.
*/
/*************************************************************************************************/
void BasMeasBattStop(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Process received WSF message.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void BasProcMsg(wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Send the battery level to the peer device.
*
* \param connId DM connection identifier.
* \param idx Index of battery level CCC descriptor in CCC descriptor handle table.
* \param level The battery level.
*
* \return None.
*/
/*************************************************************************************************/
void BasSendBattLevel(dmConnId_t connId, uint8_t idx, uint8_t level);
/*************************************************************************************************/
/*!
* \brief ATTS read callback for battery service used to read the battery level. Use this
* function as a parameter to SvcBattCbackRegister().
*
* \param connId DM connection identifier.
* \param handle ATT handle.
* \param operation ATT operation.
* \param offset read offset.
* \param pAttr pointer to Attribute
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t BasReadCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, attsAttr_t *pAttr);
/*! \} */ /* BATTERY_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* BAS_API_H */
@@ -0,0 +1,368 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Battery service server.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_batt.h"
#include "app_api.h"
#include "app_hw.h"
#include "bas_api.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief battery level initialization value */
#define BAS_BATT_LEVEL_INIT 0xFF
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! \brief Connection control block */
typedef struct
{
dmConnId_t connId; /*! \brief Connection ID */
bool_t battToSend; /*! \brief battery measurement ready to be sent on this channel */
uint8_t sentBattLevel; /*! \brief value of last sent battery level */
} basConn_t;
/*! \brief Control block */
static struct
{
basConn_t conn[DM_CONN_MAX]; /*! \brief connection control block */
wsfTimer_t measTimer; /*! \brief periodic measurement timer */
basCfg_t cfg; /*! \brief configurable parameters */
uint16_t currCount; /*! \brief current measurement period count */
bool_t txReady; /*! \brief TRUE if ready to send notifications */
uint8_t measBattLevel; /*! \brief value of last measured battery level */
} basCb;
/*************************************************************************************************/
/*!
* \brief Return TRUE if no connections with active measurements.
*
* \return TRUE if no connections active.
*/
/*************************************************************************************************/
static bool_t basNoConnActive(void)
{
basConn_t *pConn = basCb.conn;
uint8_t i;
for (i = 0; i < DM_CONN_MAX; i++, pConn++)
{
if (pConn->connId != DM_CONN_ID_NONE)
{
return FALSE;
}
}
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Setup to send measurements on active connections.
*
* \return None.
*/
/*************************************************************************************************/
static void basSetupToSend(void)
{
basConn_t *pConn = basCb.conn;
uint8_t i;
for (i = 0; i < DM_CONN_MAX; i++, pConn++)
{
if (pConn->connId != DM_CONN_ID_NONE)
{
pConn->battToSend = TRUE;
}
}
}
/*************************************************************************************************/
/*!
* \brief Find next connection with measurement to send.
*
* \param cccIdx Battery measurement CCC descriptor index.
*
* \return Connection control block.
*/
/*************************************************************************************************/
static basConn_t *basFindNextToSend(uint8_t cccIdx)
{
basConn_t *pConn = basCb.conn;
uint8_t i;
for (i = 0; i < DM_CONN_MAX; i++, pConn++)
{
if (pConn->connId != DM_CONN_ID_NONE && pConn->battToSend &&
pConn->sentBattLevel != basCb.measBattLevel)
{
if (AttsCccEnabled(pConn->connId, cccIdx))
{
return pConn;
}
}
}
return NULL;
}
/*************************************************************************************************/
/*!
* \brief Send periodic battery measurement.
*
* \param pConn Connection control block.
*
* \return None.
*/
/*************************************************************************************************/
static void basSendPeriodicBattlevel(basConn_t *pConn)
{
BasSendBattLevel(pConn->connId, basCb.measTimer.msg.status, basCb.measBattLevel);
pConn->sentBattLevel = basCb.measBattLevel;
pConn->battToSend = FALSE;
basCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Handle connection open.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void basConnOpen(dmEvt_t *pMsg)
{
basCb.txReady = TRUE;
}
/*************************************************************************************************/
/*!
* \brief Handle a received ATT handle value confirm.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void basHandleValueCnf(attEvt_t *pMsg)
{
basConn_t *pConn;
if (pMsg->hdr.status == ATT_SUCCESS && pMsg->handle == BATT_LVL_HDL)
{
basCb.txReady = TRUE;
/* find next connection to send (note ccc idx is stored in timer status) */
if ((pConn = basFindNextToSend(basCb.measTimer.msg.status)) != NULL)
{
basSendPeriodicBattlevel(pConn);
}
}
}
/*************************************************************************************************/
/*!
* \brief This function is called by the application when the periodic measurement
* timer expires.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void basMeasTimerExp(wsfMsgHdr_t *pMsg)
{
basConn_t *pConn;
/* if there are active connections */
if (basNoConnActive() == FALSE)
{
if (--basCb.currCount == 0)
{
/* reset count */
basCb.currCount = basCb.cfg.count;
/* set up battery measurement to be sent on all connections */
basSetupToSend();
/* read battery measurement sensor data */
AppHwBattRead(&basCb.measBattLevel);
/* if ready to send measurements */
if (basCb.txReady)
{
/* find next connection to send (note ccc idx is stored in timer status) */
if ((pConn = basFindNextToSend(pMsg->status)) != NULL)
{
basSendPeriodicBattlevel(pConn);
}
}
}
/* restart timer */
WsfTimerStartSec(&basCb.measTimer, basCb.cfg.period);
}
}
/*************************************************************************************************/
/*!
* \brief Initialize the battery service server.
*
* \param handerId WSF handler ID of the application using this service.
* \param pCfg Battery service configurable parameters.
*
* \return None.
*/
/*************************************************************************************************/
void BasInit(wsfHandlerId_t handlerId, basCfg_t *pCfg)
{
basCb.measTimer.handlerId = handlerId;
basCb.cfg = *pCfg;
}
/*************************************************************************************************/
/*!
* \brief Start periodic battery level measurement. This function starts a timer to perform
* periodic battery measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param battCccIdx Index of battery level CCC descriptor in CCC descriptor handle table.
*
* \return None.
*/
/*************************************************************************************************/
void BasMeasBattStart(dmConnId_t connId, uint8_t timerEvt, uint8_t battCccIdx)
{
/* if this is first connection */
if (basNoConnActive())
{
/* initialize control block */
basCb.measTimer.msg.event = timerEvt;
basCb.measTimer.msg.status = battCccIdx;
basCb.measBattLevel = BAS_BATT_LEVEL_INIT;
basCb.currCount = basCb.cfg.count;
/* start timer */
WsfTimerStartSec(&basCb.measTimer, basCb.cfg.period);
}
/* set conn id and last sent battery level */
basCb.conn[connId - 1].connId = connId;
basCb.conn[connId - 1].sentBattLevel = BAS_BATT_LEVEL_INIT;
}
/*************************************************************************************************/
/*!
* \brief Stop periodic battery level measurement.
*
* \param connId DM connection identifier.
*
* \return None.
*/
/*************************************************************************************************/
void BasMeasBattStop(dmConnId_t connId)
{
/* clear connection */
basCb.conn[connId - 1].connId = DM_CONN_ID_NONE;
basCb.conn[connId - 1].battToSend = FALSE;
/* if no remaining connections */
if (basNoConnActive())
{
/* stop timer */
WsfTimerStop(&basCb.measTimer);
}
}
/*************************************************************************************************/
/*!
* \brief Process received WSF message.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void BasProcMsg(wsfMsgHdr_t *pMsg)
{
if (pMsg->event == DM_CONN_OPEN_IND)
{
basConnOpen((dmEvt_t *) pMsg);
}
else if (pMsg->event == ATTS_HANDLE_VALUE_CNF)
{
basHandleValueCnf((attEvt_t *) pMsg);
}
else if (pMsg->event == basCb.measTimer.msg.event)
{
basMeasTimerExp(pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Send the battery level to the peer device.
*
* \param connId DM connection identifier.
* \param idx Index of battery level CCC descriptor in CCC descriptor handle table.
* \param level The battery level.
*
* \return None.
*/
/*************************************************************************************************/
void BasSendBattLevel(dmConnId_t connId, uint8_t idx, uint8_t level)
{
if (AttsCccEnabled(connId, idx))
{
AttsHandleValueNtf(connId, BATT_LVL_HDL, CH_BATT_LEVEL_LEN, &level);
}
}
/*************************************************************************************************/
/*!
* \brief ATTS read callback for battery service used to read the battery level. Use this
* function as a parameter to SvcBattCbackRegister().
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t BasReadCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, attsAttr_t *pAttr)
{
/* read the battery level and set attribute value */
AppHwBattRead(pAttr->pValue);
return ATT_SUCCESS;
}
@@ -0,0 +1,91 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Blood Pressure profile client.
*
* 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 BLPC_API_H
#define BLPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup BLOOD_PRESSURE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Blood Pressure service enumeration of handle indexes of characteristics to be discovered */
enum
{
BLPC_BPS_BPM_HDL_IDX, /*!< \brief Blood pressure measurement */
BLPC_BPS_BPM_CCC_HDL_IDX, /*!< \brief Blood pressure measurement CCC descriptor */
BLPC_BPS_ICP_HDL_IDX, /*!< \brief Intermediate cuff pressure */
BLPC_BPS_ICP_CCC_HDL_IDX, /*!< \brief Intermediate cuff pressure CCC descriptor */
BLPC_BPS_BPF_HDL_IDX, /*!< \brief Blood pressure feature */
BLPC_BPS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Blood Pressure service.
* Parameter pHdlList must point to an array of length \ref BLPC_BPS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void BlpcBpsDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref BLPC_BPS_HDL_LIST_LEN.
* If the ATT handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t BlpcBpsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* BLOOD_PRESSURE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* BLPC_API_H */
@@ -0,0 +1,264 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Blood Pressure profile collector.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "svc_ch.h"
#include "app_api.h"
#include "blpc_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Blood Pressure service
*/
/* Characteristics for discovery */
/*! Blood pressure measurement */
static const attcDiscChar_t blpcBpsBpm =
{
attBpmChUuid,
ATTC_SET_REQUIRED
};
/*! Blood pressure measurement CCC descriptor */
static const attcDiscChar_t blpcBpsBpmCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Intermediate cuff pressure */
static const attcDiscChar_t blpcBpsIcp =
{
attIcpChUuid,
0
};
/*! Intermediate cuff pressure CCC descriptor */
static const attcDiscChar_t blpcBpsIcpCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! Blood pressure feature */
static const attcDiscChar_t blpcBpsBpf =
{
attBpfChUuid,
ATTC_SET_REQUIRED
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *blpcBpsDiscCharList[] =
{
&blpcBpsBpm, /*! Blood pressure measurement */
&blpcBpsBpmCcc, /*! Blood pressure measurement CCC descriptor */
&blpcBpsIcp, /*! Intermediate cuff pressure */
&blpcBpsIcpCcc, /*! Intermediate cuff pressure CCC descriptor */
&blpcBpsBpf /*! Blood pressure feature */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(BLPC_BPS_HDL_LIST_LEN == ((sizeof(blpcBpsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Parse a blood pressure measurement.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void blpcBpsParseBpm(uint8_t *pValue, uint16_t len)
{
uint8_t flags;
uint16_t systolic, diastolic, map;
uint16_t year;
uint8_t month, day, hour, min, sec;
uint16_t pulseRate;
uint8_t userId;
uint16_t measStatus;
uint16_t minLen = CH_BPM_FLAGS_LEN + CH_BPM_MEAS_LEN;
/* Suppress unused variable compile warning */
(void)systolic; (void)diastolic; (void)map;
(void)year; (void)month; (void)day; (void)hour; (void)min; (void)sec;
(void)pulseRate; (void)userId; (void)measStatus;
if (len > 0)
{
/* get flags */
BSTREAM_TO_UINT8(flags, pValue);
/* determine expected minimum length based on flags */
if (flags & CH_BPM_FLAG_TIMESTAMP)
{
minLen += CH_BPM_TIMESTAMP_LEN;
}
if (flags & CH_BPM_FLAG_PULSE_RATE)
{
minLen += CH_BPM_PULSE_RATE_LEN;
}
if (flags & CH_BPM_FLAG_USER_ID)
{
minLen += CH_BPM_USER_ID_LEN;
}
if (flags & CH_BPM_FLAG_MEAS_STATUS)
{
minLen += CH_BPM_MEAS_STATUS_LEN;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Blood Pressure meas len:%d minLen:%d", len, minLen);
return;
}
/* blood pressure */
BSTREAM_TO_UINT16(systolic, pValue);
BSTREAM_TO_UINT16(diastolic, pValue);
BSTREAM_TO_UINT16(map, pValue);
APP_TRACE_INFO3(" Systolic:0x%04x Diastolic:0x%04x MAP:0x%04x", systolic, diastolic, map);
/* timestamp */
if (flags & CH_BPM_FLAG_TIMESTAMP)
{
BSTREAM_TO_UINT16(year, pValue);
BSTREAM_TO_UINT8(month, pValue);
BSTREAM_TO_UINT8(day, pValue);
BSTREAM_TO_UINT8(hour, pValue);
BSTREAM_TO_UINT8(min, pValue);
BSTREAM_TO_UINT8(sec, pValue);
APP_TRACE_INFO3(" Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3(" Time: %02d:%02d:%02d", hour, min, sec);
}
/* pulse rate */
if (flags & CH_BPM_FLAG_PULSE_RATE)
{
BSTREAM_TO_UINT16(pulseRate, pValue);
APP_TRACE_INFO1(" Pulse rate:0x%04x", pulseRate);
}
/* user id */
if (flags & CH_BPM_FLAG_USER_ID)
{
BSTREAM_TO_UINT8(userId, pValue);
APP_TRACE_INFO1(" User ID:%d", userId);
}
/* measurement status */
if (flags & CH_BPM_FLAG_MEAS_STATUS)
{
BSTREAM_TO_UINT16(measStatus, pValue);
APP_TRACE_INFO1(" Meas. status:0x%04x", measStatus);
}
APP_TRACE_INFO1(" Flags:0x%02x", flags);
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Blood Pressure service. Parameter
* pHdlList must point to an array of length BLPC_BPS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void BlpcBpsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attBpsSvcUuid,
BLPC_BPS_HDL_LIST_LEN, (attcDiscChar_t **) blpcBpsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length BLPC_BPS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t BlpcBpsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint16_t feature;
uint8_t *p;
uint8_t status = ATT_SUCCESS;
/* Suppress unused variable compile warning */
(void)feature;
/* blood pressure measurement */
if (pMsg->handle == pHdlList[BLPC_BPS_BPM_HDL_IDX])
{
APP_TRACE_INFO0("BP measurement");
/* parse value */
blpcBpsParseBpm(pMsg->pValue, pMsg->valueLen);
}
/* intermediate cuff pressure */
else if (pMsg->handle == pHdlList[BLPC_BPS_ICP_HDL_IDX])
{
APP_TRACE_INFO0("BP intermed. cuff pressure");
/* parse value */
blpcBpsParseBpm(pMsg->pValue, pMsg->valueLen);
}
/* blood pressure feature */
else if (pMsg->handle == pHdlList[BLPC_BPS_BPF_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT16(feature, p);
APP_TRACE_INFO1("BP feature:0x%04x", feature);
}
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,136 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Blood Pressure profile sensor.
*
* 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 BLPS_API_H
#define BLPS_API_H
#include "wsf_timer.h"
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup BLOOD_PRESSURE_PROFILE
* \{ */
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Configurable parameters */
typedef struct
{
wsfTimerTicks_t period; /*!< \brief Measurement timer expiration period in ms */
} blpsCfg_t;
/*************************************************************************************************/
/*!
* \brief Initialize the Blood Pressure profile sensor.
*
* \param handlerId WSF handler ID of the application using this service.
* \param pCfg Configurable parameters.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsInit(wsfHandlerId_t handlerId, blpsCfg_t *pCfg);
/*************************************************************************************************/
/*!
* \brief Start periodic blood pressure measurement. This function starts a timer to perform
* periodic measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param icpCccIdx Index of intermediate cuff pressure CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsMeasStart(dmConnId_t connId, uint8_t timerEvt, uint8_t icpCccIdx);
/*************************************************************************************************/
/*!
* \brief Stop periodic blood pressure measurement.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsMeasStop(void);
/*************************************************************************************************/
/*!
* \brief Blood pressure measurement complete.
*
* \param connId DM connection identifier.
* \param bpmCccIdx Index of blood pressure measurement CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsMeasComplete(dmConnId_t connId, uint8_t bpmCccIdx);
/*************************************************************************************************/
/*!
* \brief This function is called by the application when the periodic measurement
* timer expires.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsProcMsg(wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Set the blood pressure measurement flags.
*
* \param flags Blood pressure measurement flags.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsSetBpmFlags(uint8_t flags);
/*************************************************************************************************/
/*!
* \brief Set the intermediate cuff pressure flags.
*
* \param flags Intermediate cuff pressure flags.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsSetIcpFlags(uint8_t flags);
/*! \} */ /* BLOOD_PRESSURE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* BLPS_API_H */
@@ -0,0 +1,256 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Blood Pressure profile sensor.
*
* 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 "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_bps.h"
#include "app_api.h"
#include "app_hw.h"
#include "blps_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
wsfTimer_t measTimer; /* periodic measurement timer */
appBpm_t bpm; /* blood pressure measurement */
blpsCfg_t cfg; /* configurable parameters */
uint8_t bpmFlags; /* blood pressure measurement flags */
uint8_t icpFlags; /* intermediate cuff pressure flags */
} blpsCb;
/*************************************************************************************************/
/*!
* \brief Build a blood pressure measurement characteristic.
*
* \param pBuf Pointer to buffer to hold the built blood pressure measurement characteristic.
* \param pBpm Blood pressure measurement values.
*
* \return Length of pBuf in bytes.
*/
/*************************************************************************************************/
static uint8_t blpsBuildBpm(uint8_t *pBuf, appBpm_t *pBpm)
{
uint8_t *p = pBuf;
uint8_t flags = pBpm->flags;
/* flags */
UINT8_TO_BSTREAM(p, flags);
/* measurement */
UINT16_TO_BSTREAM(p, pBpm->systolic);
UINT16_TO_BSTREAM(p, pBpm->diastolic);
UINT16_TO_BSTREAM(p, pBpm->map);
/* time stamp */
if (flags & CH_BPM_FLAG_TIMESTAMP)
{
UINT16_TO_BSTREAM(p, pBpm->timestamp.year);
UINT8_TO_BSTREAM(p, pBpm->timestamp.month);
UINT8_TO_BSTREAM(p, pBpm->timestamp.day);
UINT8_TO_BSTREAM(p, pBpm->timestamp.hour);
UINT8_TO_BSTREAM(p, pBpm->timestamp.min);
UINT8_TO_BSTREAM(p, pBpm->timestamp.sec);
}
/* pulse rate */
if (flags & CH_BPM_FLAG_PULSE_RATE)
{
UINT16_TO_BSTREAM(p, pBpm->pulseRate);
}
/* user id */
if (flags & CH_BPM_FLAG_USER_ID)
{
UINT8_TO_BSTREAM(p, pBpm->userId);
}
/* measurement status */
if (flags & CH_BPM_FLAG_MEAS_STATUS)
{
UINT16_TO_BSTREAM(p, pBpm->measStatus);
}
/* return length */
return (uint8_t) (p - pBuf);
}
/*************************************************************************************************/
/*!
* \brief Initialize the Blood Pressure profile sensor.
*
* \param handerId WSF handler ID of the application using this service.
* \param pCfg Configurable parameters.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsInit(wsfHandlerId_t handlerId, blpsCfg_t *pCfg)
{
blpsCb.measTimer.handlerId = handlerId;
blpsCb.cfg = *pCfg;
}
/*************************************************************************************************/
/*!
* \brief Start periodic blood pressure measurement. This function starts a timer to perform
* periodic measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param icpCccIdx Index of intermediate cuff pressure CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsMeasStart(dmConnId_t connId, uint8_t timerEvt, uint8_t icpCccIdx)
{
/* initialize control block */
blpsCb.measTimer.msg.param = connId;
blpsCb.measTimer.msg.event = timerEvt;
blpsCb.measTimer.msg.status = icpCccIdx;
/* start timer */
WsfTimerStartMs(&blpsCb.measTimer, blpsCb.cfg.period);
}
/*************************************************************************************************/
/*!
* \brief Stop periodic blood pressure measurement.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsMeasStop(void)
{
/* stop timer */
WsfTimerStop(&blpsCb.measTimer);
}
/*************************************************************************************************/
/*!
* \brief Blood pressure measurement complete.
*
* \param connId DM connection identifier.
* \param bpmCccIdx Index of blood pressure measurement CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsMeasComplete(dmConnId_t connId, uint8_t bpmCccIdx)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* stop periodic measurement */
BlpsMeasStop();
/* if indications enabled */
if (AttsCccEnabled(connId, bpmCccIdx))
{
/* read blood pressure measurement sensor data */
AppHwBpmRead(FALSE, &blpsCb.bpm);
/* set flags */
blpsCb.bpm.flags = blpsCb.bpmFlags;
/* build blood pressure measurement characteristic */
len = blpsBuildBpm(buf, &blpsCb.bpm);
/* send blood pressure measurement indication */
AttsHandleValueInd(connId, BPS_BPM_HDL, len, buf);
}
}
/*************************************************************************************************/
/*!
* \brief This function is called by the application when the periodic measurement
* timer expires.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsProcMsg(wsfMsgHdr_t *pMsg)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* if notifications enabled (note ccc idx is stored in hdr.status) */
if (AttsCccEnabled((dmConnId_t) pMsg->param, pMsg->status))
{
/* read blood pressure measurement sensor data */
AppHwBpmRead(TRUE, &blpsCb.bpm);
/* set flags */
blpsCb.bpm.flags = blpsCb.icpFlags;
/* build blood pressure measurement characteristic */
len = blpsBuildBpm(buf, &blpsCb.bpm);
/* send intermediate cuff pressure notification */
AttsHandleValueNtf((dmConnId_t) pMsg->param, BPS_ICP_HDL, len, buf);
/* restart timer */
WsfTimerStartMs(&blpsCb.measTimer, blpsCb.cfg.period);
}
}
/*************************************************************************************************/
/*!
* \brief Set the blood pressure measurement flags.
*
* \param flags Blood pressure measurement flags.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsSetBpmFlags(uint8_t flags)
{
blpsCb.bpmFlags = flags;
}
/*************************************************************************************************/
/*!
* \brief Set the intermediate cuff pressure flags.
*
* \param flags Intermediate cuff pressure flags.
*
* \return None.
*/
/*************************************************************************************************/
void BlpsSetIcpFlags(uint8_t flags)
{
blpsCb.icpFlags = flags;
}
@@ -0,0 +1,127 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Cycling Power Profile API.
*
* 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.
*/
/*************************************************************************************************/
#ifndef CPP_API_H
#define CPP_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup CYCLING_POWER_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/** \name Cycle Power Measurement Types
*
*/
/**@{*/
#define CPP_PM_PARAM_INSTANTANEOUS_POWER 0 /* Instantaneous Power Measurement */
#define CPP_PM_PARAM_PEDAL_POWER 1 /* Pedal Power Balance */
#define CPP_PM_PARAM_ACCUMULATED_TORQUE 2 /* Accumulated Torque */
#define CPP_PM_PARAM_WHEEL_REVOLUTIONS 3 /* Wheel Revolution */
#define CPP_PM_PARAM_LAST_WHEEL_REV_TIME 4 /* Last Wheel Revolution Event Time */
#define CPP_PM_PARAM_CRANK_REVOLUTIONS 5 /* Crank Revolution */
#define CPP_PM_PARAM_LAST_CRANK_TIME 6 /* Last Crank Revolution Event Time */
#define CPP_PM_PARAM_MAX_FORCE_MAGNITUDE 7 /* Max Extreme Force Magnitudes */
#define CPP_PM_PARAM_MIN_FORCE_MAGNITUDE 8 /* Min Extreme Force Magnitudes */
#define CPP_PM_PARAM_MAX_TORQUE_MAGNITUDE 9 /* Max Extreme Torque Magnitudes */
#define CPP_PM_PARAM_MIN_TORQUE_MAGNITUDE 10 /* Min Extreme Torque Magnitudes */
#define CPP_PM_PARAM_MAX_EXTREME_ANGLE 11 /* Max Extreme Angles */
#define CPP_PM_PARAM_MIN_EXTREME_ANGLE 12 /* Min Extreme Angles */
#define CPP_PM_PARAM_TOP_DEAD_SPOT 13 /* Top Dead Spot Angle */
#define CPP_PM_PARAM_BOTTOM_DEAD_SPOT 14 /* Bottom Dead Spot Angle */
#define CPP_PM_PARAM_ACCUMULATED_ENERGY 15 /* Accumulated Energy */
/**@}*/
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Setup connection specific variables.
*
* \param connId Connection ID
*
* \return none
*/
/*************************************************************************************************/
void CppsConnOpen(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Notifies the collector of a Cycle Power Measurement.
*
* \param connId Connection ID
*
* \return none
*/
/*************************************************************************************************/
void CppsSendPowerMeasurement(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Set a cycle measurement parameter.
*
* \param type Parameter identifier
* \param value Measurement value.
*
* \return none
*/
/*************************************************************************************************/
void CppsSetParameter(uint8_t type, uint32_t value);
/*************************************************************************************************/
/*!
* \brief Set the sensor location attribute.
*
* \param location Sensor Location.
*
* \return none
*/
/*************************************************************************************************/
void CppsSetSensorLocation(uint8_t location);
/*************************************************************************************************/
/*!
* \brief Set the features attribute.
*
* \param features Features bitmask.
*
* \return none
*/
/*************************************************************************************************/
void CppsSetFeatures(uint32_t features);
/*! \} */ /* CYCLING_POWER_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* CPP_API_H */
@@ -0,0 +1,358 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Cycling Power Profile Sensor Implementation.
*
* 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 "util/bstream.h"
#include "dm_api.h"
#include "svc_cps.h"
#include "svc_ch.h"
#include "cpp_api.h"
/*************************************************************************************************
* Constant Definitions
*************************************************************************************************/
/*! \brief The maximum length of a power measurement */
#define CPPS_PM_MAX_LEN 34
/*! \brief The maximum length of a field in a power measurement */
#define CPPS_PM_MAX_FIELD_LEN 6
/*! \brief Power Measurement Flag Indicies */
enum
{
CPPS_PPBP_FLAG_INDEX, /* Pedal Power Balance Present */
CPPS_PPBR_FLAG_INDEX, /* Pedal Power Balance Reference */
CPPS_ATP_FLAG_INDEX, /* Accumulated Torque Present */
CPPS_ATS_FLAG_INDEX, /* Accumulated Torque Source */
CPPS_WRDP_FLAG_INDEX, /* Wheel Revolution Data Present */
CPPS_CRDP_FLAG_INDEX, /* Crank Revolution Data Present */
CPPS_EFMP_FLAG_INDEX, /* Extreme Force Magnitudes Present */
CPPS_ETMP_FLAG_INDEX, /* Extreme Torque Magnitudes Present */
CPPS_EAP_FLAG_INDEX, /* Extreme Angles Present */
CPPS_TDSAP_FLAG_INDEX, /* Top Dead Spot Angle Present */
CPPS_BDSAP_FLAG_INDEX, /* Bottom Dead Spot Angle Present */
CPPS_AEP_FLAG_INDEX, /* Accumulated Energy Present */
CPPS_OCI_FLAG_INDEX, /* Offset Compensation Indicator */
CPPS_NUM_FLAGS
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Power Measurement Data */
typedef struct
{
uint16_t flags; /* Power Measurement Flags */
uint16_t insantaneousPower; /* Instantaneous Power Measurement */
uint8_t powerBalance; /* Pedal Power Balance */
uint16_t accumulatedTorque; /* Accumulated Torque */
uint32_t wheelRevolutions; /* Wheel Revolution */
uint16_t wheelEventTime; /* Last Wheel Revolution Event Time */
uint16_t crankRevolutions; /* Crank Revolution */
uint16_t crankEventTime; /* Last Crank Revolution Event Time */
uint16_t maxForceMagnitude; /* Max Extreme Force Magnitudes */
uint16_t minForceMagnitude; /* Min Extreme Force Magnitudes */
uint16_t maxTorqueMagnitude; /* Max Extreme Torque Magnitudes */
uint16_t minTorqueMagnitude; /* Min Extreme Torque Magnitudes */
uint16_t maxAngle; /* Max Extreme Angles */
uint16_t minAngle; /* Min Extreme Angles */
uint16_t topDeadSpotAngle; /* Top Dead Spot Angle */
uint16_t btmDeadSpotAngle; /* Bottom Dead Spot Angle */
uint16_t accumulatedEnergy; /* Accumulated Energy */
} cppPmData_t;
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! \brief measurement data */
cppPmData_t cppsPmData;
/*! \brief index of measurement notification when MTU is too small to send all at once */
uint8_t nextMeasFlag[DM_CONN_MAX];
/*************************************************************************************************/
/*!
* \brief Set the sensor location attribute.
*
* \param location Sensor Location.
*
* \return none
*/
/*************************************************************************************************/
void CppsSetSensorLocation(uint8_t location)
{
AttsSetAttr(CPS_CPSL_HDL, sizeof(uint8_t), &location);
}
/*************************************************************************************************/
/*!
* \brief Set the features attribute.
*
* \param features Features bitmask.
*
* \return none
*/
/*************************************************************************************************/
void CppsSetFeatures(uint32_t features)
{
uint8_t tempData[4] = {UINT32_TO_BYTES(features)};
AttsSetAttr(CPS_CPF_HDL, sizeof(tempData), tempData);
}
/*************************************************************************************************/
/*!
* \brief Set a cycling power measurement parameter.
*
* \param type Parameter identifier
* \param value Measurement value.
*
* \return none
*/
/*************************************************************************************************/
void CppsSetParameter(uint8_t type, uint32_t value)
{
switch (type)
{
case CPP_PM_PARAM_INSTANTANEOUS_POWER:
cppsPmData.insantaneousPower = (uint16_t) value;
break;
case CPP_PM_PARAM_PEDAL_POWER:
cppsPmData.flags |= (1 << CPPS_PPBP_FLAG_INDEX);
cppsPmData.powerBalance = (uint8_t) value;
break;
case CPP_PM_PARAM_ACCUMULATED_TORQUE:
cppsPmData.flags |= (1 << CPPS_ATP_FLAG_INDEX);
cppsPmData.accumulatedTorque = (uint16_t) value;
break;
case CPP_PM_PARAM_WHEEL_REVOLUTIONS:
cppsPmData.flags |= (1 << CPPS_WRDP_FLAG_INDEX);
cppsPmData.wheelRevolutions = value;
break;
case CPP_PM_PARAM_LAST_WHEEL_REV_TIME:
cppsPmData.flags |= (1 << CPPS_WRDP_FLAG_INDEX);
cppsPmData.wheelEventTime = (uint16_t) value;
break;
case CPP_PM_PARAM_CRANK_REVOLUTIONS:
cppsPmData.flags |= (1 << CPPS_CRDP_FLAG_INDEX);
cppsPmData.crankRevolutions = (uint16_t) value;
break;
case CPP_PM_PARAM_LAST_CRANK_TIME:
cppsPmData.flags |= (1 << CPPS_CRDP_FLAG_INDEX);
cppsPmData.crankEventTime = (uint16_t) value;
break;
case CPP_PM_PARAM_MAX_FORCE_MAGNITUDE:
cppsPmData.flags |= (1 << CPPS_EFMP_FLAG_INDEX);
cppsPmData.maxForceMagnitude = (uint16_t) value;
break;
case CPP_PM_PARAM_MIN_FORCE_MAGNITUDE:
cppsPmData.flags |= (1 << CPPS_EFMP_FLAG_INDEX);
cppsPmData.minForceMagnitude = (uint16_t) value;
break;
case CPP_PM_PARAM_MAX_TORQUE_MAGNITUDE:
cppsPmData.flags |= (1 << CPPS_ETMP_FLAG_INDEX);
cppsPmData.maxTorqueMagnitude = (uint16_t) value;
break;
case CPP_PM_PARAM_MIN_TORQUE_MAGNITUDE:
cppsPmData.flags |= (1 << CPPS_ETMP_FLAG_INDEX);
cppsPmData.minTorqueMagnitude = (uint16_t) value;
break;
case CPP_PM_PARAM_MAX_EXTREME_ANGLE:
cppsPmData.flags |= (1 << CPPS_EAP_FLAG_INDEX);
cppsPmData.maxAngle = (uint16_t) value;
break;
case CPP_PM_PARAM_MIN_EXTREME_ANGLE:
cppsPmData.flags |= (1 << CPPS_EAP_FLAG_INDEX);
cppsPmData.minAngle = (uint16_t) value;
break;
case CPP_PM_PARAM_TOP_DEAD_SPOT:
cppsPmData.flags |= (1 << CPPS_TDSAP_FLAG_INDEX);
cppsPmData.topDeadSpotAngle = (uint16_t) value;
break;
case CPP_PM_PARAM_BOTTOM_DEAD_SPOT:
cppsPmData.flags |= (1 << CPPS_BDSAP_FLAG_INDEX);
cppsPmData.btmDeadSpotAngle = (uint16_t) value;
break;
case CPP_PM_PARAM_ACCUMULATED_ENERGY:
cppsPmData.flags |= (1 << CPPS_AEP_FLAG_INDEX);
cppsPmData.accumulatedEnergy = (uint16_t) value;
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Notifies the collector of a Cycle Power Measurement.
*
* \param connId Connection ID
*
* \return none
*/
/*************************************************************************************************/
void CppsSendPowerMeasurement(dmConnId_t connId)
{
int8_t i;
uint16_t flags = 0;
uint16_t len = 0;
uint8_t msg[CPPS_PM_MAX_LEN];
uint8_t measChar[CPPS_PM_MAX_FIELD_LEN];
uint8_t *pMeasChar;
uint8_t *pMsg = msg;
uint32_t temp;
uint16_t maxValueLen;
/* Get maximum length for a notification (ATT_MTU - 3) */
maxValueLen = AttGetMtu(connId) - ATT_VALUE_NTF_LEN;
/* Add manditory parameters. skip flags field for now */
UINT16_TO_BSTREAM(pMsg, 0);
UINT16_TO_BSTREAM(pMsg, cppsPmData.insantaneousPower);
/* Add optional parameters */
for (i = nextMeasFlag[connId - 1]; i < CPPS_NUM_FLAGS; i++)
{
/* Add data if flag is set */
if (cppsPmData.flags & (1 << i))
{
pMeasChar = measChar;
switch (i)
{
case CPPS_PPBP_FLAG_INDEX:
UINT8_TO_BSTREAM(pMeasChar, cppsPmData.powerBalance);
break;
case CPPS_ATP_FLAG_INDEX:
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.accumulatedTorque);
break;
case CPPS_WRDP_FLAG_INDEX:
UINT32_TO_BSTREAM(pMeasChar, cppsPmData.wheelRevolutions);
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.wheelEventTime);
break;
case CPPS_CRDP_FLAG_INDEX:
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.crankRevolutions);
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.crankEventTime);
break;
case CPPS_EFMP_FLAG_INDEX:
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.maxForceMagnitude);
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.minForceMagnitude);
break;
case CPPS_ETMP_FLAG_INDEX:
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.maxTorqueMagnitude);
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.minTorqueMagnitude);
break;
case CPPS_EAP_FLAG_INDEX:
temp = ((uint32_t)cppsPmData.maxAngle & 0xfff) | ((uint32_t)cppsPmData.minAngle << 12);
UINT24_TO_BSTREAM(pMeasChar, temp);
break;
case CPPS_TDSAP_FLAG_INDEX:
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.topDeadSpotAngle);
break;
case CPPS_BDSAP_FLAG_INDEX:
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.btmDeadSpotAngle);
break;
case CPPS_AEP_FLAG_INDEX:
UINT16_TO_BSTREAM(pMeasChar, cppsPmData.accumulatedEnergy);
break;
default:
break;
}
/* Add data into message buffer */
if ((len = pMeasChar - measChar) > 0)
{
if ((pMsg + len - msg) <= maxValueLen)
{
memcpy(pMsg, measChar, len);
pMsg += len;
flags |= cppsPmData.flags & (1 << i);
}
else
{
/* Buffer is full. Store current flag offset for next Notification. */
nextMeasFlag[connId - 1] = i;
break;
}
}
}
}
/* If all flags checked reset flag index */
if (i == CPPS_NUM_FLAGS)
{
nextMeasFlag[connId - 1] = 0;
}
/* Set flags */
UINT16_TO_BUF(msg, flags);
/* Calculate message length */
len = (uint16_t) (pMsg - msg);
/* Transmit notification */
AttsHandleValueNtf(connId, CPS_CPM_HDL, len, msg);
/* Clear the measurement data */
memset(&cppsPmData, 0, sizeof(cppsPmData));
}
/*************************************************************************************************/
/*!
* \fn CppsConnOpen
*
* \brief Setup connection specific variables.
*
* \param connId Connection ID
*
* \return none
*/
/*************************************************************************************************/
void CppsConnOpen(dmConnId_t connId)
{
nextMeasFlag[connId - 1] = 0;
}
@@ -0,0 +1,104 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Cycling Speed and Cadence Profile API.
*
* 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.
*/
/*************************************************************************************************/
#ifndef CSCP_API_H
#define CSCP_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup CYCLING_SPEED_AND_CADENCE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/** \name Cycling Speed Measurement Parameter Types
*
*/
/**@{*/
#define CSCP_SM_PARAM_WHEEL_REVOLUTIONS 0 /* Cumulative Wheel Revolutions */
#define CSCP_SM_PARAM_LAST_WHEEL_EVT_TIME 1 /* Last Wheel Event Time */
#define CSCP_SM_PARAM_CRANK_REVOLUTIONS 2 /* Cumulative Crank Revolutions */
#define CSCP_SM_PARAM_LAST_CRANK_TIME 3 /* Last Crank Event Time */
/**@}*/
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Set a cycling speed measurement parameter.
*
* \param type Parameter identifier
* \param value Measurement value.
*
* \return none
*/
/*************************************************************************************************/
void CscpsSetParameter(uint8_t type, uint32_t value);
/*************************************************************************************************/
/*!
* \brief Notifies the collector of a Cycle Speed Measurement.
*
* \param connId Connection ID
*
* \return none
*/
/*************************************************************************************************/
void CscpsSendSpeedMeasurement(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Set the sensor location attribute.
*
* \param location Sensor Location.
*
* \return none
*/
/*************************************************************************************************/
void CscpsSetSensorLocation(uint8_t location);
/*************************************************************************************************/
/*!
* \brief Set the features attribute.
*
* \param features Features bitmask.
*
* \return none
*/
/*************************************************************************************************/
void CscpsSetFeatures(uint16_t features);
/*! \} */ /* CYCLING_SPEED_AND_CADENCE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* CSCP_API_H */
@@ -0,0 +1,185 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Cycling Speed and Cadence Profile Sensor Implementation.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "dm_api.h"
#include "svc_cscs.h"
#include "svc_ch.h"
#include "cscp_api.h"
/*************************************************************************************************
* Constant Definitions
*************************************************************************************************/
/*! \brief The maximum length of a power measurement */
#define CSCPS_PM_MAX_LEN 11
/*! \brief Cycle Speed and Cadence Measurement Flag Indicies */
enum
{
CSCPS_WRDP_FLAG_INDEX, /*! \brief Wheel Revolution Data Present */
CSCPS_CRDP_FLAG_INDEX, /*! \brief Crank Revolution Data Present */
CSCPS_NUM_FLAGS
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Cycle Speed Measurement Data */
typedef struct
{
uint8_t flags; /*! \brief Speed Measurement Flags */
uint32_t wheelRevs; /*! \brief Cumulative Wheel Revolutions */
uint16_t lastWheelEventTime; /*! \brief Last Wheel Event Time */
uint16_t crankRevs; /*! \brief Cumulative Crank Revolutions */
uint16_t lastCrankEventTime; /*! \brief Last Crank Event Time */
} cscpSmData_t;
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! \brief Measurement data */
cscpSmData_t cscpsSmData;
/*************************************************************************************************/
/*!
* \brief Set a cycling speed measurement parameter.
*
* \param type Parameter identifier
* \param value Measurement value.
*
* \return none
*/
/*************************************************************************************************/
void CscpsSetParameter(uint8_t type, uint32_t value)
{
switch (type)
{
case CSCP_SM_PARAM_WHEEL_REVOLUTIONS:
cscpsSmData.flags |= (1 << CSCPS_WRDP_FLAG_INDEX);
cscpsSmData.wheelRevs = value;
break;
case CSCP_SM_PARAM_LAST_WHEEL_EVT_TIME:
cscpsSmData.flags |= (1 << CSCPS_WRDP_FLAG_INDEX);
cscpsSmData.lastWheelEventTime = (uint16_t) value;
break;
case CSCP_SM_PARAM_CRANK_REVOLUTIONS:
cscpsSmData.flags |= (1 << CSCPS_CRDP_FLAG_INDEX);
cscpsSmData.crankRevs = (uint16_t) value;
break;
case CSCP_SM_PARAM_LAST_CRANK_TIME:
cscpsSmData.flags |= (1 << CSCPS_CRDP_FLAG_INDEX);
cscpsSmData.lastCrankEventTime = (uint16_t) value;
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Set the sensor location attribute.
*
* \param location Sensor Location.
*
* \return none
*/
/*************************************************************************************************/
void CscpsSetSensorLocation(uint8_t location)
{
AttsSetAttr(CSCS_SL_HDL, sizeof(uint8_t), &location);
}
/*************************************************************************************************/
/*!
* \brief Set the features attribute.
*
* \param features Features bitmask.
*
* \return none
*/
/*************************************************************************************************/
void CscpsSetFeatures(uint16_t features)
{
uint8_t tempData[2] = {UINT16_TO_BYTES(features)};
AttsSetAttr(CSCS_CSF_HDL, sizeof(tempData), tempData);
}
/*************************************************************************************************/
/*!
* \brief Notifies the collector of a Cycle Speed Measurement.
*
* \param connId Connection ID
*
* \return none
*/
/*************************************************************************************************/
void CscpsSendSpeedMeasurement(dmConnId_t connId)
{
int8_t i;
uint16_t len;
uint8_t msg[CSCPS_PM_MAX_LEN];
uint8_t *p = msg;
/* Add manditory parameters */
UINT8_TO_BSTREAM(p, cscpsSmData.flags);
/* Add optional parameters */
for (i = 0; i < CSCPS_NUM_FLAGS; i++)
{
if (cscpsSmData.flags & (1 << i))
{
switch (i)
{
case CSCPS_WRDP_FLAG_INDEX:
UINT32_TO_BSTREAM(p, cscpsSmData.wheelRevs);
UINT16_TO_BSTREAM(p, cscpsSmData.lastWheelEventTime);
break;
case CSCPS_CRDP_FLAG_INDEX:
UINT16_TO_BSTREAM(p, cscpsSmData.crankRevs);
UINT16_TO_BSTREAM(p, cscpsSmData.lastCrankEventTime);
break;
}
}
}
/* Calculate message length */
len = (uint16_t) (p - msg);
/* Transmit notification */
AttsHandleValueNtf(connId, CSCS_CSM_HDL, len, msg);
/* Clear the measurement data */
memset(&cscpsSmData, 0, sizeof(cscpsSmData));
}
@@ -0,0 +1,93 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Device information service client.
*
* 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 DIS_API_H
#define DIS_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup DEVICE_INFORMATION_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered */
enum
{
DIS_MFNS_HDL_IDX, /*!< \brief Manufacturer name string */
DIS_MNS_HDL_IDX, /*!< \brief Model number string */
DIS_SNS_HDL_IDX, /*!< \brief Serial number string */
DIS_HRS_HDL_IDX, /*!< \brief Hardware revision string */
DIS_FRS_HDL_IDX, /*!< \brief Firmware revision string */
DIS_SRS_HDL_IDX, /*!< \brief Software revision string */
DIS_SID_HDL_IDX, /*!< \brief System ID */
DIS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for DIS service. Note that pHdlList
* must point to an array of handles of length \ref DIS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void DisDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref DIS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t DisValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* DEVICE_INFORMATION_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* DIS_API_H */
@@ -0,0 +1,205 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Device information service client.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "svc_ch.h"
#include "app_api.h"
#include "dis_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! DIS characteristics for discovery */
/*! Manufacturer name string */
static const attcDiscChar_t disMfns =
{
attMfnsChUuid,
0
};
/*! Model number string */
static const attcDiscChar_t disMns =
{
attMnsChUuid,
0
};
/*! Serial number string */
static const attcDiscChar_t disSns =
{
attSnsChUuid,
0
};
/*! Hardware revision string */
static const attcDiscChar_t disHrs =
{
attHrsChUuid,
0
};
/*! Firmware revision string */
static const attcDiscChar_t disFrs =
{
attFrsChUuid,
0
};
/*! Software revision string */
static const attcDiscChar_t disSrs =
{
attSrsChUuid,
0
};
/*! System ID */
static const attcDiscChar_t disSid =
{
attSidChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *disDiscCharList[] =
{
&disMfns, /*! Manufacturer name string */
&disMns, /*! Model number string */
&disSns, /*! Serial number string */
&disHrs, /*! Hardware revision string */
&disFrs, /*! Firmware revision string */
&disSrs, /*! Software revision string */
&disSid /*! System ID */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(DIS_HDL_LIST_LEN == ((sizeof(disDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Format a string for printing.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return Buffer containing string.
*/
/*************************************************************************************************/
char *disFmtString(uint8_t *pValue, uint16_t len)
{
static char buf[ATT_DEFAULT_PAYLOAD_LEN + 1];
len = (len < (sizeof(buf) - 1)) ? len : (sizeof(buf) - 1);
memcpy(buf, pValue, len);
buf[len] = '\0';
return buf;
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for DIS service. Parameter pHdlList
* must point to an array of length DIS_HDL_LIST_LEN. If discovery is successful
* the handles of discovered characteristics and descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void DisDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attDisSvcUuid,
DIS_HDL_LIST_LEN, (attcDiscChar_t **) disDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length DIS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t DisValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* manufacturer name string */
if (pMsg->handle == pHdlList[DIS_MFNS_HDL_IDX])
{
APP_TRACE_INFO1("Mfgr: %s", disFmtString(pMsg->pValue, pMsg->valueLen));
}
/* model number string */
else if (pMsg->handle == pHdlList[DIS_MNS_HDL_IDX])
{
APP_TRACE_INFO1("Model num: %s", disFmtString(pMsg->pValue, pMsg->valueLen));
}
/* serial number string */
else if (pMsg->handle == pHdlList[DIS_SNS_HDL_IDX])
{
APP_TRACE_INFO1("Serial num: %s", disFmtString(pMsg->pValue, pMsg->valueLen));
}
/* hardware revision string */
else if (pMsg->handle == pHdlList[DIS_HRS_HDL_IDX])
{
APP_TRACE_INFO1("Hardware rev: %s", disFmtString(pMsg->pValue, pMsg->valueLen));
}
/* firmware revision string */
else if (pMsg->handle == pHdlList[DIS_FRS_HDL_IDX])
{
APP_TRACE_INFO1("Firmware rev: %s", disFmtString(pMsg->pValue, pMsg->valueLen));
}
/* software revision string */
else if (pMsg->handle == pHdlList[DIS_SRS_HDL_IDX])
{
APP_TRACE_INFO1("Software rev: %s", disFmtString(pMsg->pValue, pMsg->valueLen));
}
/* system id */
else if (pMsg->handle == pHdlList[DIS_SID_HDL_IDX])
{
if (pMsg->valueLen == CH_SYSTEM_ID_LEN)
{
APP_TRACE_INFO0("System ID read ok");
}
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,85 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Find Me profile, locator role.
*
* 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 FMPL_API_H
#define FMPL_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup FIND_ME_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered for immediate alert service */
enum
{
FMPL_IAS_AL_HDL_IDX, /*!< \brief Alert level */
FMPL_IAS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Immediate Alert service. Note
* that pHdlList must point to an array of handles of length \ref FMPL_IAS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void FmplIasDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Send an immediate alert to the peer device.
*
* \param connId DM connection ID.
* \param handle Attribute handle.
* \param alert Alert value.
*
* \return None.
*/
/*************************************************************************************************/
void FmplSendAlert(dmConnId_t connId, uint16_t handle, uint8_t alert);
/*! \} */ /* FIND_ME_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* FMPL_API_H */
@@ -0,0 +1,92 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Find Me profile, locator role.
*
* 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_assert.h"
#include "app_api.h"
#include "fmpl_api.h"
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! Immediate Alert service characteristics for discovery */
/*! Alert level */
const attcDiscChar_t fmplIasAl =
{
attAlChUuid,
ATTC_SET_REQUIRED
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *fmplIasDiscCharList[] =
{
&fmplIasAl /* Alert level */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(FMPL_IAS_HDL_LIST_LEN == ((sizeof(fmplIasDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Immediate Alert service. Note
* that pHdlList must point to an array of handles of length FMPL_IAS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void FmplIasDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attIasSvcUuid,
FMPL_IAS_HDL_LIST_LEN, (attcDiscChar_t **) fmplIasDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Send an immediate alert to the peer device.
*
* \param connId DM connection ID.
* \param handle Attribute handle.
* \param alert Alert value.
*
* \return None.
*/
/*************************************************************************************************/
void FmplSendAlert(dmConnId_t connId, uint16_t handle, uint8_t alert)
{
uint8_t buf[1];
if (handle != ATT_HANDLE_NONE)
{
buf[0] = alert;
AttcWriteCmd(connId, handle, 1, buf);
}
}
@@ -0,0 +1,88 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief GAP profile.
*
* 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 GAP_API_H
#define GAP_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GAP_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered */
enum
{
GAP_CAR_HDL_IDX, /*!< \brief Central Address Resolution */
GAP_RPAO_HDL_IDX, /*!< \brief Resolvable Private Address Only */
GAP_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GAP service. Note that pHdlList
* must point to an array of handles of length \ref GAP_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GapDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref GAP_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GapValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* GAP_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GAP_API_H */
@@ -0,0 +1,127 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief GAP profile.
*
* 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.
*/
/*************************************************************************************************/
#include "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "app_db.h"
#include "app_api.h"
#include "gap_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! GAP service characteristics for discovery */
/*! Central Address Resolution */
static const attcDiscChar_t gapCar =
{
attCarChUuid,
0
};
/*! Resolvable Private Address Only */
static const attcDiscChar_t gapRpao =
{
attRpaoChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *gapDiscCharList[] =
{
&gapCar, /* Central Address Resolution */
&gapRpao /* Resolvable Private Address Only */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(GAP_HDL_LIST_LEN == ((sizeof(gapDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GAP service. Note that pHdlList
* must point to an array of handles of length GAP_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GapDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attGapSvcUuid,
GAP_HDL_LIST_LEN, (attcDiscChar_t **) gapDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length GAP_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GapValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* Central Address Resolution */
if (pMsg->handle == pHdlList[GAP_CAR_HDL_IDX])
{
appDbHdl_t dbHdl;
/* if there's a device record */
if ((dbHdl = AppDbGetHdl((dmConnId_t)pMsg->hdr.param)) != APP_DB_HDL_NONE)
{
if ((pMsg->pValue[0] == FALSE) || (pMsg->pValue[0] == TRUE))
{
/* store value in device database */
AppDbSetPeerAddrRes(dbHdl, pMsg->pValue[0]);
}
else
{
/* invalid value */
status = ATT_ERR_RANGE;
}
APP_TRACE_INFO1("Central address resolution: %d", pMsg->pValue[0]);
}
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,148 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief GATT 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.
*/
/*************************************************************************************************/
#ifndef GATT_API_H
#define GATT_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GATT_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered */
enum
{
GATT_SC_HDL_IDX, /*!< \brief Service changed */
GATT_SC_CCC_HDL_IDX, /*!< \brief Service changed client characteristic configuration descriptor */
GATT_CSF_HDL_IDX, /*!< \brief Client Supported Features */
GATT_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GATT service. Note that pHdlList
* must point to an array of handles of length \ref GATT_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GattDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref GATT_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GattValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*************************************************************************************************/
/*!
* \brief ATTS read callback for gatt service.
*
* \param connId Connection identifier.
* \param handle ATT handle.
* \param operation Operation selected.
* \param offset Offset to begin read from.
* \param pAttr Attribute to read from.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t GattReadCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, attsAttr_t *pAttr);
/*************************************************************************************************/
/*!
* \brief ATTS write callback for gatt service.
*
* \param connId Connection identifier.
* \param handle ATT handle.
* \param operation Operation selected.
* \param offset Offset to begin write.
* \param len Length of write.
* \param pValue Pointer to buffer to write.
* \param pAttr Attribute to write to.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t GattWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr);
/*************************************************************************************************/
/*!
* \brief Set Index of the Service Changed CCCD in the ATT Server.
*
* \param idx Index of the Service Changed CCCD in the ATT Server.
*
* \return None.
*/
/*************************************************************************************************/
void GattSetSvcChangedIdx(uint8_t idx);
/*************************************************************************************************/
/*!
* \brief Send Service Change Indications to the specified connections if they are configured to
* do so.
*
* \param connId DM Connection identifier or \ref DM_CONN_ID_NONE to send to all connections.
* \param start start handle for service changed value.
* \param end end handle for service changed value.
*
* \return None.
*/
/*************************************************************************************************/
void GattSendServiceChangedInd(dmConnId_t connId, uint16_t start, uint16_t end);
/*! \} */ /* GATT_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GATT_API_H */
@@ -0,0 +1,265 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief GATT 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 "wsf_assert.h"
#include "util/bstream.h"
#include "app_api.h"
#include "gatt_api.h"
#include "svc_core.h"
#include "att_api.h"
/**************************************************************************************************
Data Types
**************************************************************************************************/
/* Control block. */
typedef struct
{
bool_t svcChangedCccdIdxSet; /* Check if Service Changed CCCD index has been initialized. */
uint8_t svcChangedCccdIdx; /* Stored index of Service Changed CCCD. */
} gattServCb_t;
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block. */
gattServCb_t gattServCb;
/*! GATT service characteristics for discovery */
/*! Service changed */
static const attcDiscChar_t gattSc =
{
attScChUuid,
0
};
/*! Service changed client characteristic configuration descriptor */
static const attcDiscChar_t gattScCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! Client supported features */
static const attcDiscChar_t gattCsf =
{
attGattCsfChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *gattDiscCharList[] =
{
&gattSc, /* Service changed */
&gattScCcc, /* Service changed client characteristic configuration descriptor */
&gattCsf /* Client supported features */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(GATT_HDL_LIST_LEN == ((sizeof(gattDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GATT service. Parameter pHdlList
* must point to an array of length GATT_HDL_LIST_LEN. If discovery is successful
* the handles of discovered characteristics and descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GattDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attGattSvcUuid,
GATT_HDL_LIST_LEN, (attcDiscChar_t **) gattDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length GATT_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GattValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* service changed */
if (pMsg->handle == pHdlList[GATT_SC_HDL_IDX])
{
/* perform service changed */
AppDiscServiceChanged(pMsg);
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Set Index of the Service Changed CCCD in the ATT Server.
*
* \param idx Index of the Service Changed CCCD in the ATT Server.
*
* \return None.
*/
/*************************************************************************************************/
void GattSetSvcChangedIdx(uint8_t idx)
{
gattServCb.svcChangedCccdIdxSet = TRUE;
gattServCb.svcChangedCccdIdx = idx;
}
/*************************************************************************************************/
/*!
* \brief Send Service Change Indications to the specified connections if they are configured to
* do so.
*
* \param connId DM Connection identifier or \ref DM_CONN_ID_NONE to send to all connections.
* \param start start handle for service changed value.
* \param end end handle for service changed value.
*
* \return None.
*/
/*************************************************************************************************/
void GattSendServiceChangedInd(dmConnId_t connId, uint16_t start, uint16_t end)
{
uint8_t svcChangedValues[4];
uint8_t *p;
if (!gattServCb.svcChangedCccdIdxSet)
{
return;
}
p = svcChangedValues;
UINT16_TO_BSTREAM(p, start);
UINT16_TO_BSTREAM(p, end);
/* If connection is not specified */
if (connId == DM_CONN_ID_NONE)
{
/* Send to all. */
for (connId = 1; connId <= DM_CONN_MAX; connId++)
{
if (AttsCccEnabled(connId, gattServCb.svcChangedCccdIdx))
{
AttsHandleValueInd(connId, GATT_SC_HDL, sizeof(svcChangedValues), svcChangedValues);
}
}
}
else
{
/* Send to only this one. */
if (AttsCccEnabled(connId, gattServCb.svcChangedCccdIdx))
{
AttsHandleValueInd(connId, GATT_SC_HDL, sizeof(svcChangedValues), svcChangedValues);
}
}
}
/*************************************************************************************************/
/*!
* \brief ATTS read callback for gatt service.
*
* \param connId Connection identifier.
* \param handle ATT handle.
* \param operation Operation selected.
* \param offset Offset to begin read from.
* \param pAttr Attribute to read from.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t GattReadCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, attsAttr_t *pAttr)
{
switch (handle)
{
case GATT_CSF_HDL:
{
uint8_t csf[ATT_CSF_LEN];
AttsCsfGetFeatures(connId, csf, sizeof(csf));
memcpy(pAttr->pValue, csf, ATT_CSF_LEN);
}
break;
default:
break;
}
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for gatt service.
*
* \param connId Connection identifier.
* \param handle ATT handle.
* \param operation Operation selected.
* \param offset Offset to begin write.
* \param len Length of write.
* \param pValue Pointer to buffer to write.
* \param pAttr Attribute to write to.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t GattWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
uint8_t status;
switch (handle)
{
case GATT_CSF_HDL:
status = AttsCsfWriteFeatures(connId, offset, len, pValue);
break;
default:
status = ATT_SUCCESS;
break;
}
return status;
}
@@ -0,0 +1,143 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Glucose profile collector.
*
* 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 GLPC_API_H
#define GLPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GLUCOSE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Glucose service enumeration of handle indexes of characteristics to be discovered */
enum
{
GLPC_GLS_GLM_HDL_IDX, /*!< \brief Glucose measurement */
GLPC_GLS_GLM_CCC_HDL_IDX, /*!< \brief Glucose measurement CCC descriptor */
GLPC_GLS_GLMC_HDL_IDX, /*!< \brief Glucose measurement context */
GLPC_GLS_GLMC_CCC_HDL_IDX, /*!< \brief Glucose measurement context CCC descriptor */
GLPC_GLS_GLF_HDL_IDX, /*!< \brief Glucose feature */
GLPC_GLS_RACP_HDL_IDX, /*!< \brief Record access control point */
GLPC_GLS_RACP_CCC_HDL_IDX, /*!< \brief Record access control point CCC descriptor */
GLPC_GLS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Glucose service RACP filter type */
typedef struct
{
union
{
uint16_t seqNum; /*!< \brief Sequence number filter */
} param; /*!< \brief Parameter union */
uint8_t type; /*!< \brief Filter type */
} glpcFilter_t;
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Glucose service.
* Parameter pHdlList must point to an array of length \ref GLPC_GLS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GlpcGlsDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref GLPC_GLS_HDL_LIST_LEN.
* If the ATT handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GlpcGlsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Send a command to the glucose service record access control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param opcode Command opcode.
* \param oper Command operator or 0 if no operator required.
* \param pFilter Command filter parameters or NULL of no parameters required.
*
* \return None.
*/
/*************************************************************************************************/
void GlpcGlsRacpSend(dmConnId_t connId, uint16_t handle, uint8_t opcode, uint8_t oper,
glpcFilter_t *pFilter);
/*************************************************************************************************/
/*!
* \brief Set the last received glucose measurement sequence number.
*
* \param seqNum Glucose measurement sequence number.
*
* \return None.
*/
/*************************************************************************************************/
void GlpcGlsSetLastSeqNum(uint16_t seqNum);
/*************************************************************************************************/
/*!
* \brief Get the last received glucose measurement sequence number.
*
* \return Last received glucose measurement sequence number.
*/
/*************************************************************************************************/
uint16_t GlpcGlsGetLastSeqNum(void);
/*! \} */ /* GLUCOSE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GLPC_API_H */
@@ -0,0 +1,535 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Glucose profile collector.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "svc_ch.h"
#include "app_api.h"
#include "glpc_api.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/* Length of response data contained in a received RACP message */
#define GLPC_GLS_RACP_RSP_LEN 4
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Glucose service characteristics for discovery
*/
/*! Glucose measurement */
static const attcDiscChar_t glpcGlsGlm =
{
attGlmChUuid,
ATTC_SET_REQUIRED
};
/*! Glucose measurement CCC descriptor */
static const attcDiscChar_t glpcGlsGlmCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Glucose measurement context */
static const attcDiscChar_t glpcGlsGlmc =
{
attGlmcChUuid,
0
};
/*! Glucose measurement context CCC descriptor */
static const attcDiscChar_t glpcGlsGlmcCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! Glucose feature */
static const attcDiscChar_t glpcGlsGlf =
{
attGlfChUuid,
ATTC_SET_REQUIRED
};
/*! Record access control point */
static const attcDiscChar_t glpcGlsRacp =
{
attRacpChUuid,
ATTC_SET_REQUIRED
};
/*! Record access control point CCC descriptor */
static const attcDiscChar_t glpcGlsRacpCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *glpcGlsDiscCharList[] =
{
&glpcGlsGlm, /*! Glucose measurement */
&glpcGlsGlmCcc, /*! Glucose measurement CCC descriptor */
&glpcGlsGlmc, /*! Glucose measurement context */
&glpcGlsGlmcCcc, /*! Glucose measurement context CCC descriptor */
&glpcGlsGlf, /*! Glucose feature */
&glpcGlsRacp, /*! Record access control point */
&glpcGlsRacpCcc /*! Record access control point CCC descriptor */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(GLPC_GLS_HDL_LIST_LEN == ((sizeof(glpcGlsDiscCharList) / sizeof(attcDiscChar_t *))));
/*! Control block */
static struct
{
uint16_t lastSeqNum; /*! Last received glucose measurement sequence number */
} glpcCb;
/*************************************************************************************************/
/*!
* \brief Parse a glucose measurement.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void glpcGlsParseGlm(uint8_t *pValue, uint16_t len)
{
uint8_t flags;
uint16_t seqNum;
uint16_t year;
uint8_t month, day, hour, min, sec;
int16_t timeOffset;
uint16_t glucose;
int16_t mantissa;
int8_t exponent;
uint8_t typeLoc;
uint16_t sensorStatus;
uint16_t minLen = CH_GLM_FLAGS_LEN + CH_GLM_SEQNUM_LEN + CH_GLM_TIMESTAMP_LEN;
/* Suppress unused variable compile warning */
(void)year; (void)month; (void)day; (void)hour; (void)min; (void)sec;
(void)sensorStatus; (void)typeLoc; (void)exponent; (void)mantissa; (void)timeOffset;
if (len > 0)
{
/* get flags */
BSTREAM_TO_UINT8(flags, pValue);
/* determine expected minimum length based on flags */
if (flags & CH_GLM_FLAG_TIME_OFFSET)
{
minLen += CH_GLM_TIME_OFFSET_LEN;
}
if (flags & CH_GLM_FLAG_CONC_TYPE_LOC)
{
minLen += CH_GLM_CONC_TYPE_LOC_LEN;
}
if (flags & CH_GLM_FLAG_SENSOR_STATUS)
{
minLen += CH_GLM_SENSOR_STATUS_LEN;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Glucose meas len:%d minLen:%d", len, minLen);
return;
}
/* sequence number */
BSTREAM_TO_UINT16(seqNum, pValue);
glpcCb.lastSeqNum = seqNum;
/* base time */
BSTREAM_TO_UINT16(year, pValue);
BSTREAM_TO_UINT8(month, pValue);
BSTREAM_TO_UINT8(day, pValue);
BSTREAM_TO_UINT8(hour, pValue);
BSTREAM_TO_UINT8(min, pValue);
BSTREAM_TO_UINT8(sec, pValue);
APP_TRACE_INFO3(" Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3(" Time: %02d:%02d:%02d", hour, min, sec);
/* time offset */
if (flags & CH_GLM_FLAG_TIME_OFFSET)
{
BSTREAM_TO_UINT16(timeOffset, pValue);
APP_TRACE_INFO1(" Time offset: %d", timeOffset);
}
/* glucose concentration, type, and location */
if (flags & CH_GLM_FLAG_CONC_TYPE_LOC)
{
BSTREAM_TO_UINT16(glucose, pValue);
UINT16_TO_SFLT(mantissa, exponent, glucose);
APP_TRACE_INFO2(" Glucose concentration: %de%d", mantissa, exponent);
BSTREAM_TO_UINT8(typeLoc, pValue);
APP_TRACE_INFO2(" Type: %d Location: %d", (typeLoc & 0x0F), (typeLoc >> 4));
}
/* sensor status */
if (flags & CH_GLM_FLAG_SENSOR_STATUS)
{
BSTREAM_TO_UINT16(sensorStatus, pValue);
APP_TRACE_INFO1(" Sensor status: 0x%04x", sensorStatus);
}
APP_TRACE_INFO2(" Flags: 0x%02x Sequence Number: %d", flags, seqNum);
}
/*************************************************************************************************/
/*!
* \brief Parse a glucose measurement context.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void glpcGlsParseGlmc(uint8_t *pValue, uint16_t len)
{
uint8_t flags;
uint16_t seqNum;
uint8_t extFlags;
uint8_t carbId;
uint16_t carb;
uint8_t meal;
uint8_t testerHealth;
uint16_t exerDuration;
uint8_t exerIntensity;
uint8_t medicationId;
uint16_t medication;
uint16_t hba1c;
int16_t mantissa;
int8_t exponent;
uint16_t minLen = CH_GLMC_FLAGS_LEN + CH_GLMC_SEQNUM_LEN;
/* Suppress unused variable compile warning */
(void)seqNum; (void)extFlags; (void)carbId; (void)meal; (void)testerHealth;
(void)medicationId; (void)exerIntensity; (void)exerDuration;
(void)exponent; (void)mantissa;
if (len > 0)
{
/* get flags */
BSTREAM_TO_UINT8(flags, pValue);
/* determine expected minimum length based on flags */
if (flags & CH_GLMC_FLAG_CARB)
{
minLen += CH_GLMC_CARB_LEN;
}
if (flags & CH_GLMC_FLAG_MEAL)
{
minLen += CH_GLMC_MEAL_LEN;
}
if (flags & CH_GLMC_FLAG_TESTER)
{
minLen += CH_GLMC_TESTER_LEN;
}
if (flags & CH_GLMC_FLAG_EXERCISE)
{
minLen += CH_GLMC_EXERCISE_LEN;
}
if (flags & CH_GLMC_FLAG_MED)
{
minLen += CH_GLMC_MED_LEN;
}
if (flags & CH_GLMC_FLAG_HBA1C)
{
minLen += CH_GLMC_HBA1C_LEN;
}
if (flags & CH_GLMC_FLAG_EXT)
{
minLen += CH_GLMC_EXT_LEN;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Glucose meas context len:%d minLen:%d", len, minLen);
return;
}
/* sequence number */
BSTREAM_TO_UINT16(seqNum, pValue);
/* extended flags */
if (flags & CH_GLMC_FLAG_EXT)
{
BSTREAM_TO_UINT8(extFlags, pValue);
APP_TRACE_INFO1(" Extended Flags: 0x%02x", extFlags);
}
/* carbohydrate id and carbohydrate */
if (flags & CH_GLMC_FLAG_CARB)
{
BSTREAM_TO_UINT8(carbId, pValue);
BSTREAM_TO_UINT16(carb, pValue);
UINT16_TO_SFLT(mantissa, exponent, carb);
APP_TRACE_INFO3(" Carb Id: %d Carb-kg: %de%d", carbId, mantissa, exponent);
}
/* meal */
if (flags & CH_GLMC_FLAG_MEAL)
{
BSTREAM_TO_UINT8(meal, pValue);
APP_TRACE_INFO1(" Meal: %d", meal);
}
/* tester-health */
if (flags & CH_GLMC_FLAG_TESTER)
{
BSTREAM_TO_UINT8(testerHealth, pValue);
APP_TRACE_INFO2(" Tester: %d Health: %d", (testerHealth & 0x0F), (testerHealth >> 4));
}
/* exercise duration and exercise intensity */
if (flags & CH_GLMC_FLAG_EXERCISE)
{
BSTREAM_TO_UINT16(exerDuration, pValue);
BSTREAM_TO_UINT8(exerIntensity, pValue);
APP_TRACE_INFO2(" Exercise Duration: %d Intensity: %d", exerDuration, exerIntensity);
}
/* medication ID and medication */
if (flags & CH_GLMC_FLAG_MED)
{
BSTREAM_TO_UINT8(medicationId, pValue);
BSTREAM_TO_UINT16(medication, pValue);
UINT16_TO_SFLT(mantissa, exponent, medication);
APP_TRACE_INFO3(" Medication ID: %d Units: %de%d", medicationId, mantissa, exponent);
}
/* hba1c */
if (flags & CH_GLMC_FLAG_HBA1C)
{
BSTREAM_TO_UINT16(hba1c, pValue);
UINT16_TO_SFLT(mantissa, exponent, hba1c);
APP_TRACE_INFO2(" HbA1c: %de%d", mantissa, exponent);
}
APP_TRACE_INFO2(" Flags: 0x%02x Sequence Number: %d", flags, seqNum);
}
/*************************************************************************************************/
/*!
* \brief Process a message received from the sensor on the record access control point.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void glpcGlsProcRacp(uint8_t *pValue, uint16_t len)
{
uint8_t opcode;
uint16_t numRecords;
uint16_t reqOpcode;
uint16_t status;
/* Suppress unused variable compile warning */
(void)status; (void)reqOpcode; (void)numRecords;
/* verify length */
if (len != GLPC_GLS_RACP_RSP_LEN)
{
APP_TRACE_INFO1("Unexpected RACP message length: %d", len);
return;
}
/* parse message */
BSTREAM_TO_UINT8(opcode, pValue);
pValue++;
if (opcode == CH_RACP_OPCODE_NUM_RSP)
{
BSTREAM_TO_UINT16(numRecords, pValue);
APP_TRACE_INFO1("Number of records: %d", numRecords);
}
else if (opcode == CH_RACP_OPCODE_RSP)
{
BSTREAM_TO_UINT8(reqOpcode, pValue);
BSTREAM_TO_UINT8(status, pValue);
APP_TRACE_INFO2("Response opcode: %d status: %d", reqOpcode, status);
}
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Glucose service. Parameter
* pHdlList must point to an array of length GLPC_GLS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GlpcGlsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attGlsSvcUuid,
GLPC_GLS_HDL_LIST_LEN, (attcDiscChar_t **) glpcGlsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length GLPC_GLS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GlpcGlsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint16_t feature;
uint8_t *p;
uint8_t status = ATT_SUCCESS;
/* Suppress unused variable compile warning */
(void)feature;
/* glucose measurement */
if (pMsg->handle == pHdlList[GLPC_GLS_GLM_HDL_IDX])
{
APP_TRACE_INFO0("Glucose meas.");
/* parse value */
glpcGlsParseGlm(pMsg->pValue, pMsg->valueLen);
}
/* glucose measurement context */
else if (pMsg->handle == pHdlList[GLPC_GLS_GLMC_HDL_IDX])
{
APP_TRACE_INFO0("Glucose meas. context");
/* parse value */
glpcGlsParseGlmc(pMsg->pValue, pMsg->valueLen);
}
/* record access control point */
else if (pMsg->handle == pHdlList[GLPC_GLS_RACP_HDL_IDX])
{
glpcGlsProcRacp(pMsg->pValue, pMsg->valueLen);
}
/* glucose feature */
else if (pMsg->handle == pHdlList[GLPC_GLS_GLF_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT16(feature, p);
APP_TRACE_INFO1("Glucose feature:0x%04x", feature);
}
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Send a command to the glucose service record access control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param opcode Command opcode.
* \param oper Command operator or 0 if no operator required.
* \param pFilter Command filter parameters or NULL of no parameters required.
*
* \return None.
*/
/*************************************************************************************************/
void GlpcGlsRacpSend(dmConnId_t connId, uint16_t handle, uint8_t opcode, uint8_t oper,
glpcFilter_t *pFilter)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t *p = buf;
if (handle != ATT_HANDLE_NONE)
{
/* build RACP command */
UINT8_TO_BSTREAM(p, opcode);
UINT8_TO_BSTREAM(p, oper);
if (pFilter != NULL)
{
UINT8_TO_BSTREAM(p, pFilter->type);
if (pFilter->type == CH_RACP_GLS_FILTER_SEQ)
{
UINT16_TO_BSTREAM(p, pFilter->param.seqNum);
}
}
AttcWriteReq(connId, handle, (uint16_t) (p - buf), buf);
}
}
/*************************************************************************************************/
/*!
* \brief Set the last received glucose measurement sequence number.
*
* \param seqNum Glucose measurement sequence number.
*
* \return None.
*/
/*************************************************************************************************/
void GlpcGlsSetLastSeqNum(uint16_t seqNum)
{
glpcCb.lastSeqNum = seqNum;
}
/*************************************************************************************************/
/*!
* \brief Get the last received glucose measurement sequence number.
*
* \return Last received glucose measurement sequence number.
*/
/*************************************************************************************************/
uint16_t GlpcGlsGetLastSeqNum(void)
{
return glpcCb.lastSeqNum;
}
@@ -0,0 +1,123 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Glucose profile sensor.
*
* 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 GLPS_API_H
#define GLPS_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GLUCOSE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief All supported features of the glucose profile */
#define GLP_ALL_SUPPORTED_FEATURES 0x000F
/*************************************************************************************************/
/*!
* \brief Initialize the Glucose profile sensor.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsInit(void);
/*************************************************************************************************/
/*!
* \brief This function is called by the application when a message that requires
* processing by the glucose profile sensor is received.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsProcMsg(wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsBtn(dmConnId_t connId, uint8_t btn);
/*************************************************************************************************/
/*!
* \brief ATTS write callback for glucose service record access control point. Use this
* function as a parameter to SvcGlsCbackRegister().
*
* \param connId DM connection identifier.
* \param handle ATT handle.
* \param operation ATT operation.
* \param offset Write offset.
* \param len Write length.
* \param pValue Value to write.
* \param pAttr Attribute to write.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t GlpsRacpWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr);
/*************************************************************************************************/
/*!
* \brief Set the supported features of the glucose sensor.
*
* \param feature Feature bitmask.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsSetFeature(uint16_t feature);
/*************************************************************************************************/
/*!
* \brief Set the CCCD index used by the application for glucose service characteristics.
*
* \param glmCccIdx Glucose measurement CCCD index.
* \param glmcCccIdx Glucose measurement context CCCD index.
* \param racpCccIdx Record access control point CCCD index.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsSetCccIdx(uint8_t glmCccIdx, uint8_t glmcCccIdx, uint8_t racpCccIdx);
/*! \} */ /* GLUCOSE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GLPS_API_H */
@@ -0,0 +1,430 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Glucose profile example record database and access functions.
*
* 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 "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_gls.h"
#include "app_api.h"
#include "glps_api.h"
#include "glps_main.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Number of records in example database */
#define GLPS_DB_NUM_RECORDS 3
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! Control block */
static struct
{
uint8_t numRec;
} glpsDbCb;
/*! Example record database */
static glpsRec_t glpsDb[GLPS_DB_NUM_RECORDS] =
{
/* record 1 */
{
/* measurement */
{
CH_GLM_FLAG_TIME_OFFSET | CH_GLM_FLAG_CONC_TYPE_LOC | /* Flags */
CH_GLM_FLAG_UNITS_MOL_L | CH_GLM_FLAG_SENSOR_STATUS,
0x0001, /* Sequence number */
{2012, 6, 15, 20, 52, 36}, /* Base time */
60, /* Time offset */
SFLT_TO_UINT16(50, -3), /* Glucose concentration (SFLOAT) */
CH_GLM_LOC_FINGER | (CH_GLM_TYPE_ART_BLOOD << 4), /* Sample type and sample location */
CH_GLM_STATUS_BATT_LOW /* Sensor status annunciation */
},
/* context */
{
CH_GLMC_FLAG_CARB | CH_GLMC_FLAG_MEAL | /* Flags */
CH_GLMC_FLAG_TESTER | CH_GLMC_FLAG_EXERCISE |
CH_GLMC_FLAG_MED | CH_GLMC_FLAG_MED_KG |
CH_GLMC_FLAG_HBA1C,
0x0001, /* Sequence number */
0, /* Extended Flags */
CH_GLMC_CARB_DINNER, /* Carbohydrate ID */
SFLT_TO_UINT16(12, -3), /* Carbohydrate (SFLOAT) */
CH_GLMC_MEAL_POSTPRANDIAL, /* Meal */
CH_GLMC_HEALTH_NONE | (CH_GLMC_TESTER_SELF << 4), /* Tester and health */
300, /* Exercise Duration */
99, /* Exercise Intensity */
CH_GLMC_MED_LONG, /* Medication ID */
SFLT_TO_UINT16(50, -6), /* Medication (SFLOAT) */
SFLT_TO_UINT16(10, 0) /* HbA1c */
},
},
/* record 2 */
{
/* measurement */
{
CH_GLM_FLAG_CONC_TYPE_LOC | CH_GLM_FLAG_UNITS_KG_L | /* Flags */
CH_GLM_FLAG_CONTEXT_INFO,
0x0002, /* Sequence number */
{2012, 6, 16, 6, 45, 20}, /* Base time */
0, /* Time offset */
SFLT_TO_UINT16(20, -5), /* Glucose concentration (SFLOAT) */
CH_GLM_LOC_FINGER | (CH_GLM_TYPE_ART_BLOOD << 4), /* Sample type and sample location */
0 /* Sensor status annunciation */
},
/* context */
{
CH_GLMC_FLAG_CARB | CH_GLMC_FLAG_MEAL | /* Flags */
CH_GLMC_FLAG_TESTER | CH_GLMC_FLAG_EXERCISE |
CH_GLMC_FLAG_MED | CH_GLMC_FLAG_MED_L |
CH_GLMC_FLAG_HBA1C,
0x0002, /* Sequence number */
0, /* Extended Flags */
CH_GLMC_CARB_BREAKFAST, /* Carbohydrate ID */
SFLT_TO_UINT16(3, -3), /* Carbohydrate (SFLOAT) */
CH_GLMC_MEAL_PREPRANDIAL, /* Meal */
CH_GLMC_HEALTH_NONE | (CH_GLMC_TESTER_SELF << 4), /* Tester and health */
1000, /* Exercise Duration */
25, /* Exercise Intensity */
CH_GLMC_MED_LONG, /* Medication ID */
SFLT_TO_UINT16(10, -3), /* Medication (SFLOAT) */
SFLT_TO_UINT16(11, 0) /* HbA1c */
},
},
/* record 3 */
{
/* measurement */
{
CH_GLM_FLAG_CONC_TYPE_LOC | CH_GLM_FLAG_UNITS_KG_L, /* Flags */
0x0003, /* Sequence number */
{2012, 6, 16, 18, 45, 20}, /* Base time */
0, /* Time offset */
SFLT_TO_UINT16(20, -5), /* Glucose concentration (SFLOAT) */
CH_GLM_LOC_FINGER | (CH_GLM_TYPE_ART_BLOOD << 4), /* Sample type and sample location */
0 /* Sensor status annunciation */
},
/* context */
{
CH_GLMC_FLAG_TESTER | CH_GLMC_FLAG_EXERCISE | /* Flags */
CH_GLMC_FLAG_MED | CH_GLMC_FLAG_MED_L |
CH_GLMC_FLAG_HBA1C,
0x0003, /* Sequence number */
0, /* Extended Flags */
0, /* Carbohydrate ID */
SFLT_TO_UINT16(0, 0), /* Carbohydrate (SFLOAT) */
0, /* Meal */
CH_GLMC_HEALTH_NONE | (CH_GLMC_TESTER_SELF << 4), /* Tester and health */
1001, /* Exercise Duration */
26, /* Exercise Intensity */
CH_GLMC_MED_LONG, /* Medication ID */
SFLT_TO_UINT16(15, -3), /* Medication (SFLOAT) */
SFLT_TO_UINT16(12, 0) /* HbA1c */
},
}
};
/*************************************************************************************************/
/*!
* \brief Get last database record.
*
* \return Pointer to record or NULL if no record.
*/
/*************************************************************************************************/
static glpsRec_t *glpsDbGetEnd(void)
{
if (glpsDbCb.numRec == 0)
{
return NULL;
}
return (glpsRec_t *) &glpsDb[glpsDbCb.numRec - 1];
}
/*************************************************************************************************/
/*!
* \brief Get the next database record after the given current record.
*
* \param pCurrRec Pointer to current record.
*
* \return Pointer to record or NULL if no record.
*/
/*************************************************************************************************/
static glpsRec_t *glpsDbGetNext(glpsRec_t *pCurrRec)
{
if (glpsDbCb.numRec == 0 || pCurrRec == glpsDbGetEnd())
{
return NULL;
}
else if (pCurrRec == NULL)
{
return (glpsRec_t *) glpsDb;
}
else
{
return (pCurrRec + 1);
}
}
/*************************************************************************************************/
/*!
* \brief Get the next record filtered for "all".
*
* \param pCurrRec Pointer to current record.
* \param pRec Return pointer to next record, if found.
*
* \return CH_RACP_RSP_SUCCESS if a record is found, otherwise an error status is returned.
*/
/*************************************************************************************************/
static uint8_t glpsDbOpAll(glpsRec_t *pCurrRec, glpsRec_t **pRec)
{
*pRec = glpsDbGetNext(pCurrRec);
return (*pRec != NULL) ? CH_RACP_RSP_SUCCESS : CH_RACP_RSP_NO_RECORDS;
}
/*************************************************************************************************/
/*!
* \brief Get the next record filtered for "greater than or equal to sequence number".
*
* \param pFilter Glucose service RACP filter parameters.
* \param pCurrRec Pointer to current record.
* \param pRec Return pointer to next record, if found.
*
* \return CH_RACP_RSP_SUCCESS if a record is found, otherwise an error status is returned.
*/
/*************************************************************************************************/
static uint8_t glpsDbOpGteqSeqNum(uint8_t *pFilter, glpsRec_t *pCurrRec, glpsRec_t **pRec)
{
uint16_t seqNum;
/* parse seq number */
pFilter++;
BYTES_TO_UINT16(seqNum, pFilter);
/* find record */
while ((*pRec = glpsDbGetNext(pCurrRec)) != NULL)
{
if ((*pRec)->meas.seqNum >= seqNum)
{
return CH_RACP_RSP_SUCCESS;
}
pCurrRec = *pRec;
}
return CH_RACP_RSP_NO_RECORDS;
}
/*************************************************************************************************/
/*!
* \brief Get the next record filtered for "last".
*
* \param pCurrRec Pointer to current record.
* \param pRec Return pointer to next record, if found.
*
* \return CH_RACP_RSP_SUCCESS if a record is found, otherwise an error status is returned.
*/
/*************************************************************************************************/
static uint8_t glpsDbOpLast(glpsRec_t *pCurrRec, glpsRec_t **pRec)
{
/* if current record is already last return failure */
*pRec = glpsDbGetEnd();
if (*pRec != NULL && *pRec != pCurrRec)
{
return CH_RACP_RSP_SUCCESS;
}
else
{
return CH_RACP_RSP_NO_RECORDS;
}
}
/*************************************************************************************************/
/*!
* \brief Initialize the glucose record database.
*
* \return None.
*/
/*************************************************************************************************/
void glpsDbInit(void)
{
glpsDbCb.numRec = GLPS_DB_NUM_RECORDS;
}
/*************************************************************************************************/
/*!
* \brief Get the next record that matches the given filter parameters that follows
* the given current record.
*
* \param oper Operator.
* \param pFilter Glucose service RACP filter parameters.
* \param pCurrRec Pointer to current record.
* \param pRec Return pointer to next record, if found.
*
* \return CH_RACP_RSP_SUCCESS if a record is found, otherwise an error status is returned.
*/
/*************************************************************************************************/
uint8_t glpsDbGetNextRecord(uint8_t oper, uint8_t *pFilter, glpsRec_t *pCurrRec, glpsRec_t **pRec)
{
uint8_t status;
switch (oper)
{
case CH_RACP_OPERATOR_ALL:
status = glpsDbOpAll(pCurrRec, pRec);
break;
case CH_RACP_OPERATOR_GTEQ:
/* check filter type */
if (*pFilter == CH_RACP_GLS_FILTER_SEQ)
{
status = glpsDbOpGteqSeqNum(pFilter, pCurrRec, pRec);
}
else
{
status = CH_RACP_RSP_OPERAND_NOT_SUP;
}
break;
case CH_RACP_OPERATOR_LAST:
status = glpsDbOpLast(pCurrRec, pRec);
break;
case CH_RACP_OPERATOR_NULL:
status = CH_RACP_RSP_INV_OPERATOR;
break;
default:
status = CH_RACP_RSP_OPERATOR_NOT_SUP;
break;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Delete records that match the given filter parameters.
*
* \param oper Operator.
* \param pFilter Glucose service RACP filter parameters.
*
* \return CH_RACP_RSP_SUCCESS if records deleted, otherwise an error status is returned.
*/
/*************************************************************************************************/
uint8_t glpsDbDeleteRecords(uint8_t oper, uint8_t *pFilter)
{
/* only 'all records' is supported */
if (oper == CH_RACP_OPERATOR_ALL)
{
glpsDbCb.numRec = 0;
return CH_RACP_RSP_SUCCESS;
}
else
{
return CH_RACP_RSP_OPERATOR_NOT_SUP;
}
}
/*************************************************************************************************/
/*!
* \brief Get the number of records matching the filter parameters.
*
* \param oper Operator.
* \param pFilter Glucose service RACP filter parameters.
* \param pNumRec Returns number of records which match filter parameters.
*
* \return RACP status.
*/
/*************************************************************************************************/
uint8_t glpsDbGetNumRecords(uint8_t oper, uint8_t *pFilter, uint8_t *pNumRec)
{
glpsRec_t *pCurrRec = NULL;
uint8_t status;
*pNumRec = 0;
while ((status = glpsDbGetNextRecord(oper, pFilter, pCurrRec, &pCurrRec)) == CH_RACP_RSP_SUCCESS)
{
(*pNumRec)++;
}
if (status == CH_RACP_RSP_NO_RECORDS)
{
status = CH_RACP_RSP_SUCCESS;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Generate a new record.
*
* \return None.
*/
/*************************************************************************************************/
void glpsDbGenerateRecord(void)
{
if (glpsDbCb.numRec < GLPS_DB_NUM_RECORDS)
{
glpsDbCb.numRec++;
}
}
/*************************************************************************************************/
/*!
* \brief For conformance testing only. Toggle the sample data record number 2's medication
* quantity unit flag between Kilograms and Liters. Also modifies quantity.
*
* \return None.
*/
/*************************************************************************************************/
void glpsDbToggleMedicationUnits(void)
{
if (glpsDb[1].context.flags & CH_GLMC_FLAG_MED_L)
{
/* Change medication quantity to Kilograms. */
glpsDb[1].context.flags &= ~CH_GLMC_FLAG_MED_L;
glpsDb[1].context.flags |= CH_GLMC_FLAG_MED_KG;
glpsDb[1].context.medication = SFLT_TO_UINT16(50, -6);
}
else
{
/* Change medication quantiy to Liters. */
glpsDb[1].context.flags &= ~CH_GLMC_FLAG_MED_KG;
glpsDb[1].context.flags |= CH_GLMC_FLAG_MED_L;
glpsDb[1].context.medication = SFLT_TO_UINT16(10, -3);
}
}
@@ -0,0 +1,822 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Glucose 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 "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_gls.h"
#include "app_api.h"
#include "app_ui.h"
#include "glps_api.h"
#include "glps_main.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
uint8_t operand[GLPS_OPERAND_MAX]; /* Stored operand filter data */
glpsRec_t *pCurrRec; /* Pointer to current measurement record */
bool_t inProgress; /* TRUE if RACP procedure in progress */
bool_t txReady; /* TRUE if ready to send next notification or indication */
bool_t aborting; /* TRUE if abort procedure in progress */
uint8_t glmCccIdx; /* Glucose measurement CCCD index */
uint8_t glmcCccIdx; /* Glucose measurement context CCCD index */
uint8_t racpCccIdx; /* Record access control point CCCD index */
uint8_t oper; /* Stored operator */
} glpsCb;
/*************************************************************************************************/
/*!
* \brief Build a glucose measurement characteristic.
*
* \param pBuf Pointer to buffer to hold the built glucose measurement.
* \param pGlm Glucose measurement values.
*
* \return Length of pBuf in bytes.
*/
/*************************************************************************************************/
static uint8_t glpsBuildGlm(uint8_t *pBuf, glpsGlm_t *pGlm)
{
uint8_t *p = pBuf;
uint8_t flags = pGlm->flags;
/* flags */
UINT8_TO_BSTREAM(p, flags);
/* sequence number */
UINT16_TO_BSTREAM(p, pGlm->seqNum);
/* base time */
UINT16_TO_BSTREAM(p, pGlm->baseTime.year);
UINT8_TO_BSTREAM(p, pGlm->baseTime.month);
UINT8_TO_BSTREAM(p, pGlm->baseTime.day);
UINT8_TO_BSTREAM(p, pGlm->baseTime.hour);
UINT8_TO_BSTREAM(p, pGlm->baseTime.min);
UINT8_TO_BSTREAM(p, pGlm->baseTime.sec);
/* time offset */
if (flags & CH_GLM_FLAG_TIME_OFFSET)
{
UINT16_TO_BSTREAM(p, pGlm->timeOffset);
}
/* glucose concentration, type, and sample location */
if (flags & CH_GLM_FLAG_CONC_TYPE_LOC)
{
UINT16_TO_BSTREAM(p, pGlm->concentration);
UINT8_TO_BSTREAM(p, pGlm->typeSampleLoc);
}
/* sensor status annunciation */
if (flags & CH_GLM_FLAG_SENSOR_STATUS)
{
UINT16_TO_BSTREAM(p, pGlm->sensorStatus);
}
/* return length */
return (uint8_t) (p - pBuf);
}
/*************************************************************************************************/
/*!
* \brief Build a glucose measurement context characteristic.
*
* \param pBuf Pointer to buffer to hold the built glucose measurement context.
* \param pGlmc Glucose measurement context values.
*
* \return Length of pBuf in bytes.
*/
/*************************************************************************************************/
static uint8_t glpsBuildGlmc(uint8_t *pBuf, glpsGlmc_t *pGlmc)
{
uint8_t *p = pBuf;
uint8_t flags = pGlmc->flags;
/* flags */
UINT8_TO_BSTREAM(p, flags);
/* sequence number */
UINT16_TO_BSTREAM(p, pGlmc->seqNum);
/* extended flags present */
if (flags & CH_GLMC_FLAG_EXT)
{
UINT8_TO_BSTREAM(p, pGlmc->extFlags);
}
/* carbohydrate id and carbohydrate present */
if (flags & CH_GLMC_FLAG_CARB)
{
UINT8_TO_BSTREAM(p, pGlmc->carbId);
UINT16_TO_BSTREAM(p, pGlmc->carb);
}
/* meal present */
if (flags & CH_GLMC_FLAG_MEAL)
{
UINT8_TO_BSTREAM(p, pGlmc->meal);
}
/* tester-health present */
if (flags & CH_GLMC_FLAG_TESTER)
{
UINT8_TO_BSTREAM(p, pGlmc->testerHealth);
}
/* exercise duration and exercise intensity present */
if (flags & CH_GLMC_FLAG_EXERCISE)
{
UINT16_TO_BSTREAM(p, pGlmc->exerDuration);
UINT8_TO_BSTREAM(p, pGlmc->exerIntensity);
}
/* medication ID and medication present */
if (flags & CH_GLMC_FLAG_MED)
{
UINT8_TO_BSTREAM(p, pGlmc->medicationId);
UINT16_TO_BSTREAM(p, pGlmc->medication);
}
/* hba1c present */
if (flags & CH_GLMC_FLAG_HBA1C)
{
UINT16_TO_BSTREAM(p, pGlmc->hba1c);
}
/* return length */
return (uint8_t) (p - pBuf);
}
/*************************************************************************************************/
/*!
* \brief Send a glucose measurement context notification.
*
* \param connId Connection ID.
* \param pRec Return pointer to glucose record.
*
* \return None.
*/
/*************************************************************************************************/
void glpsSendMeasContext(dmConnId_t connId, glpsRec_t *pRec)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* build glucose measurement context characteristic */
len = glpsBuildGlmc(buf, &glpsCb.pCurrRec->context);
/* send notification */
AttsHandleValueNtf(connId, GLS_GLMC_HDL, len, buf);
glpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Send a glucose measurement notification.
*
* \param connId Connection ID.
* \param pRec Return pointer to glucose record.
*
* \return None.
*/
/*************************************************************************************************/
void glpsSendMeas(dmConnId_t connId, glpsRec_t *pRec)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* build glucose measurement characteristic */
len = glpsBuildGlm(buf, &glpsCb.pCurrRec->meas);
/* send notification */
AttsHandleValueNtf(connId, GLS_GLM_HDL, len, buf);
glpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Send a RACP response indication.
*
* \param connId Connection ID.
* \param opcode RACP opcode.
* \param status RACP status.
*
* \return None.
*/
/*************************************************************************************************/
void glpsRacpSendRsp(dmConnId_t connId, uint8_t opcode, uint8_t status)
{
uint8_t buf[GLPS_RACP_RSP_LEN];
/* build response */
buf[0] = CH_RACP_OPCODE_RSP;
buf[1] = CH_RACP_OPERATOR_NULL;
buf[2] = opcode;
buf[3] = status;
/* send indication */
AttsHandleValueInd(connId, GLS_RACP_HDL, GLPS_RACP_RSP_LEN, buf);
glpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Send a RACP number of records response indication.
*
* \param connId Connection ID.
* \param numRec Number of records.
*
* \return None.
*/
/*************************************************************************************************/
void glpsRacpSendNumRecRsp(dmConnId_t connId, uint16_t numRec)
{
uint8_t buf[GLPS_RACP_NUM_REC_RSP_LEN];
/* build response */
buf[0] = CH_RACP_OPCODE_NUM_RSP;
buf[1] = CH_RACP_OPERATOR_NULL;
buf[2] = UINT16_TO_BYTE0(numRec);
buf[3] = UINT16_TO_BYTE1(numRec);
/* send indication */
AttsHandleValueInd(connId, GLS_RACP_HDL, GLPS_RACP_RSP_LEN, buf);
glpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Handle connection open.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void glpsConnOpen(dmEvt_t *pMsg)
{
/* initialize */
glpsCb.pCurrRec = NULL;
glpsCb.aborting = FALSE;
glpsCb.inProgress = FALSE;
glpsCb.txReady = FALSE;
glpsCb.oper = CH_RACP_OPERATOR_NULL;
}
/*************************************************************************************************/
/*!
* \brief Handle connection close.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void glpsConnClose(dmEvt_t *pMsg)
{
glpsCb.pCurrRec = NULL;
glpsCb.aborting = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Handle an ATT handle value confirm.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void glpsHandleValueCnf(attEvt_t *pMsg)
{
dmConnId_t connId = (dmConnId_t) pMsg->hdr.param;
glpsCb.txReady = TRUE;
/* if aborting finish that up */
if (glpsCb.aborting)
{
glpsCb.aborting = FALSE;
glpsRacpSendRsp(connId, CH_RACP_OPCODE_ABORT, CH_RACP_RSP_SUCCESS);
}
/* if this is for RACP indication */
if (pMsg->handle == GLS_RACP_HDL)
{
/* procedure no longer in progress */
glpsCb.inProgress = FALSE;
}
/* if this is for measurement or context notification */
else if (pMsg->handle == GLS_GLM_HDL || pMsg->handle == GLS_GLMC_HDL)
{
if (glpsCb.pCurrRec != NULL)
{
/* if measurement was sent and there is context to send */
if (pMsg->handle == GLS_GLM_HDL && AttsCccEnabled(connId, glpsCb.glmcCccIdx) &&
(glpsCb.pCurrRec->meas.flags & CH_GLM_FLAG_CONTEXT_INFO))
{
/* send context */
glpsSendMeasContext(connId, glpsCb.pCurrRec);
}
/* else if there is another record */
else if (glpsDbGetNextRecord(glpsCb.oper, glpsCb.operand,
glpsCb.pCurrRec, &glpsCb.pCurrRec) == CH_RACP_RSP_SUCCESS)
{
/* send measurement */
glpsSendMeas(connId, glpsCb.pCurrRec);
}
/* else all records sent; send RACP response */
else
{
glpsRacpSendRsp(connId, CH_RACP_OPCODE_REPORT, CH_RACP_RSP_SUCCESS);
}
}
}
}
/*************************************************************************************************/
/*!
* \brief Verify validity of operand data. If valid, store the data.
*
* \param oper Operator.
* \param len Operand length.
* \param pOperand Operand data.
*
* \return RACP status.
*/
/*************************************************************************************************/
static uint8_t glpsRacpOperCheck(uint8_t oper, uint16_t len, uint8_t *pOperand)
{
uint8_t status = CH_RACP_RSP_SUCCESS;
uint8_t filterType;
uint8_t filterLen = 0;
/* these operators have no operands */
if (oper == CH_RACP_OPERATOR_ALL || oper == CH_RACP_OPERATOR_FIRST ||
oper == CH_RACP_OPERATOR_LAST || oper == CH_RACP_OPERATOR_NULL)
{
if (len != 0)
{
status = CH_RACP_RSP_INV_OPERAND;
}
}
/* remaining operators must have operands */
else if (oper == CH_RACP_OPERATOR_LTEQ || oper == CH_RACP_OPERATOR_GTEQ ||
oper == CH_RACP_OPERATOR_RANGE)
{
if (len == 0)
{
status = CH_RACP_RSP_INV_OPERAND;
}
else
{
filterType = *pOperand;
len--;
/* operand length depends on filter type */
if (filterType == CH_RACP_GLS_FILTER_SEQ)
{
filterLen = CH_RACP_GLS_FILTER_SEQ_LEN;
}
else if (filterType == CH_RACP_GLS_FILTER_TIME)
{
filterLen = CH_RACP_GLS_FILTER_TIME_LEN;
}
else
{
status = CH_RACP_RSP_OPERAND_NOT_SUP;
}
if (status == CH_RACP_RSP_SUCCESS)
{
/* range operator has two filters, others have one */
if (oper == CH_RACP_OPERATOR_RANGE)
{
filterLen *= 2;
}
/* verify length */
if (len != filterLen)
{
status = CH_RACP_RSP_INV_OPERAND;
}
}
}
}
/* unknown operator */
else
{
status = CH_RACP_RSP_OPERATOR_NOT_SUP;
}
/* store operator and operand */
if (status == CH_RACP_RSP_SUCCESS)
{
glpsCb.oper = oper;
memcpy(glpsCb.operand, pOperand, len);
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Handle a RACP report stored records operation.
*
* \param connId Connection ID.
* \param oper Operator.
* \param pOperand Operand data.
*
* \return None.
*/
/*************************************************************************************************/
static void glpsRacpReport(dmConnId_t connId, uint8_t oper, uint8_t *pOperand)
{
uint8_t status;
/* if record found */
if ((status = glpsDbGetNextRecord(oper, pOperand, NULL, &glpsCb.pCurrRec)) == CH_RACP_RSP_SUCCESS)
{
/* send measurement */
glpsSendMeas(connId, glpsCb.pCurrRec);
}
/* if not successful send response */
else
{
glpsRacpSendRsp(connId, CH_RACP_OPCODE_REPORT, status);
}
}
/*************************************************************************************************/
/*!
* \brief Handle a RACP delete records operation.
*
* \param connId Connection ID.
* \param oper Operator.
* \param pOperand Operand data.
*
* \return None.
*/
/*************************************************************************************************/
static void glpsRacpDelete(dmConnId_t connId, uint8_t oper, uint8_t *pOperand)
{
uint8_t status;
/* delete records */
status = glpsDbDeleteRecords(oper, pOperand);
/* send response */
glpsRacpSendRsp(connId, CH_RACP_OPCODE_DELETE, status);
}
/*************************************************************************************************/
/*!
* \brief Handle a RACP abort operation.
*
* \param connId Connection ID.
*
* \return None.
*/
/*************************************************************************************************/
static void glpsRacpAbort(dmConnId_t connId)
{
/* if operation in progress */
if (glpsCb.inProgress)
{
/* abort operation and clean up */
glpsCb.pCurrRec = NULL;
}
/* send response */
if (glpsCb.txReady)
{
glpsRacpSendRsp(connId, CH_RACP_OPCODE_ABORT, CH_RACP_RSP_SUCCESS);
}
else
{
glpsCb.aborting = TRUE;
}
}
/*************************************************************************************************/
/*!
* \brief Handle a RACP report number of stored records operation.
*
* \param connId Connection ID.
* \param oper Operator.
* \param pOperand Operand data.
*
* \return None.
*/
/*************************************************************************************************/
static void glpsRacpReportNum(dmConnId_t connId, uint8_t oper, uint8_t *pOperand)
{
uint8_t status;
uint8_t numRec;
/* get number of records */
status = glpsDbGetNumRecords(oper, pOperand, &numRec);
if (status == CH_RACP_RSP_SUCCESS)
{
/* send response */
/* note: status check guarantees numRec has been initialized */
/* coverity[uninit_use_in_call] */
glpsRacpSendNumRecRsp(connId, numRec);
}
else
{
glpsRacpSendRsp(connId, CH_RACP_OPCODE_REPORT_NUM, status);
}
}
/*************************************************************************************************/
/*!
*
* \brief Toggle bonding flag in Glucose Feature Characteristic.
*
* \return None.
*/
/*************************************************************************************************/
static void glpsToggleBondingFlag(void)
{
uint8_t *pGlpsFlagsValue;
uint16_t len;
/* Get flags */
if (AttsGetAttr(GLS_GLF_HDL, &len, &pGlpsFlagsValue) == ATT_SUCCESS)
{
uint16_t glpsFlags;
BYTES_TO_UINT16(glpsFlags, pGlpsFlagsValue);
if (glpsFlags & CH_GLF_MULTI_BOND)
{
glpsFlags &= ~CH_GLF_MULTI_BOND;
}
else
{
glpsFlags |= CH_GLF_MULTI_BOND;
}
GlpsSetFeature(glpsFlags);
}
}
/*************************************************************************************************/
/*!
* \fn GlpsInit
*
* \brief Initialize the Glucose profile sensor.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsInit(void)
{
glpsDbInit();
}
/*************************************************************************************************/
/*!
* \brief This function is called by the application when a message that requires
* processing by the glucose profile sensor is received.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case DM_CONN_OPEN_IND:
glpsConnOpen((dmEvt_t *) pMsg);
break;
case DM_CONN_CLOSE_IND:
glpsConnClose((dmEvt_t *) pMsg);
break;
case ATTS_HANDLE_VALUE_CNF:
glpsHandleValueCnf((attEvt_t *) pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsBtn(dmConnId_t connId, uint8_t btn)
{
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_2_MED:
/* Toggle medication quantity, for test purposes only */
glpsDbToggleMedicationUnits();
break;
case APP_UI_BTN_2_LONG:
/* generate a new record */
glpsDbGenerateRecord();
break;
default:
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_2_SHORT:
/* Toggle bonding flag */
glpsToggleBondingFlag();
break;
case APP_UI_BTN_2_MED:
/* Toggle medication quantity, for test purposes only */
glpsDbToggleMedicationUnits();
break;
case APP_UI_BTN_2_LONG:
/* generate a new record */
glpsDbGenerateRecord();
break;
case APP_UI_BTN_2_EX_LONG:
/* delete all records */
glpsDbDeleteRecords(CH_RACP_OPERATOR_ALL, NULL);
break;
default:
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for glucose service record access control point. Use this
* function as a parameter to SvcGlsCbackRegister().
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t GlpsRacpWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
uint8_t opcode;
uint8_t oper;
uint8_t status;
/* sanity check on length */
if (len < GLPS_RACP_MIN_WRITE_LEN)
{
return ATT_ERR_LENGTH;
}
/* if control point not configured for indication */
if (!AttsCccEnabled(connId, glpsCb.racpCccIdx))
{
return GLS_ERR_CCCD;
}
/* parse opcode and operator and adjust remaining parameter length */
BSTREAM_TO_UINT8(opcode, pValue);
BSTREAM_TO_UINT8(oper, pValue);
len -= 2;
/* handle a procedure in progress */
if (opcode != CH_RACP_OPCODE_ABORT && glpsCb.inProgress)
{
return GLS_ERR_IN_PROGRESS;
}
/* handle record request when notifications not enabled */
if (opcode == CH_RACP_OPCODE_REPORT && !AttsCccEnabled(connId, glpsCb.glmCccIdx))
{
return GLS_ERR_CCCD;
}
/* verify operands */
if ((status = glpsRacpOperCheck(oper, len, pValue)) == CH_RACP_RSP_SUCCESS)
{
switch (opcode)
{
/* report records */
case CH_RACP_OPCODE_REPORT:
glpsRacpReport(connId, oper, pValue);
break;
/* delete records */
case CH_RACP_OPCODE_DELETE:
glpsRacpDelete(connId, oper, pValue);
break;
/* abort current operation */
case CH_RACP_OPCODE_ABORT:
glpsRacpAbort(connId);
break;
/* report number of records */
case CH_RACP_OPCODE_REPORT_NUM:
glpsRacpReportNum(connId, oper, pValue);
break;
/* unsupported opcode */
default:
glpsRacpSendRsp(connId, opcode, CH_RACP_RSP_OPCODE_NOT_SUP);
break;
}
}
/* else invalid operands; send response with failure status */
else
{
glpsRacpSendRsp(connId, opcode, status);
}
/* procedure now in progress */
glpsCb.inProgress = TRUE;
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Set the supported features of the glucose sensor.
*
* \param feature Feature bitmask.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsSetFeature(uint16_t feature)
{
uint8_t buf[2] = {UINT16_TO_BYTES(feature)};
AttsSetAttr(GLS_GLF_HDL, sizeof(buf), buf);
}
/*************************************************************************************************/
/*!
* \brief Set the CCCD index used by the application for glucose service characteristics.
*
* \param glmCccIdx Glucose measurement CCCD index.
* \param glmcCccIdx Glucose measurement context CCCD index.
* \param racpCccIdx Record access control point CCCD index.
*
* \return None.
*/
/*************************************************************************************************/
void GlpsSetCccIdx(uint8_t glmCccIdx, uint8_t glmcCccIdx, uint8_t racpCccIdx)
{
glpsCb.glmCccIdx = glmCccIdx;
glpsCb.glmcCccIdx = glmCccIdx;
glpsCb.racpCccIdx = racpCccIdx;
}
@@ -0,0 +1,167 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Glucose profile sensor internal interfaces.
*
* 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 GLPS_MAIN_H
#define GLPS_MAIN_H
#include "app_hw.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GLUCOSE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Minimum RACP write length */
#define GLPS_RACP_MIN_WRITE_LEN 2
/*! \brief RACP response length */
#define GLPS_RACP_RSP_LEN 4
/*! \brief Glucose RACP number of stored records response length */
#define GLPS_RACP_NUM_REC_RSP_LEN 4
/*! \brief RACP operand maximum length */
#define GLPS_OPERAND_MAX ((CH_RACP_GLS_FILTER_TIME_LEN * 2) + 1)
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Glucose measurement structure */
typedef struct
{
uint8_t flags; /*!< \brief Flags */
uint16_t seqNum; /*!< \brief Sequence number */
appDateTime_t baseTime; /*!< \brief Base time */
int16_t timeOffset; /*!< \brief Time offset */
uint16_t concentration; /*!< \brief Glucose concentration (SFLOAT) */
uint8_t typeSampleLoc; /*!< \brief Sample type and sample location */
uint16_t sensorStatus; /*!< \brief Sensor status annunciation */
} glpsGlm_t;
/*! \brief Glucose measurement context structure */
typedef struct
{
uint8_t flags; /*!< \brief Flags */
uint16_t seqNum; /*!< \brief Sequence number */
uint8_t extFlags; /*!< \brief Extended Flags */
uint8_t carbId; /*!< \brief Carbohydrate ID */
uint16_t carb; /*!< \brief Carbohydrate (SFLOAT) */
uint8_t meal; /*!< \brief Meal */
uint8_t testerHealth; /*!< \brief Tester and health */
uint16_t exerDuration; /*!< \brief Exercise Duration */
uint8_t exerIntensity; /*!< \brief Exercise Intensity */
uint8_t medicationId; /*!< \brief Medication ID */
uint16_t medication; /*!< \brief Medication (SFLOAT) */
uint16_t hba1c; /*!< \brief HbA1c */
} glpsGlmc_t;
/*! \brief Glucose measurement record */
typedef struct
{
glpsGlm_t meas; /*!< \brief Glucose measurement */
glpsGlmc_t context; /*!< \brief Glucose measurement context */
} glpsRec_t;
/*************************************************************************************************/
/*!
* \brief Initialize the glucose record database.
*
* \return None.
*/
/*************************************************************************************************/
void glpsDbInit(void);
/*************************************************************************************************/
/*!
* \brief Get the next record that matches the given filter parameters that follows
* the given current record.
*
* \param oper Operator.
* \param pFilter Glucose service RACP filter parameters.
* \param pCurrRec Pointer to current record.
* \param pRec Return pointer to next record, if found.
*
* \return \ref CH_RACP_RSP_SUCCESS if a record is found, otherwise an error status is returned.
*/
/*************************************************************************************************/
uint8_t glpsDbGetNextRecord(uint8_t oper, uint8_t *pFilter, glpsRec_t *pCurrRec, glpsRec_t **pRec);
/*************************************************************************************************/
/*!
* \brief Delete records that match the given filter parameters.
*
* \param oper Operator.
* \param pFilter Glucose service RACP filter parameters.
*
* \return \ref CH_RACP_RSP_SUCCESS if records deleted, otherwise an error status is returned.
*/
/*************************************************************************************************/
uint8_t glpsDbDeleteRecords(uint8_t oper, uint8_t *pFilter);
/*************************************************************************************************/
/*!
* \brief Get the number of records matching the filter parameters.
*
* \param oper Operator.
* \param pFilter Glucose service RACP filter parameters.
* \param pNumRec Returns number of records which match filter parameters.
*
* \return RACP status.
*/
/*************************************************************************************************/
uint8_t glpsDbGetNumRecords(uint8_t oper, uint8_t *pFilter, uint8_t *pNumRec);
/*************************************************************************************************/
/*!
* \brief Generate a new record.
*
* \return None.
*/
/*************************************************************************************************/
void glpsDbGenerateRecord(void);
/*************************************************************************************************/
/*!
* \brief For conformance testing only. Toggle the sample data record number 2's medication
* quantity unit flag between Kilograms and Liters. Also modifies quantity.
*
* \return None.
*/
/*************************************************************************************************/
void glpsDbToggleMedicationUnits(void);
/*! \} */ /* GLUCOSE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GLPS_MAIN_H */
@@ -0,0 +1,203 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Human Interface Device Profile.
*
* 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 HID_API_H
#define HID_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup HUMAN_INTERFACE_DEVICE_PROFILE
* \{ */
/**************************************************************************************************
Constant Values
**************************************************************************************************/
/** \name Type of HID Information
*
*/
/**@{*/
#define HID_INFO_CONTROL_POINT 0 /*!< \brief Control point information. */
#define HID_INFO_PROTOCOL_MODE 1 /*!< \brief Protocol mode information. */
/**@}*/
/** \name HID Boot Report ID
*
*/
/**@{*/
#define HID_KEYBOARD_BOOT_ID 0xFF /*!< \brief Keyboard boot ID. */
#define HID_MOUSE_BOOT_ID 0xFE /*!< \brief Mouse boot ID. */
/**@}*/
/**************************************************************************************************
Callback Function Types
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief This callback function sends a received Output Report to the application.
*
* \param connId The connection identifier.
* \param id The ID of the report.
* \param len The length of the report data in pReport.
* \param pReport A buffer containing the report.
*
* \return None.
*/
/*************************************************************************************************/
typedef void (*hidOutputReportCback_t)(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport);
/*************************************************************************************************/
/*!
* \brief This callback function sends a received Feature Report to the application.
*
* \param connId The connection identifier.
* \param id The ID of the report.
* \param len The length of the report data in pReport.
* \param pReport A buffer containing the report.
*
* \return None.
*/
/*************************************************************************************************/
typedef void (*hidFeatureReportCback_t)(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport);
/*************************************************************************************************/
/*!
* \brief This callback function notifies the application of a change in the protocol mode or
* control point from the host.
*
* \param connId The connection identifier.
* \param mode The type of information (\ref HID_INFO_CONTROL_POINT or \ref HID_INFO_PROTOCOL_MODE)
* \param value The value of the information
*
* \return None.
*/
/*************************************************************************************************/
typedef void (*hidInfoCback_t)(dmConnId_t connId, uint8_t type, uint8_t value);
/*! \brief HID Report Type/ID to Attribute handle map item */
typedef struct
{
uint8_t type; /*!< \brief Type */
uint8_t id; /*!< \brief Id */
uint16_t handle; /*!< \brief Handle */
} hidReportIdMap_t;
/*! \brief HID Profile Configuration */
typedef struct
{
hidReportIdMap_t *pReportIdMap; /*!< \brief A map between report Type/ID and Attribute handle */
uint8_t reportIdMapSize; /*!< \brief The number of Reports in the ID map (pReportIdMap) */
hidOutputReportCback_t outputCback; /*!< \brief Callback called on receipt of an Output Report */
hidFeatureReportCback_t featureCback; /*!< \brief Callback called on receipt of a Feature Report */
hidInfoCback_t infoCback; /*!< \brief Callback called on receipt of protocol mode or control point */
} hidConfig_t;
/**************************************************************************************************
API Functions
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Sends an input report to the host
*
* \param connId The connection ID
* \param reportId The Report ID
* \param len The length of the report in bytes
* \param pValue A buffer containing the report
*
* \return none.
*/
/*************************************************************************************************/
void HidSendInputReport(dmConnId_t connId, uint8_t reportId, uint16_t len, uint8_t *pValue);
/*************************************************************************************************/
/*!
* \brief Sets the HID protocol mode for keyboard and mouse devices that support Boot Mode.
*
* \param protocolMode The protocol mode (\ref HID_PROTOCOL_MODE_REPORT or \ref HID_PROTOCOL_MODE_BOOT)
*
* \return None.
*/
/*************************************************************************************************/
void HidSetProtocolMode(uint8_t protocolMode);
/*************************************************************************************************/
/*!
* \brief Gets the HID protocol mode value.
*
* \return The protocol mode value (\ref HID_PROTOCOL_MODE_REPORT or \ref HID_PROTOCOL_MODE_BOOT).
*/
/*************************************************************************************************/
uint8_t HidGetProtocolMode(void);
/*************************************************************************************************/
/*!
* \brief Gets the HID control point value.
*
* \return The control point value (\ref HID_CONTROL_POINT_SUSPEND or \ref HID_CONTROL_POINT_RESUME).
*/
/*************************************************************************************************/
uint8_t HidGetControlPoint(void);
/*************************************************************************************************/
/*!
* \brief Initialize the HID profile.
*
* \param pConfig HID Configuration structure
*
* \return None.
*/
/*************************************************************************************************/
void HidInit(const hidConfig_t *pConfig);
/*************************************************************************************************/
/*!
* \brief Called on an ATTS Write to the HID Service.
*
* \param connId DM connection identifier.
* \param handle ATT handle.
* \param operation ATT operation.
* \param offset Write offset.
* \param len Write length.
* \param pValue Value to write.
* \param pAttr Attribute to write.
*
* \return ATT status.
*
*/
/*************************************************************************************************/
uint8_t HidAttsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue,
attsAttr_t *pAttr);
/*! \} */ /* HUMAN_INTERFACE_DEVICE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* HID_API_H */
@@ -0,0 +1,286 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Human Interface Device Profile.
*
* 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 "wsf_assert.h"
#include "wsf_trace.h"
#include "att_api.h"
#include "svc_hid.h"
#include "app_api.h"
#include "hid_api.h"
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! HID control block */
typedef struct
{
const hidConfig_t *pConfig; /* HID Configuration passed in from the application */
} hidCb_t;
hidCb_t hidCb;
/*************************************************************************************************/
/*!
* \brief Gets the attribute handle of a report given the type and id
*
* \param type The type of report (HID_REPORT_TYPE_INPUT, HID_REPORT_TYPE_OUTPUT, HID_REPORT_TYPE_FEATURE)
* \param id The ID of the report
*
* \return The attribute handle for the report or ATT_HANDLE_NONE if the report is not in the map.
*/
/*************************************************************************************************/
static uint16_t hidGetReportHandle(uint8_t type, uint8_t id)
{
hidReportIdMap_t *pMap;
uint8_t count;
uint8_t i;
WSF_ASSERT(hidCb.pConfig);
WSF_ASSERT(hidCb.pConfig->pReportIdMap);
pMap = hidCb.pConfig->pReportIdMap;
count = hidCb.pConfig->reportIdMapSize;
for (i = 0; i < count; i++)
{
if (pMap[i].type == type && pMap[i].id == id)
{
return pMap[i].handle;
}
}
return ATT_HANDLE_NONE;
}
/*************************************************************************************************/
/*!
* \brief Gets the type and ID of a report given a handle
*
* \param handle The attribute handle
*
* \return A pointer to the hidReportIdMap_t with the type and ID
*/
/*************************************************************************************************/
static hidReportIdMap_t *hidGetReportIdMap(uint16_t handle)
{
hidReportIdMap_t *pMap;
uint8_t count;
uint8_t i;
WSF_ASSERT(hidCb.pConfig);
WSF_ASSERT(hidCb.pConfig->pReportIdMap);
pMap = hidCb.pConfig->pReportIdMap;
count = hidCb.pConfig->reportIdMapSize;
for (i = 0; i < count; i++)
{
if (pMap[i].handle == handle)
{
return &pMap[i];
}
}
return NULL;
}
/*************************************************************************************************/
/*!
* \brief Gets the HID control point value.
*
* \return The control point value (HID_CONTROL_POINT_SUSPEND or HID_CONTROL_POINT_RESUME).
*/
/*************************************************************************************************/
uint8_t HidGetControlPoint(void)
{
uint16_t len = 1;
uint8_t *pValue = NULL;
AttsGetAttr(HID_CONTROL_POINT_HDL, &len, &pValue);
return *pValue;
}
/*************************************************************************************************/
/*!
* \brief Gets the HID protocol mode value.
*
* \return The protocol mode value (HID_PROTOCOL_MODE_REPORT or HID_PROTOCOL_MODE_BOOT).
*/
/*************************************************************************************************/
uint8_t HidGetProtocolMode(void)
{
uint16_t len = 1;
uint8_t *pValue = NULL;
AttsGetAttr(HID_PROTOCOL_MODE_HDL, &len, &pValue);
return *pValue;
}
/*************************************************************************************************/
/*!
* \brief Sets the HID protocol mode for keyboard and mouse devices that support Boot Mode.
*
* \param protocolMode The protocol mode (HID_PROTOCOL_MODE_REPORT or HID_PROTOCOL_MODE_BOOT)
*
* \return None.
*/
/*************************************************************************************************/
void HidSetProtocolMode(uint8_t protocolMode)
{
AttsSetAttr(HID_PROTOCOL_MODE_HDL, 1, &protocolMode);
}
/*************************************************************************************************/
/*!
* \brief Sends an input report to the host
*
* \param connId The connection ID
* \param reportId The Report ID
* \param len The length of the report in bytes
* \param pValue A buffer containing the report
*
* \return none.
*/
/*************************************************************************************************/
void HidSendInputReport(dmConnId_t connId, uint8_t reportId, uint16_t len, uint8_t *pValue)
{
uint16_t handle = hidGetReportHandle(HID_REPORT_TYPE_INPUT, reportId);
if (handle != ATT_HANDLE_NONE)
{
/* Store the attribute value */
AttsSetAttr(handle, len, pValue);
/* send notification */
AttsHandleValueNtf(connId, handle, len, pValue);
}
}
/*************************************************************************************************/
/*!
* \brief Called on an ATTS Write to the HID Service.
*
* \return ATT status.
*
*/
/*************************************************************************************************/
uint8_t HidAttsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue,
attsAttr_t *pAttr)
{
hidReportIdMap_t *pIdMap;
WSF_ASSERT(hidCb.pConfig);
switch (handle)
{
case HID_CONTROL_POINT_HDL:
/* notify the application */
if (hidCb.pConfig->infoCback != NULL)
{
hidCb.pConfig->infoCback(connId, HID_INFO_CONTROL_POINT, *pValue);
}
break;
case HID_PROTOCOL_MODE_HDL:
/* Record the value of the protocol mode */
HidSetProtocolMode(pValue[0]);
if (hidCb.pConfig->infoCback != NULL)
{
hidCb.pConfig->infoCback(connId, HID_INFO_PROTOCOL_MODE, *pValue);
}
break;
case HID_KEYBOARD_BOOT_OUT_HDL:
/* set the attribute value so it can be read by the host */
AttsSetAttr(handle, len, pValue);
/* notify the application */
if (hidCb.pConfig->outputCback != NULL)
{
pIdMap = hidGetReportIdMap(handle);
if (pIdMap != NULL)
{
hidCb.pConfig->outputCback(connId, pIdMap->id, len, pValue);
}
}
break;
default:
pIdMap = hidGetReportIdMap(handle);
if (pIdMap != NULL)
{
/* set the attribute value so it can be read by the host */
AttsSetAttr(handle, len, pValue);
/* notify the application */
if (pIdMap->type == HID_REPORT_TYPE_FEATURE)
{
if (hidCb.pConfig->featureCback != NULL)
{
hidCb.pConfig->featureCback(connId, pIdMap->id, len, pValue);
}
}
else if (pIdMap->type == HID_REPORT_TYPE_OUTPUT)
{
if (hidCb.pConfig->outputCback != NULL)
{
hidCb.pConfig->outputCback(connId, pIdMap->id, len, pValue);
}
}
}
break;
}
return 0;
}
/*************************************************************************************************/
/*!
* \brief Initialize the HID profile.
*
* \param pConfig HID Configuration structure
*
* \return None.
*/
/*************************************************************************************************/
void HidInit(const hidConfig_t *pConfig)
{
WSF_ASSERT(pConfig);
/* Store the configuration */
hidCb.pConfig = pConfig;
}
@@ -0,0 +1,103 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Heart Rate profile client.
*
* 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 HRPC_API_H
#define HRPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup HEART_RATE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Heart Rate service enumeration of handle indexes of characteristics to be discovered */
enum
{
HRPC_HRS_HRM_HDL_IDX, /*!< \brief Heart rate measurement */
HRPC_HRS_HRM_CCC_HDL_IDX, /*!< \brief Heart rate measurement CCC descriptor */
HRPC_HRS_BSL_HDL_IDX, /*!< \brief Body sensor location */
HRPC_HRS_HRCP_HDL_IDX, /*!< \brief Heart rate control point */
HRPC_HRS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Heart Rate service.
* Parameter pHdlList must point to an array of length \ref HRPC_HRS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void HrpcHrsDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Send a command to the heart rate control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param command Control point command.
*
* \return None.
*/
/*************************************************************************************************/
void HrpcHrsControl(dmConnId_t connId, uint16_t handle, uint8_t command);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref HRPC_HRS_HDL_LIST_LEN.
* If the ATT handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characterist handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t HrpcHrsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* HEART_RATE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* HRPC_API_H */
@@ -0,0 +1,259 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Heart Rate profile collector.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "svc_ch.h"
#include "app_api.h"
#include "hrpc_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Heart Rate service
*/
/* Characteristics for discovery */
/*! Heart rate measurement */
static const attcDiscChar_t hrpcHrsHrm =
{
attHrmChUuid,
ATTC_SET_REQUIRED
};
/*! Heart rate measurement CCC descriptor */
static const attcDiscChar_t hrpcHrsHrmCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Body sensor location */
static const attcDiscChar_t hrpcHrsBsl =
{
attBslChUuid,
0
};
/*! Heart rate control point */
static const attcDiscChar_t hrpcHrsHrcp =
{
attHrcpChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *hrpcHrsDiscCharList[] =
{
&hrpcHrsHrm, /*! Heart rate measurement */
&hrpcHrsHrmCcc, /*! Heart rate measurement CCC descriptor */
&hrpcHrsBsl, /*! Body sensor location */
&hrpcHrsHrcp, /*! Heart rate control point */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(HRPC_HRS_HDL_LIST_LEN == ((sizeof(hrpcHrsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Parse a heart rate measurement.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void hrcpHrsParseHrm(uint8_t *pValue, uint16_t len)
{
uint8_t flags;
uint16_t minLen = 1 + CH_HRM_LEN_VALUE_8BIT;
uint16_t heartRate;
uint16_t energyExp;
uint16_t rrInterval;
/* Suppress unused variable compile warning */
(void)heartRate; (void)energyExp; (void)rrInterval;
if (len > 0)
{
/* get flags */
BSTREAM_TO_UINT8(flags, pValue);
/* determine expected minimum length based on flags */
if (flags & CH_HRM_FLAGS_VALUE_16BIT)
{
minLen++;
}
if (flags & CH_HRM_FLAGS_ENERGY_EXP)
{
minLen += CH_HRM_LEN_ENERGY_EXP;
}
if (flags & CH_HRM_FLAGS_RR_INTERVAL)
{
minLen += CH_HRM_LEN_RR_INTERVAL;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Heart Rate meas len:%d minLen:%d", len, minLen);
return;
}
/* heart rate */
if (flags & CH_HRM_FLAGS_VALUE_16BIT)
{
BSTREAM_TO_UINT16(heartRate, pValue);
}
else
{
BSTREAM_TO_UINT8(heartRate, pValue);
}
APP_TRACE_INFO1(" Heart rate: %d", heartRate);
/* energy expended */
if (flags & CH_HRM_FLAGS_ENERGY_EXP)
{
BSTREAM_TO_UINT16(energyExp, pValue);
APP_TRACE_INFO1(" Energy Exp: %d", energyExp);
}
/* r-r interval */
if (flags & CH_HRM_FLAGS_RR_INTERVAL)
{
/* get length of r-r interval bytes */
len = len + CH_HRM_LEN_RR_INTERVAL - minLen;
/* if len is somehow missing a byte (len is odd) reduce by 1 */
if (len & 1)
{
len--;
}
/* parse r-r intervals */
do
{
BSTREAM_TO_UINT16(rrInterval, pValue);
APP_TRACE_INFO1(" r-r Interval: %d", rrInterval);
len -= 2;
} while (len > 0);
}
APP_TRACE_INFO1(" Flags:0x%02x", flags);
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Heart Rate service. Parameter
* pHdlList must point to an array of length HRPC_HRS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void HrpcHrsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attHrsSvcUuid,
HRPC_HRS_HDL_LIST_LEN, (attcDiscChar_t **) hrpcHrsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Send a command to the heart rate control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param command Control point command.
*
* \return None.
*/
/*************************************************************************************************/
void HrpcHrsControl(dmConnId_t connId, uint16_t handle, uint8_t command)
{
uint8_t buf[1];
if (handle != ATT_HANDLE_NONE)
{
buf[0] = command;
AttcWriteReq(connId, handle, sizeof(buf), buf);
}
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length HRPC_HRS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t HrpcHrsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t *p;
uint8_t sensorLoc;
uint8_t status = ATT_SUCCESS;
/* heart rate measurement */
if (pMsg->handle == pHdlList[HRPC_HRS_HRM_HDL_IDX])
{
/* parse value */
hrcpHrsParseHrm(pMsg->pValue, pMsg->valueLen);
}
/* body sensor location */
else if (pMsg->handle == pHdlList[HRPC_HRS_BSL_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(sensorLoc, p);
/* ignore if out of range */
if (sensorLoc <= CH_BSENSOR_LOC_FOOT)
{
APP_TRACE_INFO1("Body sensor location:%d", sensorLoc);
}
}
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,131 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Heart Rate profile sensor.
*
* 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 HRPS_API_H
#define HRPS_API_H
#include "wsf_timer.h"
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup HEART_RATE_PROFILE
* \{ */
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Configurable parameters */
typedef struct
{
wsfTimerTicks_t period; /*!< \brief Measurement timer expiration period in ms */
} hrpsCfg_t;
/*************************************************************************************************/
/*!
* \brief Initialize the Heart Rate profile sensor.
*
* \param handlerId WSF handler ID of the application using this service.
* \param pCfg Configurable parameters.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsInit(wsfHandlerId_t handlerId, hrpsCfg_t *pCfg);
/*************************************************************************************************/
/*!
* \brief Start periodic heart rate measurement. This function starts a timer to perform
* periodic measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param hrmCccIdx Index of heart rate CCC descriptor in CCC descriptor handle table.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsMeasStart(dmConnId_t connId, uint8_t timerEvt, uint8_t hrmCccIdx);
/*************************************************************************************************/
/*!
* \brief Stop periodic heart rate measurement.
*
* \param connId DM connection identifier.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsMeasStop(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Process received WSF message.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsProcMsg(wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief ATTS write callback for heart rate service Use this function as a parameter
* to SvcHrsCbackRegister().
*
* \param connId DM connection identifier.
* \param handle ATT handle.
* \param operation ATT operation.
* \param offset Write offset.
* \param len Write length.
* \param pValue Value to write.
* \param pAttr Attribute to write.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t HrpsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr);
/*************************************************************************************************/
/*!
* \brief Set the heart rate measurement flags.
*
* \param flags Heart rate measurement flags.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsSetFlags(uint8_t flags);
/*! \} */ /* HEART_RATE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* HRPS_API_H */
@@ -0,0 +1,465 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Heart Rate profile sensor.
*
* 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_assert.h"
#include "wsf_buf.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_hrs.h"
#include "app_api.h"
#include "app_hw.h"
#include "hrps_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! \brief Connection control block */
typedef struct
{
dmConnId_t connId; /*! \brief Connection ID */
bool_t hrmToSend; /*! \brief heart rate measurement ready to be sent on this channel */
} hrpsConn_t;
/*! \brief Control block */
static struct
{
hrpsConn_t conn[DM_CONN_MAX]; /* \brief connection control block */
wsfTimer_t measTimer; /* \brief periodic measurement timer */
appHrm_t hrm; /* \brief heart rate measurement */
hrpsCfg_t cfg; /* \brief configurable parameters */
uint16_t energyExp; /* \brief energy expended value */
bool_t txReady; /* \brief TRUE if ready to send notifications */
uint8_t flags; /* \brief heart rate measurement flags */
} hrpsCb;
/*************************************************************************************************/
/*!
* \brief Return TRUE if no connections with active measurements.
*
* \return TRUE if no connections active.
*/
/*************************************************************************************************/
static bool_t hrpsNoConnActive(void)
{
hrpsConn_t *pConn = hrpsCb.conn;
uint8_t i;
for (i = 0; i < DM_CONN_MAX; i++, pConn++)
{
if (pConn->connId != DM_CONN_ID_NONE)
{
return FALSE;
}
}
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Setup to send measurements on active connections.
*
* \return None.
*/
/*************************************************************************************************/
static void hrpsSetupToSend(void)
{
hrpsConn_t *pConn = hrpsCb.conn;
uint8_t i;
for (i = 0; i < DM_CONN_MAX; i++, pConn++)
{
if (pConn->connId != DM_CONN_ID_NONE)
{
pConn->hrmToSend = TRUE;
}
}
}
/*************************************************************************************************/
/*!
* \brief Find next connection with measurement to send.
*
* \param cccIdx Heart rate measurement CCC descriptor index.
*
* \return Connection control block.
*/
/*************************************************************************************************/
static hrpsConn_t *hrpsFindNextToSend(uint8_t cccIdx)
{
hrpsConn_t *pConn = hrpsCb.conn;
uint8_t i;
for (i = 0; i < DM_CONN_MAX; i++, pConn++)
{
if (pConn->connId != DM_CONN_ID_NONE && pConn->hrmToSend)
{
if (AttsCccEnabled(pConn->connId, cccIdx))
{
return pConn;
}
}
}
return NULL;
}
/*************************************************************************************************/
/*!
* \brief Build a heart rate measurement characteristic.
*
* \param connId DM connection identifier.
* \param pBuf Pointer to buffer to hold the built heart rate measurement characteristic.
* \param pHrm Heart rate measurement values.
*
* \return Length of pBuf in bytes.
*/
/*************************************************************************************************/
static uint8_t hrpsBuildHrm(dmConnId_t connId, uint8_t **pBuf, appHrm_t *pHrm)
{
uint8_t *pHrpsData;
uint8_t flags = pHrm->flags;
uint8_t i;
uint16_t *pInterval;
uint8_t len = 2; /* Start with 2 for flags and 1 Byte Heart Rate measurement */
uint8_t maxLen = AttGetMtu(connId) - ATT_VALUE_NTF_LEN;
/* Calculate Buffer length */
if (flags & CH_HRM_FLAGS_VALUE_16BIT)
{
len += 1;
}
if (flags & CH_HRM_FLAGS_ENERGY_EXP)
{
len += 2;
}
/* rr interval */
if (flags & CH_HRM_FLAGS_RR_INTERVAL)
{
len += pHrm->numIntervals * sizeof(uint16_t);
}
/* Adjust length if necessary */
if (len > maxLen)
{
len = maxLen;
}
/* Allocate buffer */
if ((*pBuf = (uint8_t *)WsfBufAlloc(len)) != NULL)
{
/* Add data to buffer */
pHrpsData = *pBuf;
/* flags */
UINT8_TO_BSTREAM(pHrpsData, flags);
/* Subtract 2 for flags and 1 Byte Heart Rate measurement */
len -= 2;
/* heart rate measurement */
if (flags & CH_HRM_FLAGS_VALUE_16BIT)
{
UINT16_TO_BSTREAM(pHrpsData, (uint16_t)pHrm->heartRate);
/* Subtract an additional byte for a 2 byte Heart Rate measurement */
len -= 1;
}
else
{
UINT8_TO_BSTREAM(pHrpsData, pHrm->heartRate);
}
/* energy expended */
if (flags & CH_HRM_FLAGS_ENERGY_EXP)
{
UINT16_TO_BSTREAM(pHrpsData, pHrm->energyExp);
len -= 2;
}
/* rr interval */
if (flags & CH_HRM_FLAGS_RR_INTERVAL)
{
pInterval = pHrm->pRrInterval;
/* Use as many rr intervals as will fit in remaining buffer space. */
i = pHrm->numIntervals < (len / sizeof(uint16_t)) ?
pHrm->numIntervals : (len / sizeof(uint16_t));
for (; i > 0; i--, pInterval++)
{
UINT16_TO_BSTREAM(pHrpsData, *pInterval);
}
}
/* return length */
return (uint8_t)(pHrpsData - *pBuf);
}
return 0;
}
/*************************************************************************************************/
/*!
* \brief Send heart rate measurement notification
*
* \return None.
*/
/*************************************************************************************************/
static void hrpsSendHrmNtf(dmConnId_t connId)
{
uint8_t *pBuf;
uint8_t len;
/* Build heart rate measurement characteristic */
if ((len = hrpsBuildHrm(connId, &pBuf, &hrpsCb.hrm)) > 0)
{
/* Send notification */
AttsHandleValueNtf(connId, HRS_HRM_HDL, len, pBuf);
/* Free allocated buffer */
WsfBufFree(pBuf);
}
}
/*************************************************************************************************/
/*!
* \brief Handle connection open.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void hrpsConnOpen(dmEvt_t *pMsg)
{
hrpsCb.txReady = TRUE;
}
/*************************************************************************************************/
/*!
* \brief Handle a received ATT handle value confirm.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void hrpsHandleValueCnf(attEvt_t *pMsg)
{
hrpsConn_t *pConn;
if (pMsg->hdr.status == ATT_SUCCESS && pMsg->handle == HRS_HRM_HDL)
{
hrpsCb.txReady = TRUE;
/* find next connection to send (note ccc idx is stored in timer status) */
if ((pConn = hrpsFindNextToSend(hrpsCb.measTimer.msg.status)) != NULL)
{
hrpsSendHrmNtf(pConn->connId);
hrpsCb.txReady = FALSE;
pConn->hrmToSend = FALSE;
}
}
}
/*************************************************************************************************/
/*!
* \brief This function is called by the application when the periodic measurement
* timer expires.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void hrpsMeasTimerExp(wsfMsgHdr_t *pMsg)
{
hrpsConn_t *pConn;
/* if there are active connections */
if (hrpsNoConnActive() == FALSE)
{
/* set up heart rate measurement to be sent on all connections */
hrpsSetupToSend();
/* read heart rate measurement sensor data */
AppHwHrmRead(&hrpsCb.hrm);
/* if ready to send measurements */
if (hrpsCb.txReady)
{
/* find next connection to send (note ccc idx is stored in timer status) */
if ((pConn = hrpsFindNextToSend(pMsg->status)) != NULL)
{
hrpsSendHrmNtf(pConn->connId);
hrpsCb.txReady = FALSE;
pConn->hrmToSend = FALSE;
}
}
/* restart timer */
WsfTimerStartMs(&hrpsCb.measTimer, hrpsCb.cfg.period);
/* increment energy expended for test/demonstration purposes */
hrpsCb.hrm.energyExp++;
}
}
/*************************************************************************************************/
/*!
* \brief Initialize the Heart Rate profile sensor.
*
* \param handerId WSF handler ID of the application using this service.
* \param pCfg Configurable parameters.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsInit(wsfHandlerId_t handlerId, hrpsCfg_t *pCfg)
{
hrpsCb.measTimer.handlerId = handlerId;
hrpsCb.cfg = *pCfg;
}
/*************************************************************************************************/
/*!
* \brief Start periodic heart rate measurement. This function starts a timer to perform
* periodic measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param hrmCccIdx Index of heart rate CCC descriptor in CCC descriptor handle table.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsMeasStart(dmConnId_t connId, uint8_t timerEvt, uint8_t hrmCccIdx)
{
/* if this is first connection */
if (hrpsNoConnActive())
{
/* initialize control block */
hrpsCb.measTimer.msg.event = timerEvt;
hrpsCb.measTimer.msg.status = hrmCccIdx;
/* start timer */
WsfTimerStartMs(&hrpsCb.measTimer, hrpsCb.cfg.period);
}
/* set conn id */
hrpsCb.conn[connId - 1].connId = connId;
}
/*************************************************************************************************/
/*!
* \brief Stop periodic heart rate measurement.
*
* \param connId DM connection identifier.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsMeasStop(dmConnId_t connId)
{
/* clear connection */
hrpsCb.conn[connId - 1].connId = DM_CONN_ID_NONE;
hrpsCb.conn[connId - 1].hrmToSend = FALSE;
/* if no remaining connections */
if (hrpsNoConnActive())
{
/* stop timer */
WsfTimerStop(&hrpsCb.measTimer);
}
}
/*************************************************************************************************/
/*!
* \brief Process received WSF message.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsProcMsg(wsfMsgHdr_t *pMsg)
{
if (pMsg->event == DM_CONN_OPEN_IND)
{
hrpsConnOpen((dmEvt_t *) pMsg);
}
else if (pMsg->event == ATTS_HANDLE_VALUE_CNF)
{
hrpsHandleValueCnf((attEvt_t *) pMsg);
}
else if (pMsg->event == hrpsCb.measTimer.msg.event)
{
hrpsMeasTimerExp(pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for heart rate service. Use this function as a parameter
* to SvcHrsCbackRegister().
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t HrpsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
if (*pValue == CH_HRCP_RESET_ENERGY_EXP)
{
/* reset energy expended */
hrpsCb.hrm.energyExp = 0;
return ATT_SUCCESS;
}
else
{
/* else unknown control point command */
return HRS_ERR_CP_NOT_SUP;
}
}
/*************************************************************************************************/
/*!
* \brief Set the heart rate measurement flags.
*
* \param flags Heart rate measurement flags.
*
* \return None.
*/
/*************************************************************************************************/
void HrpsSetFlags(uint8_t flags)
{
hrpsCb.hrm.flags = flags;
}
@@ -0,0 +1,91 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health Thermometer profile client.
*
* 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 HTPC_API_H
#define HTPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup HEALTH_THERMOMETER_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Health Thermometer service enumeration of handle indexes of characteristics to be discovered */
enum
{
HTPC_HTS_TM_HDL_IDX, /*!< \brief Temperature measurement */
HTPC_HTS_TM_CCC_HDL_IDX, /*!< \brief Temperature measurement CCC descriptor */
HTPC_HTS_IT_HDL_IDX, /*!< \brief Intermediate temperature */
HTPC_HTS_IT_CCC_HDL_IDX, /*!< \brief Intermediate temperature CCC descriptor */
HTPC_HTS_TT_HDL_IDX, /*!< \brief Temperature type */
HTPC_HTS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Health Thermometer service.
* Parameter pHdlList must point to an array of length \ref HTPC_HTS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void HtpcHtsDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref HTPC_HTS_HDL_LIST_LEN.
* If the ATT handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristc handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t HtpcHtsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* HEALTH_THERMOMETER_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* HTPC_API_H */
@@ -0,0 +1,231 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health Thermometer profile collector.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "svc_ch.h"
#include "app_api.h"
#include "htpc_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Health Thermometer service
*/
/* Characteristics for discovery */
/*! Temperature measurement */
static const attcDiscChar_t htpcHtsTm =
{
attTmChUuid,
ATTC_SET_REQUIRED
};
/*! Temperature measurement CCC descriptor */
static const attcDiscChar_t htpcHtsTmCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Intermediate temperature */
static const attcDiscChar_t htpcHtsIt =
{
attItChUuid,
0
};
/*! Intermediate temperature CCC descriptor */
static const attcDiscChar_t htpcHtsItCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! Temperature type */
static const attcDiscChar_t htpcHtsTt =
{
attTtChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *htpcHtsDiscCharList[] =
{
&htpcHtsTm, /*! Temperature measurement */
&htpcHtsTmCcc, /*! Temperature measurement CCC descriptor */
&htpcHtsIt, /*! Intermediate temperature */
&htpcHtsItCcc, /*! Intermediate temperature CCC descriptor */
&htpcHtsTt /*! Temperature type */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(HTPC_HTS_HDL_LIST_LEN == ((sizeof(htpcHtsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Parse a temperature measurement.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void htpcHtsParseTm(uint8_t *pValue, uint16_t len)
{
uint8_t flags;
uint32_t tempUint;
int32_t tempM;
int8_t tempE;
uint16_t year;
uint8_t month, day, hour, min, sec;
uint8_t tempType;
uint16_t minLen = CH_TM_FLAGS_LEN + CH_TM_MEAS_LEN;
/* Suppress unused variable compile warning */
(void)tempM; (void)tempE; (void)tempType;
(void)year; (void)month; (void)day; (void)hour; (void)min; (void)sec;
if (len > 0)
{
/* get flags */
BSTREAM_TO_UINT8(flags, pValue);
/* determine expected minimum length based on flags */
if (flags & CH_TM_FLAG_TIMESTAMP)
{
minLen += CH_TM_TIMESTAMP_LEN;
}
if (flags & CH_TM_FLAG_TEMP_TYPE)
{
minLen += CH_TM_TEMP_TYPE_LEN;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Temperature meas len:%d minLen:%d", len, minLen);
return;
}
/* Temperature */
BSTREAM_TO_UINT32(tempUint, pValue);
UINT32_TO_FLT(tempM, tempE, tempUint);
APP_TRACE_INFO2(" Temperature:%de%d", tempM, tempE);
/* timestamp */
if (flags & CH_TM_FLAG_TIMESTAMP)
{
BSTREAM_TO_UINT16(year, pValue);
BSTREAM_TO_UINT8(month, pValue);
BSTREAM_TO_UINT8(day, pValue);
BSTREAM_TO_UINT8(hour, pValue);
BSTREAM_TO_UINT8(min, pValue);
BSTREAM_TO_UINT8(sec, pValue);
APP_TRACE_INFO3(" Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3(" Time: %02d:%02d:%02d", hour, min, sec);
}
/* temperature type */
if (flags & CH_TM_FLAG_TEMP_TYPE)
{
BSTREAM_TO_UINT8(tempType, pValue);
APP_TRACE_INFO1(" Temp. Type:%d", tempType);
}
APP_TRACE_INFO1(" Flags:0x%02x", flags);
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Health Thermometer service. Parameter
* pHdlList must point to an array of length HTPC_HTS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void HtpcHtsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attHtsSvcUuid,
HTPC_HTS_HDL_LIST_LEN, (attcDiscChar_t **) htpcHtsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length HTPC_HTS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t HtpcHtsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* temperature measurement */
if (pMsg->handle == pHdlList[HTPC_HTS_TM_HDL_IDX])
{
APP_TRACE_INFO0("Temperature measurement");
/* parse value */
htpcHtsParseTm(pMsg->pValue, pMsg->valueLen);
}
/* intermediate cuff pressure */
else if (pMsg->handle == pHdlList[HTPC_HTS_IT_HDL_IDX])
{
APP_TRACE_INFO0("Intermed. temperature");
/* parse value */
htpcHtsParseTm(pMsg->pValue, pMsg->valueLen);
}
/* temperature type */
else if (pMsg->handle == pHdlList[HTPC_HTS_TT_HDL_IDX])
{
APP_TRACE_INFO1("Temperature type:%d", pMsg->pValue[0]);
}
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,136 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health Thermometer profile sensor.
*
* 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 HTPS_API_H
#define HTPS_API_H
#include "wsf_timer.h"
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup HEALTH_THERMOMETER_PROFILE
* \{ */
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Configurable parameters */
typedef struct
{
wsfTimerTicks_t period; /*!< \brief Measurement timer expiration period in ms */
} htpsCfg_t;
/*************************************************************************************************/
/*!
* \brief Initialize the Health Thermometer profile sensor.
*
* \param handlerId WSF handler ID of the application using this service.
* \param pCfg Configurable parameters.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsInit(wsfHandlerId_t handlerId, htpsCfg_t *pCfg);
/*************************************************************************************************/
/*!
* \brief Start periodic temperature measurement. This function starts a timer to perform
* periodic measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param itCccIdx Index of intermediate temperature CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsMeasStart(dmConnId_t connId, uint8_t timerEvt, uint8_t itCccIdx);
/*************************************************************************************************/
/*!
* \brief Stop periodic temperature measurement.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsMeasStop(void);
/*************************************************************************************************/
/*!
* \brief Temperature measurement complete.
*
* \param connId DM connection identifier.
* \param tmCccIdx Index of temperature measurement CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsMeasComplete(dmConnId_t connId, uint8_t tmCccIdx);
/*************************************************************************************************/
/*!
* \brief This function is called by the application when the periodic measurement
* timer expires.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsProcMsg(wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Set the temperature measurement flags.
*
* \param flags Temperature measurement flags.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsSetTmFlags(uint8_t flags);
/*************************************************************************************************/
/*!
* \brief Set the intermediate temperature flags.
*
* \param flags Intermediate temperature flags.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsSetItFlags(uint8_t flags);
/*! \} */ /* HEALTH_THERMOMETER_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* HTPS_API_H */
@@ -0,0 +1,242 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health Thermometer profile sensor.
*
* 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 "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_hts.h"
#include "app_api.h"
#include "app_hw.h"
#include "htps_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
wsfTimer_t measTimer; /* periodic measurement timer */
appTm_t tm; /* temperature measurement */
htpsCfg_t cfg; /* configurable parameters */
uint8_t tmFlags; /* temperature measurement flags */
uint8_t itFlags; /* intermediate temperature flags */
} htpsCb;
/*************************************************************************************************/
/*!
* \brief Build a temperature measurement characteristic.
*
* \param pBuf Pointer to buffer to hold the built temperature measurement characteristic.
* \param pTm Temperature measurement values.
*
* \return Length of pBuf in bytes.
*/
/*************************************************************************************************/
static uint8_t htpsBuildTm(uint8_t *pBuf, appTm_t *pTm)
{
uint8_t *p = pBuf;
uint8_t flags = pTm->flags;
/* flags */
UINT8_TO_BSTREAM(p, flags);
/* measurement */
UINT32_TO_BSTREAM(p, pTm->temperature);
/* time stamp */
if (flags & CH_TM_FLAG_TIMESTAMP)
{
UINT16_TO_BSTREAM(p, pTm->timestamp.year);
UINT8_TO_BSTREAM(p, pTm->timestamp.month);
UINT8_TO_BSTREAM(p, pTm->timestamp.day);
UINT8_TO_BSTREAM(p, pTm->timestamp.hour);
UINT8_TO_BSTREAM(p, pTm->timestamp.min);
UINT8_TO_BSTREAM(p, pTm->timestamp.sec);
}
/* temperature type */
if (flags & CH_TM_FLAG_TEMP_TYPE)
{
UINT8_TO_BSTREAM(p, pTm->tempType);
}
/* return length */
return (uint8_t) (p - pBuf);
}
/*************************************************************************************************/
/*!
* \brief Initialize the Health Thermometer profile sensor.
*
* \param handerId WSF handler ID of the application using this service.
* \param pCfg Configurable parameters.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsInit(wsfHandlerId_t handlerId, htpsCfg_t *pCfg)
{
htpsCb.measTimer.handlerId = handlerId;
htpsCb.cfg = *pCfg;
}
/*************************************************************************************************/
/*!
* \brief Start periodic temperature measurement. This function starts a timer to perform
* periodic measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param itCccIdx Index of intermediate temperature CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsMeasStart(dmConnId_t connId, uint8_t timerEvt, uint8_t itCccIdx)
{
/* initialize control block */
htpsCb.measTimer.msg.param = connId;
htpsCb.measTimer.msg.event = timerEvt;
htpsCb.measTimer.msg.status = itCccIdx;
/* start timer */
WsfTimerStartMs(&htpsCb.measTimer, htpsCb.cfg.period);
}
/*************************************************************************************************/
/*!
* \brief Stop periodic temperature measurement.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsMeasStop(void)
{
/* stop timer */
WsfTimerStop(&htpsCb.measTimer);
}
/*************************************************************************************************/
/*!
* \brief Temperature measurement complete.
*
* \param connId DM connection identifier.
* \param tmCccIdx Index of temperature measurement CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsMeasComplete(dmConnId_t connId, uint8_t tmCccIdx)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* stop periodic measurement */
HtpsMeasStop();
/* if indications enabled */
if (AttsCccEnabled(connId, tmCccIdx))
{
/* read temperature measurement sensor data */
AppHwTmRead(FALSE, &htpsCb.tm);
/* set flags */
htpsCb.tm.flags = htpsCb.tmFlags;
/* build temperature measurement characteristic */
len = htpsBuildTm(buf, &htpsCb.tm);
/* send temperature measurement indication */
AttsHandleValueInd(connId, HTS_TM_HDL, len, buf);
}
}
/*************************************************************************************************/
/*!
* \brief This function is called by the application when the periodic measurement
* timer expires.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsProcMsg(wsfMsgHdr_t *pMsg)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* if notifications enabled (note ccc idx is stored in hdr.status) */
if (AttsCccEnabled((dmConnId_t) pMsg->param, pMsg->status))
{
/* read temperature measurement sensor data */
AppHwTmRead(TRUE, &htpsCb.tm);
/* set flags */
htpsCb.tm.flags = htpsCb.itFlags;
/* build temperature measurement characteristic */
len = htpsBuildTm(buf, &htpsCb.tm);
/* send intermediate temperature notification */
AttsHandleValueNtf((dmConnId_t) pMsg->param, HTS_IT_HDL, len, buf);
/* restart timer */
WsfTimerStartMs(&htpsCb.measTimer, htpsCb.cfg.period);
}
}
/*************************************************************************************************/
/*!
* \brief Set the temperature measurement flags.
*
* \param flags Temperature measurement flags.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsSetTmFlags(uint8_t flags)
{
htpsCb.tmFlags = flags;
}
/*************************************************************************************************/
/*!
* \brief Set the intermediate temperature flags.
*
* \param flags Intermediate temperature flags.
*
* \return None.
*/
/*************************************************************************************************/
void HtpsSetItFlags(uint8_t flags)
{
htpsCb.itFlags = flags;
}
@@ -0,0 +1,267 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange Protocol Definitions.
*
* Copyright (c) 2013-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 WDX_DEFS_H
#define WDX_DEFS_H
#ifdef __cplusplus
extern "C"
{
#endif
/*! \addtogroup WIRELESS_DATA_EXCHANGE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Base UUID: 005fXXXX-2ff2-4ed5-b045-4C7463617865 */
#define WDX_UUID_PART1 0x65, 0x78, 0x61, 0x63, 0x74, 0x4c, 0x45, 0xb0, \
0xd5, 0x4e, 0xf2, 0x2f
/*! \brief Base UUID Part 2 */
#define WDX_UUID_PART2 0x5f, 0x00
/*! \brief Macro for building UUIDs */
#define WDX_UUID_BUILD(part) WDX_UUID_PART1, UINT16_TO_BYTES(part), WDX_UUID_PART2
/*! \brief WDX Service */
#define WDX_SVC_UUID 0xFEF6
/*! \brief WDX Device Configuration Characteristic */
#define WDX_DC_UUID WDX_UUID_BUILD(0x0002)
/*! \brief WDX File Transfer Control Characteristic */
#define WDX_FTC_UUID WDX_UUID_BUILD(0x0003)
/*! \brief WDX File Transfer Data Characteristic */
#define WDX_FTD_UUID WDX_UUID_BUILD(0x0004)
/*! \brief WDX Authentication Characteristic */
#define WDX_AU_UUID WDX_UUID_BUILD(0x0005)
/**************************************************************************************************
Constant Definitions
**************************************************************************************************/
/** \name WDXS File List Configuration
*
*/
/**@{*/
#define WDX_FLIST_HANDLE 0 /*!< \brief File List handle */
#define WDX_FLIST_FORMAT_VER 1 /*!< \brief File List version */
#define WDX_FLIST_HDR_SIZE 7 /*!< \brief File List header length */
#define WDX_FLIST_RECORD_SIZE 40 /*!< \brief File List record length */
/*! \brief File list max length. */
#define WDX_FLIST_MAX_LEN (WDX_FLIST_HDR_SIZE + (WDX_FLIST_RECORD_SIZE * (WSF_EFS_MAX_FILES-1)))
/**@}*/
/*! \brief Device configuration characteristic message header length */
#define WDX_DC_HDR_LEN 2
/** \name Device Configuration Characteristic oOperations
*
*/
/**@{*/
#define WDX_DC_OP_GET 0x01 /*!< \brief Get a parameter value */
#define WDX_DC_OP_SET 0x02 /*!< \brief Set a parameter value */
#define WDX_DC_OP_UPDATE 0x03 /*!< \brief Send an update of a parameter value */
/**@}*/
/** \name Device Control Characteristic Parameter IDs
*
*/
/**@{*/
#define WDX_DC_ID_CONN_UPDATE_REQ 0x01 /*!< \brief Connection Parameter Update Request */
#define WDX_DC_ID_CONN_PARAM 0x02 /*!< \brief Current Connection Parameters */
#define WDX_DC_ID_DISCONNECT_REQ 0x03 /*!< \brief Disconnect Request */
#define WDX_DC_ID_CONN_SEC_LEVEL 0x04 /*!< \brief Connection Security Level */
#define WDX_DC_ID_SECURITY_REQ 0x05 /*!< \brief Security Request */
#define WDX_DC_ID_SERVICE_CHANGED 0x06 /*!< \brief Service Changed */
#define WDX_DC_ID_DELETE_BONDS 0x07 /*!< \brief Delete Bonds */
#define WDX_DC_ID_ATT_MTU 0x08 /*!< \brief Current ATT MTU */
#define WDX_DC_ID_PHY_UPDATE_REQ 0x09 /*!< \brief PHY update request */
#define WDX_DC_ID_PHY 0x0A /*!< \brief Current PHY */
#define WDX_DC_ID_BATTERY_LEVEL 0x20 /*!< \brief Battery level */
#define WDX_DC_ID_MODEL_NUMBER 0x21 /*!< \brief Device Model */
#define WDX_DC_ID_FIRMWARE_REV 0x22 /*!< \brief Device Firmware Revision */
#define WDX_DC_ID_ENTER_DIAGNOSTICS 0x23 /*!< \brief Enter Diagnostic Mode */
#define WDX_DC_ID_DIAGNOSTICS_COMPLETE 0x24 /*!< \brief Diagnostic Complete */
#define WDX_DC_ID_DISCONNECT_AND_RESET 0x25 /*!< \brief Disconnect and Reset */
/**@}*/
/** \name Device Control Parameter Lengths
*
*/
/**@{*/
#define WDX_DC_LEN_DATA_FORMAT 1 /*!< \brief Data format */
#define WDX_DC_LEN_SEC_LEVEL 1 /*!< \brief Security Level */
#define WDX_DC_LEN_ATT_MTU 2 /*!< \brief ATT MTU */
#define WDX_DC_LEN_BATTERY_LEVEL 1 /*!< \brief Battery level */
#define WDX_DC_LEN_CONN_PARAM_REQ 8 /*!< \brief Connection parameter request */
#define WDX_DC_LEN_CONN_PARAM 7 /*!< \brief Current connection parameters */
#define WDX_DC_LEN_PHY_UPDATE_REQ 5 /*!< \brief PHY update request */
#define WDX_DC_LEN_PHY 3 /*!< \brief Current PHY */
#define WDX_DC_LEN_DIAG_COMPLETE 0 /*!< \brief Diagnostic complete */
#define WDX_DC_LEN_DEVICE_MODEL 18 /*!< \brief Device Model */
#define WDX_DC_LEN_FIRMWARE_REV 16 /*!< \brief Firmware Revision */
/**@}*/
/** \name File Transfer Control Characteristic Message Header Length
*
*/
/**@{*/
#define WDX_FTC_HDR_LEN 1 /*!< \brief Header length. */
#define WDX_FTC_HANDLE_LEN 2 /*!< \brief Handle length. */
/** \name File Transfer Control Characteristic Operations
*
*/
/**@{*/
#define WDX_FTC_OP_NONE 0x00 /*!< \brief No operation */
#define WDX_FTC_OP_GET_REQ 0x01 /*!< \brief Get a file from the server */
#define WDX_FTC_OP_GET_RSP 0x02 /*!< \brief File get response */
#define WDX_FTC_OP_PUT_REQ 0x03 /*!< \brief Put a file to the server */
#define WDX_FTC_OP_PUT_RSP 0x04 /*!< \brief File put response */
#define WDX_FTC_OP_ERASE_REQ 0x05 /*!< \brief Erase a file on the server */
#define WDX_FTC_OP_ERASE_RSP 0x06 /*!< \brief File erase response */
#define WDX_FTC_OP_VERIFY_REQ 0x07 /*!< \brief Verify a file (e.g. check its CRC) */
#define WDX_FTC_OP_VERIFY_RSP 0x08 /*!< \brief File verify response */
#define WDX_FTC_OP_ABORT 0x09 /*!< \brief Abort a get, put, or list operation in progress */
#define WDX_FTC_OP_EOF 0x0a /*!< \brief End of file reached */
/**@}*/
/** \name File Transfer Control Permissions
*
*/
/**@{*/
#define WDX_FTC_GET_PERMITTED 0x01 /*!< \brief File Get Permitted */
#define WDX_FTC_PUT_PERMITTED 0x02 /*!< \brief File Put Permitted */
#define WDX_FTC_ERASE_PERMITTED 0x04 /*!< \brief File Erase Permitted */
#define WDX_FTC_VERIFY_PERMITTED 0x08 /*!< \brief File Verify Permitted */
/**@}*/
/** \name File Transfer Control Characteristic Status
*
*/
/**@{*/
#define WDX_FTC_ST_SUCCESS 0 /*!< \brief Success */
#define WDX_FTC_ST_INVALID_OP_FILE 1 /*!< \brief Invalid operation for this file */
#define WDX_FTC_ST_INVALID_HANDLE 2 /*!< \brief Invalid file handle */
#define WDX_FTC_ST_INVALID_OP_DATA 3 /*!< \brief Invalid operation data */
#define WDX_FTC_ST_IN_PROGRESS 4 /*!< \brief Operation in progress */
#define WDX_FTC_ST_VERIFICATION 5 /*!< \brief Verification failure */
/**@}*/
/** \name File Transfer Control Transport
*
*/
/**@{*/
#define WDX_FTC_TRANSPORT_TYPE 0 /*!< \brief Transport Type */
#define WDX_FTC_TRANSPORT_ID 0x0030 /*!< \brief Transport ID */
/**@}*/
/*! \brief File transfer data characteristic message header length */
#define WDX_FTD_HDR_LEN 0
/*! \brief Authentication message header length */
#define WDX_AU_HDR_LEN 1
/** \name Authentication Characteristic Operations
*
*/
/**@{*/
#define WDX_AU_OP_START 0x01 /*!< \brief Authentication start */
#define WDX_AU_OP_CHALLENGE 0x02 /*!< \brief Authentication challenge */
#define WDX_AU_OP_REPLY 0x03 /*!< \brief Authentication reply */
/**@}*/
/** \name Proprietary ATT Error Codes
*
*/
/**@{*/
#define WDX_APP_AUTH_REQUIRED 0x80 /*!< \brief Application authentication required */
#define WDX_AU_ST_INVALID_MESSAGE 0x81 /*!< \brief Authentication invalid message */
#define WDX_AU_ST_INVALID_STATE 0x82 /*!< \brief Authentication invalid state */
#define WDX_AU_ST_AUTH_FAILED 0x83 /*!< \brief Authentication failed */
/**@}*/
/** \name Authentication Characteristic Authentication Level
*
*/
/**@{*/
#define WDX_AU_LVL_NONE 0x00 /*!< \brief None */
#define WDX_AU_LVL_USER 0x01 /*!< \brief User level */
#define WDX_AU_LVL_MAINT 0x02 /*!< \brief Maintenance level */
#define WDX_AU_LVL_DEBUG 0x03 /*!< \brief Debug level */
/**@}*/
/** \name Authenttication Characteristic Message Parameter Lengths
*
*/
/**@{*/
#define WDX_AU_MSG_HDR_LEN 1 /*!< \brief Message header length */
#define WDX_AU_PARAM_LEN_START 2 /*!< \brief Authentication start */
#define WDX_AU_PARAM_LEN_CHALLENGE 16 /*!< \brief Authentication challenge */
#define WDX_AU_PARAM_LEN_REPLY 8 /*!< \brief Authentication reply */
/**@}*/
/** \name Authenttication Characteristic Random Number and Key Lengths
*
*/
/**@{*/
#define WDX_AU_RAND_LEN 16 /*!< \brief Authentication Random challenge length (bytes)*/
#define WDX_AU_KEY_LEN 16 /*!< \brief Authentication Key length (bytes) */
#define WDX_AU_HASH_LEN 8 /*!< \brief Authentication Hash length (bytes) */
/**@}*/
/** \name WDXS Media Types
*
*/
/**@{*/
#define WDX_FLASH_MEDIA 0 /*!< \brief Flash media type. */
#define WDX_OTA_MEDIA 1 /*!< \brief OTA media type. */
#define WDX_RAM_MEDIA 2 /*!< \brief RAM media type. */
#define WDX_STREAM_MEDIA 3 /*!< \brief Stream media type. */
/**@}*/
/** \name WDXS File Transfer Control Command Message Lengths
*
*/
/**@{*/
#define WDX_FTC_ABORT_LEN 3 /*!< \brief Abort message length. */
#define WDX_FTC_ERASE_LEN 3 /*!< \brief Erase message length. */
#define WDX_FTC_VERIFY_LEN 3 /*!< \brief Verify message length. */
#define WDX_FTC_PUT_LEN 16 /*!< \brief Put message length. */
#define WDX_FTC_GET_LEN 12 /*!< \brief Get message length. */
/**@}*/
/*! \} */ /* WIRELESS_DATA_EXCHANGE_PROFILE */
#ifdef __cplusplus
}
#endif
#endif /* WDX_DEFS_H */
@@ -0,0 +1,104 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Phone Alert Status profile client.
*
* 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 PASPC_API_H
#define PASPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup PHONE_ALERT_STATUS_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Phone Alert Status service enumeration of handle indexes of characteristics to be discovered */
enum
{
PASPC_PASS_AS_HDL_IDX, /*!< \brief Alert status */
PASPC_PASS_AS_CCC_HDL_IDX, /*!< \brief Alert status CCC descriptor */
PASPC_PASS_RS_HDL_IDX, /*!< \brief Ringer setting */
PASPC_PASS_RS_CCC_HDL_IDX, /*!< \brief Ringer setting CCC descriptor */
PASPC_PASS_RCP_HDL_IDX, /*!< \brief Ringer control point */
PASPC_PASS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Phone Alert Status service.
* Parameter pHdlList must point to an array of length \ref PASPC_PASS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void PaspcPassDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Send a command to the ringer control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param command Control point command.
*
* \return None.
*/
/*************************************************************************************************/
void PaspcPassControl(dmConnId_t connId, uint16_t handle, uint8_t command);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref PASPC_PASS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t PaspcPassValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* PHONE_ALERT_STATUS_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* PASPC_API_H */
@@ -0,0 +1,166 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Phone Alert Status profile client.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "app_api.h"
#include "paspc_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Phone Alert Status service
*/
/* Characteristics for discovery */
/*! Alert status */
static const attcDiscChar_t paspcPassAs =
{
attAsChUuid,
ATTC_SET_REQUIRED
};
/*! Alert status CCC descriptor */
static const attcDiscChar_t paspcPassAsCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Ringer setting */
static const attcDiscChar_t paspcPassRs =
{
attRsChUuid,
ATTC_SET_REQUIRED
};
/*! Ringer setting CCC descriptor */
static const attcDiscChar_t paspcPassRsCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Ringer control point */
static const attcDiscChar_t paspcPassRcp =
{
attRcpChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *paspcPassDiscCharList[] =
{
&paspcPassAs, /*! Alert status */
&paspcPassAsCcc, /*! Alert status CCC descriptor */
&paspcPassRs, /*! Ringer setting */
&paspcPassRsCcc, /*! Ringer setting CCC descriptor */
&paspcPassRcp /*! Ringer control point */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(PASPC_PASS_HDL_LIST_LEN == ((sizeof(paspcPassDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Phone Alert Status service. Parameter
* pHdlList must point to an array of length PASPC_ANS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void PaspcPassDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attPassSvcUuid,
PASPC_PASS_HDL_LIST_LEN, (attcDiscChar_t **) paspcPassDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Send a command to the ringer control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param command Control point command.
*
* \return None.
*/
/*************************************************************************************************/
void PaspcPassControl(dmConnId_t connId, uint16_t handle, uint8_t command)
{
uint8_t buf[1];
if (handle != ATT_HANDLE_NONE)
{
buf[0] = command;
AttcWriteCmd(connId, handle, sizeof(buf), buf);
}
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length PASPC_PASS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t PaspcPassValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* alert status */
if (pMsg->handle == pHdlList[PASPC_PASS_AS_HDL_IDX])
{
APP_TRACE_INFO1("Phone alert status: 0x%02x", *pMsg->pValue);
}
/* ringer setting */
else if (pMsg->handle == pHdlList[PASPC_PASS_RS_HDL_IDX])
{
APP_TRACE_INFO1("Ringer setting: 0x%02x", *pMsg->pValue);
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,108 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Pulse Oximeter profile collector.
*
* 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 PLXPC_API_H
#define PLXPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup PULSE_OXIMETER_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Pulse Oximeter service enumeration of handle indexes of characteristics to be discovered */
enum
{
PLXPC_PLXS_PLXSC_HDL_IDX, /*!< \brief Pulse Oximeter Spot Check measurement */
PLXPC_PLXS_PLXSC_CCC_HDL_IDX, /*!< \brief Pulse Oximeter Spot Check measurement CCC descriptor */
PLXPC_PLXS_PLXC_HDL_IDX, /*!< \brief Pulse Oximeter Continuous measurement */
PLXPC_PLXS_PLXC_CCC_HDL_IDX, /*!< \brief Pulse Oximeter Continuous measurement CCC descriptor */
PLXPC_PLXS_PLXF_HDL_IDX, /*!< \brief Pulse Oximeter features */
PLXPC_PLXS_RACP_HDL_IDX, /*!< \brief Record access control point */
PLXPC_PLXS_RACP_CCC_HDL_IDX, /*!< \brief Record access control point CCC descriptor */
PLXPC_PLXS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Pulse Oximeter service.
* Parameter pHdlList must point to an array of length \ref PLXPC_PLXS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpcPlxsDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref PLXPC_PLXS_HDL_LIST_LEN.
* If the ATT handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t PlxpcPlxsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Send a command to the pulse oximeter service record access control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param opcode Command opcode.
* \param oper Command operator or 0 if no operator required.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpcPlxsRacpSend(dmConnId_t connId, uint16_t handle, uint8_t opcode, uint8_t oper);
/*! \} */ /* PULSE_OXIMETER_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* PLXPC_API_H */
@@ -0,0 +1,546 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Pulse Oximeter profile collector.
*
* 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 "util/bstream.h"
#include "svc_ch.h"
#include "app_api.h"
#include "plxpc_api.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/* Length of response data contained in a received RACP message */
#define PLXPC_PLX_RACP_RSP_LEN 4
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Pulse Oximeter service characteristics for discovery
*/
/*! Pulse Oximeter spot check measurement */
static const attcDiscChar_t plxpcPlxsPlxsc =
{
attPlxscmChUuid,
0
};
/*! Pulse Oximeter spot check measurement CCC descriptor */
static const attcDiscChar_t plxpcPlxsPlxscCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! Pulse Oximeter continuous measurement */
static const attcDiscChar_t plxpcPlxsPlxc =
{
attPlxcmChUuid,
0
};
/*! Pulse Oximeter continuous measurement CCC descriptor */
static const attcDiscChar_t plxpcPlxsPlxcCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! Pulse Oximeter features */
static const attcDiscChar_t plxpcPlxsPlxf =
{
attPlxfChUuid,
ATTC_SET_REQUIRED
};
/*! Record access control point */
static const attcDiscChar_t plxpcPlxsRacp =
{
attRacpChUuid,
0
};
/*! Record access control point CCC descriptor */
static const attcDiscChar_t plxpcPlxsRacpCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *plxpcPlxsDiscCharList[] =
{
&plxpcPlxsPlxsc, /*! Pulse Oximeter measurement */
&plxpcPlxsPlxscCcc, /*! Pulse Oximeter measurement CCC descriptor */
&plxpcPlxsPlxc, /*! Pulse Oximeter measurement context */
&plxpcPlxsPlxcCcc, /*! Pulse Oximeter measurement context CCC descriptor */
&plxpcPlxsPlxf, /*! Pulse Oximeter feature */
&plxpcPlxsRacp, /*! Record access control point */
&plxpcPlxsRacpCcc /*! Record access control point CCC descriptor */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(PLXPC_PLXS_HDL_LIST_LEN == ((sizeof(plxpcPlxsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Parse a pulse oximeter spot check measurement.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void plxpcPlxsParsePlxsc(uint8_t *pValue, uint16_t len)
{
uint8_t flags;
uint16_t year;
uint8_t month, day, hour, min, sec;
int16_t mantissa;
int8_t exponent;
uint16_t spo2;
uint16_t pulse;
uint32_t sensorStatus;
uint16_t measurementStatus;
uint16_t pulseAmpIndex;
uint16_t minLen = CH_PLX_FLAGS_LEN + CH_PLX_SPO2_LEN + CH_PLX_PULSE_LEN;
/* Suppress unused variable compile warning */
(void)measurementStatus; (void)sensorStatus; (void)exponent; (void)mantissa;
(void)year; (void)month; (void)day; (void)hour; (void)min; (void)sec;
if (len > 0)
{
/* get flags */
BSTREAM_TO_UINT8(flags, pValue);
/* determine expected minimum length based on flags */
if (flags & CH_PLXSC_FLAG_TIMESTAMP)
{
minLen += CH_PLXSC_TIMESTAMP_LEN;
}
if (flags & CH_PLXSC_FLAG_MEASUREMENT_STATUS)
{
minLen += CH_PLX_MEASUREMENT_STATUS_LEN;
}
if (flags & CH_PLXSC_FLAG_SENSOR_STATUS)
{
minLen += CH_PLX_SENSOR_STATUS_LEN;
}
if (flags & CH_PLXSC_FLAG_PULSE_AMP_INDX)
{
minLen += CH_PLX_PULSE_AMP_INDX_LEN;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Pulse Oximeter meas len:%d minLen:%d", len, minLen);
return;
}
/* get SpO2 */
BSTREAM_TO_UINT16(spo2, pValue);
UINT16_TO_SFLT(mantissa, exponent, spo2);
APP_TRACE_INFO2(" Pulse Oximeter SpO2: %de%d", mantissa, exponent);
/* get pulse */
BSTREAM_TO_UINT16(pulse, pValue);
UINT16_TO_SFLT(mantissa, exponent, pulse);
APP_TRACE_INFO2(" Pulse Oximeter Pulse: %de%d", mantissa, exponent);
/* timestamp */
if (flags & CH_PLXSC_FLAG_TIMESTAMP)
{
/* base time */
BSTREAM_TO_UINT16(year, pValue);
BSTREAM_TO_UINT8(month, pValue);
BSTREAM_TO_UINT8(day, pValue);
BSTREAM_TO_UINT8(hour, pValue);
BSTREAM_TO_UINT8(min, pValue);
BSTREAM_TO_UINT8(sec, pValue);
APP_TRACE_INFO3(" Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3(" Time: %02d:%02d:%02d", hour, min, sec);
}
/* measurement status */
if (flags & CH_PLXSC_FLAG_MEASUREMENT_STATUS)
{
BSTREAM_TO_UINT16(measurementStatus, pValue);
APP_TRACE_INFO1(" Pulse Oximeter measurement status: 0x%04", measurementStatus);
}
/* sensor status */
if (flags & CH_PLXSC_FLAG_SENSOR_STATUS)
{
BSTREAM_TO_UINT24(sensorStatus, pValue);
APP_TRACE_INFO1(" Pulse Oximeter Sensor status: 0x%04x", sensorStatus);
}
/* Pulse amplitde index */
if (flags & CH_PLXSC_FLAG_PULSE_AMP_INDX)
{
BSTREAM_TO_UINT16(pulseAmpIndex, pValue);
UINT16_TO_SFLT(mantissa, exponent, pulseAmpIndex);
APP_TRACE_INFO2(" Pulse Oximeter Pulse Amplitude Index: %de%d", mantissa, exponent);
}
}
/*************************************************************************************************/
/*!
* \brief Parse a pulse oximeter continuous measurement.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void plxpcPlxsParsePlxc(uint8_t *pValue, uint16_t len)
{
uint8_t flags;
int16_t mantissa;
int8_t exponent;
uint16_t spo2;
uint16_t pulse;
uint32_t sensorStatus;
uint16_t measurementStatus;
uint16_t pulseAmpIndex;
uint16_t minLen = CH_PLX_FLAGS_LEN + CH_PLX_SPO2_LEN + CH_PLX_PULSE_LEN;
/* Suppress unused variable compile warning */
(void)measurementStatus; (void)sensorStatus; (void)exponent; (void)mantissa;
if (len > 0)
{
/* get flags */
BSTREAM_TO_UINT8(flags, pValue);
/* determine expected minimum length based on flags */
if (flags & CH_PLXC_FLAG_SPO2PR_FAST)
{
minLen += CH_PLXC_SPO2PR_FAST_LEN;
}
if (flags & CH_PLXC_FLAG_SPO2PR_SLOW)
{
minLen += CH_PLXC_SPO2PR_SLOW_LEN;
}
if (flags & CH_PLXC_FLAG_MEASUREMENT_STATUS)
{
minLen += CH_PLX_MEASUREMENT_STATUS_LEN;
}
if (flags & CH_PLXC_FLAG_SENSOR_STATUS)
{
minLen += CH_PLX_SENSOR_STATUS_LEN;
}
if (flags & CH_PLXC_FLAG_PULSE_AMP_INDX)
{
minLen += CH_PLX_PULSE_AMP_INDX_LEN;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Pulse Oximeter meas len:%d minLen:%d", len, minLen);
return;
}
/* get SpO2 */
BSTREAM_TO_UINT16(spo2, pValue);
UINT16_TO_SFLT(mantissa, exponent, spo2);
APP_TRACE_INFO2(" Pulse Oximeter SpO2: %de%d", mantissa, exponent);
/* get pulse */
BSTREAM_TO_UINT16(pulse, pValue);
UINT16_TO_SFLT(mantissa, exponent, pulse);
APP_TRACE_INFO2(" Pulse Oximeter Pulse: %de%d", mantissa, exponent);
/* SpO2PR-Fast */
if (flags & CH_PLXC_FLAG_SPO2PR_FAST)
{
/* get SpO2 */
BSTREAM_TO_UINT16(spo2, pValue);
UINT16_TO_SFLT(mantissa, exponent, spo2);
APP_TRACE_INFO2(" Pulse Oximeter SpO2-Fast: %de%d", mantissa, exponent);
/* get pulse */
BSTREAM_TO_UINT16(pulse, pValue);
UINT16_TO_SFLT(mantissa, exponent, pulse);
APP_TRACE_INFO2(" Pulse Oximeter Pulse-Fast: %de%d", mantissa, exponent);
}
/* SpO2PR-Slow */
if (flags & CH_PLXC_FLAG_SPO2PR_SLOW)
{
/* get SpO2 */
BSTREAM_TO_UINT16(spo2, pValue);
UINT16_TO_SFLT(mantissa, exponent, spo2);
APP_TRACE_INFO2(" Pulse Oximeter SpO2-Slow: %de%d", mantissa, exponent);
/* get pulse */
BSTREAM_TO_UINT16(pulse, pValue);
UINT16_TO_SFLT(mantissa, exponent, pulse);
APP_TRACE_INFO2(" Pulse Oximeter Pulse-Slow: %de%d", mantissa, exponent);
}
/* measurement status */
if (flags & CH_PLXC_FLAG_MEASUREMENT_STATUS)
{
BSTREAM_TO_UINT16(measurementStatus, pValue);
APP_TRACE_INFO1(" Pulse Oximeter measurement status: 0x%04", measurementStatus);
}
/* sensor status */
if (flags & CH_PLXC_FLAG_SENSOR_STATUS)
{
BSTREAM_TO_UINT24(sensorStatus, pValue);
APP_TRACE_INFO1(" Pulse Oximeter Sensor status: 0x%04x", sensorStatus);
}
/* Pulse amplitde index */
if (flags & CH_PLXC_FLAG_PULSE_AMP_INDX)
{
BSTREAM_TO_UINT16(pulseAmpIndex, pValue);
UINT16_TO_SFLT(mantissa, exponent, pulseAmpIndex);
APP_TRACE_INFO2(" Pulse Oximeter Pulse Amplitude Index: %de%d", mantissa, exponent);
}
}
/*************************************************************************************************/
/*!
* \brief Process a message received from the sensor on the record access control point.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void plxpcPlxsProcRacp(uint8_t *pValue, uint16_t len)
{
uint8_t opcode;
uint16_t numRecords;
uint16_t reqOpcode;
uint16_t status;
/* Suppress unused variable compile warning */
(void)status; (void)reqOpcode; (void)numRecords;
/* verify length */
if (len != PLXPC_PLX_RACP_RSP_LEN)
{
APP_TRACE_INFO1("Unexpected RACP message length: %d", len);
return;
}
/* parse message */
BSTREAM_TO_UINT8(opcode, pValue);
/* Operator unused */
pValue++;
if (opcode == CH_RACP_OPCODE_NUM_RSP)
{
BSTREAM_TO_UINT16(numRecords, pValue);
APP_TRACE_INFO1("Number of records: %d", numRecords);
}
else if (opcode == CH_RACP_OPCODE_RSP)
{
BSTREAM_TO_UINT8(reqOpcode, pValue);
BSTREAM_TO_UINT8(status, pValue);
APP_TRACE_INFO2("Response opcode: %d status: %d", reqOpcode, status);
}
}
/*************************************************************************************************/
/*!
* \brief Parse a pulse oximeter features.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void plxpcPlxParsePlxf(uint8_t *pValue, uint16_t len)
{
int16_t features;
int16_t measStatusSupport;
int16_t sensorStatusSupport;
uint16_t minLen = CH_PLXF_MIN_FEATURES_LEN;
/* Suppress unused variable compile warning */
(void)measStatusSupport; (void)sensorStatusSupport;
if (len >= minLen)
{
/* get flags */
BSTREAM_TO_UINT16(features, pValue);
/* determine expected minimum length based on features */
if (features & CH_PLF_FLAG_MEAS_STATUS_SUP)
{
minLen += CH_PLXF_SENSOR_SUPPORT_LEN;
}
if (features & CH_PLF_FLAG_SENSOR_STATUS_SUP)
{
minLen += CH_PLXF_MEASUREMENT_SUPPORT_LEN;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Pulse Oximeter feature len:%d minLen:%d", len, minLen);
return;
}
/* SpO2PR-Fast */
if (features & CH_PLF_FLAG_MEAS_STATUS_SUP)
{
/* get Measurement Status Support */
BSTREAM_TO_UINT16(measStatusSupport, pValue);
APP_TRACE_INFO1(" Pulse Oximeter measurement status sypported: 0x%04", measStatusSupport);
}
/* SpO2PR-Slow */
if (features & CH_PLF_FLAG_SENSOR_STATUS_SUP)
{
/* get Device and Sensor Status Support */
BSTREAM_TO_UINT16(sensorStatusSupport, pValue);
APP_TRACE_INFO1(" Pulse Oximeter sensor status supported: 0x%04", sensorStatusSupport);
}
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Pulse Oximeter service. Parameter
* pHdlList must point to an array of length PLXPC_PLXS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpcPlxsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attPlxsSvcUuid,
PLXPC_PLXS_HDL_LIST_LEN, (attcDiscChar_t **) plxpcPlxsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length PLXPC_PLXS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t PlxpcPlxsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* spot check measurement */
if (pMsg->handle == pHdlList[PLXPC_PLXS_PLXSC_HDL_IDX])
{
APP_TRACE_INFO0("Pulse Oximeter spot check measurement.");
/* parse value */
plxpcPlxsParsePlxsc(pMsg->pValue, pMsg->valueLen);
}
/* continuous measurement */
else if (pMsg->handle == pHdlList[PLXPC_PLXS_PLXC_HDL_IDX])
{
APP_TRACE_INFO0("Pulse Oximeter continuous measurement");
/* parse value */
plxpcPlxsParsePlxc(pMsg->pValue, pMsg->valueLen);
}
/* record access control point */
else if (pMsg->handle == pHdlList[PLXPC_PLXS_RACP_HDL_IDX])
{
plxpcPlxsProcRacp(pMsg->pValue, pMsg->valueLen);
}
/* pulse oximeter feature */
else if (pMsg->handle == pHdlList[PLXPC_PLXS_PLXF_HDL_IDX])
{
APP_TRACE_INFO0("Pulse Oximeter features");
/* parse value */
plxpcPlxParsePlxf(pMsg->pValue, pMsg->valueLen);
}
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Send a command to the pulse oximeter service record access control point.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param opcode Command opcode.
* \param oper Command operator or 0 if no operator required.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpcPlxsRacpSend(dmConnId_t connId, uint16_t handle, uint8_t opcode, uint8_t oper)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t *p = buf;
if (handle != ATT_HANDLE_NONE)
{
/* build RACP command */
UINT8_TO_BSTREAM(p, opcode);
UINT8_TO_BSTREAM(p, oper);
AttcWriteReq(connId, handle, (uint16_t) (p - buf), buf);
}
}
@@ -0,0 +1,185 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Pulse Oximeter profile sensor.
*
* 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 PLXPS_API_H
#define PLXPS_API_H
#include "app_hw.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup PULSE_OXIMETER_PROFILE
* \{ */
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Configurable parameters */
typedef struct
{
wsfTimerTicks_t period; /*!< \brief Continuous Measurement timer expiration period in ms */
} plxpsCfg_t;
/*! \brief Pulse Oximeter continuous measurement structure */
typedef appPlxCm_t plxpCm_t;
/*! \brief Pulse Oximeter spot check measurement structure */
typedef appPlxScm_t plxpScm_t;
/*************************************************************************************************/
/*!
* \brief Initialize the Pulse Oximeter profile sensor.
*
* \param handlerId DM connection identifier.
* \param pCfg Configuration parameters.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsInit(wsfHandlerId_t handlerId, plxpsCfg_t *pCfg);
/*************************************************************************************************/
/*!
* \brief This function is called by the application when a message that requires
* processing by the pulse oximeter profile sensor is received.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsProcMsg(wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsBtn(dmConnId_t connId, uint8_t btn);
/*************************************************************************************************/
/*!
* \brief ATTS write callback for pulse oximeter service.
*
* \param connId DM connection identifier.
* \param handle ATT handle.
* \param operation ATT operation.
* \param offset Write offset.
* \param len Write length.
* \param pValue Value to write.
* \param pAttr Attribute to write.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t PlxpsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr);
/*************************************************************************************************/
/*!
* \brief Set the supported features of the pulse oximeter sensor.
*
* \param feature Feature bitmask.
* \param measStatus Measurement status.
* \param sensorStatus Sensor status.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsSetFeature(uint16_t feature, uint16_t measStatus, uint32_t sensorStatus);
/*************************************************************************************************/
/*!
* \brief Set the CCCD index used by the application for pulse oximeter service characteristics.
*
* \param plxscCccIdx Pulse Oximeter spot check CCCD index.
* \param plxcCccIdx Pulse Oximeter continuous CCCD index.
* \param racpCccIdx Record access control point CCCD index.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsSetCccIdx(uint8_t plxscCccIdx, uint8_t plxcCccIdx, uint8_t racpCccIdx);
/*************************************************************************************************/
/*!
* \brief Start periodic pulse oximeter measurement. This function starts a timer to perform
* periodic measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param plxmCccIdx Index of pulse oximeter CCC descriptor in CCC descriptor handle table.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsMeasStart(dmConnId_t connId, uint8_t timerEvt, uint8_t plxmCccIdx);
/*************************************************************************************************/
/*!
* \brief Stop periodic pulse oximeter measurement.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsMeasStop(void);
/*************************************************************************************************/
/*!
* \brief Send a spot check measurement indication.
*
* \param connId Connection ID.
* \param pMeas Pointer to pulse oximeter spot check measurement.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsSendSpotCheckMeas(dmConnId_t connId, plxpScm_t *pMeas);
/*************************************************************************************************/
/*!
* \brief Send a Continuous measurement notification.
*
* \param connId Connection ID.
* \param pMeas Pointer to Pulse Oximiter continuous measurement.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsSendContinuousMeas(dmConnId_t connId, plxpCm_t *pMeas);
/*! \} */ /* PULSE_OXIMETER_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* PLXPS_API_H */
@@ -0,0 +1,261 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Pulse Oximeter profile example record database and access functions.
*
* 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 "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_gls.h"
#include "app_api.h"
#include "plxps_api.h"
#include "plxps_main.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Number of records in example database */
#define PLXPS_DB_NUM_RECORDS 3
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! Control block */
static struct
{
uint8_t numRec;
} plxpsDbCb;
/*! Example record database */
static const plxpsRec_t plxpsDb[PLXPS_DB_NUM_RECORDS] =
{
/* record 1 */
{
/* spotCheck */
{
0, /* Flags */
SFLT_TO_UINT16(96, 1), /* Pulse Oximeter Spo2 (SFLOAT) */
SFLT_TO_UINT16(60, 1), /* Pulse Oximeter Pulse (SFLOAT) */
{2012, 6, 15, 20, 52, 36}, /* timestamp */
0, /* Measurement status */
0, /* Sensor status */
SFLT_TO_UINT16(96, 1), /* Pulse Amplitude Index */
}
},
/* record 2 */
{
/* spotCheck */
{
0, /* Flags */
SFLT_TO_UINT16(96, 1), /* Pulse Oximeter Spo2 (SFLOAT) */
SFLT_TO_UINT16(60, 1), /* Pulse Oximeter Pulse (SFLOAT) */
{2012, 6, 15, 20, 52, 36}, /* timestamp */
0, /* Measurement status */
0, /* Sensor status */
SFLT_TO_UINT16(96, 1), /* Pulse Amplitude Index */
}
},
/* record 3 */
{
/* spotCheck */
{
0, /* Flags */
SFLT_TO_UINT16(96, 1), /* Pulse Oximeter Spo2 (SFLOAT) */
SFLT_TO_UINT16(60, 1), /* Pulse Oximeter Pulse (SFLOAT) */
{2012, 6, 15, 20, 52, 36}, /* timestamp */
0, /* Measurement status */
0, /* Sensor status */
SFLT_TO_UINT16(96, 1), /* Pulse Amplitude Index */
}
}
};
/*************************************************************************************************/
/*!
* \brief Get the next database record after the given current record.
*
* \param pCurrRec Pointer to current record.
*
* \return Pointer to record or NULL if no record.
*/
/*************************************************************************************************/
static const plxpsRec_t *plxpsDbGetEnd()
{
return plxpsDb + plxpsDbCb.numRec;
}
/*************************************************************************************************/
/*!
* \brief Get the next database record after the given current record.
*
* \param pCurrRec Pointer to current record.
*
* \return Pointer to record or NULL if no record.
*/
/*************************************************************************************************/
static plxpsRec_t *plxpsDbGetNext(plxpsRec_t *pCurrRec)
{
if (plxpsDbCb.numRec == 0 || pCurrRec == plxpsDbGetEnd())
{
plxpsDbCb.numRec = 0;
return NULL;
}
else if (pCurrRec == NULL)
{
return (plxpsRec_t *) plxpsDb;
}
else
{
return (pCurrRec + 1);
}
}
/*************************************************************************************************/
/*!
* \brief Get the next record filtered for "all".
*
* \param pCurrRec Pointer to current record.
* \param pRec Return pointer to next record, if found.
*
* \return CH_RACP_RSP_SUCCESS if a record is found, otherwise an error status is returned.
*/
/*************************************************************************************************/
static uint8_t plxpsDbOpAll(plxpsRec_t *pCurrRec, plxpsRec_t **pRec)
{
*pRec = plxpsDbGetNext(pCurrRec);
return (*pRec != NULL) ? CH_RACP_RSP_SUCCESS : CH_RACP_RSP_NO_RECORDS;
}
/*************************************************************************************************/
/*!
* \brief Initialize the pulse oximeter record database.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsDbInit(void)
{
plxpsDbCb.numRec = PLXPS_DB_NUM_RECORDS;
}
/*************************************************************************************************/
/*!
* \brief Get the next record that matches the given filter parameters that follows
* the given current record.
*
* \param oper Operator.
* \param pCurrRec Pointer to current record.
* \param pRec Return pointer to next record, if found.
*
* \return CH_RACP_RSP_SUCCESS if a record is found, otherwise an error status is returned.
*/
/*************************************************************************************************/
uint8_t plxpsDbGetNextRecord(uint8_t oper, plxpsRec_t *pCurrRec, plxpsRec_t **pRec)
{
uint8_t status;
if (oper == CH_RACP_OPERATOR_ALL)
{
status = plxpsDbOpAll(pCurrRec, pRec);
}
else
{
status = CH_RACP_RSP_INV_OPERATOR;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Delete records that match the given filter parameters.
*
* \param oper Operator.
*
* \return CH_RACP_RSP_SUCCESS if records deleted, otherwise an error status is returned.
*/
/*************************************************************************************************/
uint8_t plxpsDbDeleteRecords(uint8_t oper)
{
/* only 'all records' is supported */
if (oper == CH_RACP_OPERATOR_ALL)
{
plxpsDbCb.numRec = 0;
return CH_RACP_RSP_SUCCESS;
}
else
{
return CH_RACP_RSP_INV_OPERATOR;
}
}
/*************************************************************************************************/
/*!
* \brief Get the number of records matching the filter parameters.
*
* \param oper Operator.
* \param pNumRec Returns number of records which match filter parameters.
*
* \return RACP status.
*/
/*************************************************************************************************/
uint8_t plxpsDbGetNumRecords(uint8_t oper, uint8_t *pNumRec)
{
plxpsRec_t *pCurrRec = NULL;
uint8_t status;
*pNumRec = 0;
while ((status = plxpsDbGetNextRecord(oper, pCurrRec, &pCurrRec)) == CH_RACP_RSP_SUCCESS)
{
(*pNumRec)++;
}
if (status == CH_RACP_RSP_NO_RECORDS)
{
status = CH_RACP_RSP_SUCCESS;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Generate a new record.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsDbGenerateRecord(void)
{
if (plxpsDbCb.numRec < PLXPS_DB_NUM_RECORDS)
{
plxpsDbCb.numRec++;
}
}
@@ -0,0 +1,785 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Pulse Oximeter profile sensor.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_plxs.h"
#include "app_api.h"
#include "app_ui.h"
#include "plxps_api.h"
#include "plxps_main.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
wsfTimer_t measTimer; /* continuous measurement timer */
plxpsCfg_t *pCfg; /* configurable parameters */
plxpsRec_t *pCurrRec; /* Pointer to current measurement record */
plxpCm_t plxpsCm; /* Continuous measurement data */
bool_t inProgress; /* TRUE if RACP procedure in progress */
bool_t cmTxPending; /* TRUE if Continuous Measurement tx pending */
bool_t txReady; /* TRUE if ready to send next notification or indication */
bool_t aborting; /* TRUE if abort procedure in progress */
uint8_t plxscCccIdx; /* Pulse Oximeter spot check measurement CCCD index */
uint8_t plxcCccIdx; /* Pulse Oximeter continuous measurement CCCD index */
uint8_t racpCccIdx; /* Record access control point CCCD index */
} plxpsCb;
/*************************************************************************************************/
/*!
* \brief Build a spot check measurement characteristic.
*
* \param pBuf Pointer to buffer to hold the built characteristic.
* \param pScm Pulse Oximeter spot check measurement values.
*
* \return Length of pBuf in bytes.
*/
/*************************************************************************************************/
static uint8_t plxpsBuildScm(uint8_t *pBuf, plxpScm_t *pScm)
{
uint8_t *p = pBuf;
uint8_t flags = pScm->flags;
/* flags */
UINT8_TO_BSTREAM(p, flags);
/* Manditory SpO2 */
UINT16_TO_BSTREAM(p, pScm->spo2);
/* Manditory Pulse Rate */
UINT16_TO_BSTREAM(p, pScm->pulseRate);
/* Timestamp */
if (flags & CH_PLXSC_FLAG_TIMESTAMP)
{
UINT16_TO_BSTREAM(p, pScm->timestamp.year);
UINT8_TO_BSTREAM(p, pScm->timestamp.month);
UINT8_TO_BSTREAM(p, pScm->timestamp.day);
UINT8_TO_BSTREAM(p, pScm->timestamp.hour);
UINT8_TO_BSTREAM(p, pScm->timestamp.min);
UINT8_TO_BSTREAM(p, pScm->timestamp.sec);
}
/* Measurement Status */
if (flags & CH_PLXSC_FLAG_MEASUREMENT_STATUS)
{
UINT16_TO_BSTREAM(p, pScm->measStatus);
}
/* Device and Sensor Status */
if (flags & CH_PLXSC_FLAG_SENSOR_STATUS)
{
UINT24_TO_BSTREAM(p, pScm->sensorStatus);
}
/* Pulse Amplitude Index */
if (flags & CH_PLXSC_FLAG_PULSE_AMP_INDX)
{
UINT16_TO_BSTREAM(p, pScm->pulseAmpIndex);
}
/* return length */
return (uint8_t) (p - pBuf);
}
/*************************************************************************************************/
/*!
* \brief Build a Continuous measurement characteristic.
*
* \param pBuf Pointer to buffer to hold the built characteristic.
* \param pCm Pulse Oximeter continuous measurement values.
*
* \return Length of pBuf in bytes.
*/
/*************************************************************************************************/
static uint8_t plxpsBuildCm(uint8_t *pBuf, plxpCm_t *pCm)
{
uint8_t *p = pBuf;
uint8_t flags = pCm->flags;
/* flags */
UINT8_TO_BSTREAM(p, flags);
/* Manditory SpO2 */
UINT16_TO_BSTREAM(p, pCm->spo2);
/* Manditory Pulse Rate */
UINT16_TO_BSTREAM(p, pCm->pulseRate);
/* SpO2PR Fast */
if (flags & CH_PLXC_FLAG_SPO2PR_FAST)
{
UINT16_TO_BSTREAM(p, pCm->spo2Fast);
UINT16_TO_BSTREAM(p, pCm->pulseRateFast);
}
/* SpO2PR Slow */
if (flags & CH_PLXC_FLAG_SPO2PR_SLOW)
{
UINT16_TO_BSTREAM(p, pCm->spo2Slow);
UINT16_TO_BSTREAM(p, pCm->pulseRateSlow);
}
/* Measurement Status */
if (flags & CH_PLXC_FLAG_MEASUREMENT_STATUS)
{
UINT16_TO_BSTREAM(p, pCm->measStatus);
}
/* Device and Sensor Status */
if (flags & CH_PLXC_FLAG_SENSOR_STATUS)
{
UINT24_TO_BSTREAM(p, pCm->sensorStatus);
}
/* Pulse Amplitude Index */
if (flags & CH_PLXC_FLAG_PULSE_AMP_INDX)
{
UINT16_TO_BSTREAM(p, pCm->pulseAmpIndex);
}
/* return length */
return (uint8_t) (p - pBuf);
}
/*************************************************************************************************/
/*!
* \brief Send a Continuous measurement notification.
*
* \param connId Connection ID.
* \param pMeas Pointer to Pulse Oximiter continuous measurement.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsSendContinuousMeas(dmConnId_t connId, plxpCm_t *pMeas)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* build continuous measurement characteristic */
len = plxpsBuildCm(buf, pMeas);
/* send notification */
AttsHandleValueNtf(connId, PLXS_CONTINUOUS_HDL, len, buf);
plxpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Send a spot check measurement indication.
*
* \param connId Connection ID.
* \param pMeas Pointer to pulse oximeter spot check measurement.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsSendSpotCheckMeas(dmConnId_t connId, plxpScm_t *pMeas)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* build spot check measurement characteristic */
len = plxpsBuildScm(buf, pMeas);
/* send indication */
AttsHandleValueInd(connId, PLXS_SPOT_CHECK_HDL, len, buf);
plxpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Send a RACP response indication.
*
* \param connId Connection ID.
* \param opcode RACP opcode.
* \param status RACP status.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsRacpSendRsp(dmConnId_t connId, uint8_t opcode, uint8_t status)
{
uint8_t buf[PLXPS_RACP_RSP_LEN];
/* build response */
buf[0] = CH_RACP_OPCODE_RSP;
buf[1] = CH_RACP_OPERATOR_NULL;
buf[2] = opcode;
buf[3] = status;
/* send indication */
AttsHandleValueInd(connId, PLXS_RECORD_ACCESS_HDL, PLXPS_RACP_RSP_LEN, buf);
plxpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Send a RACP number of records response indication.
*
* \param connId Connection ID.
* \param numRec Number of records.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsRacpSendNumRecRsp(dmConnId_t connId, uint16_t numRec)
{
uint8_t buf[PLXPS_RACP_NUM_REC_RSP_LEN];
/* build response */
buf[0] = CH_RACP_OPCODE_NUM_RSP;
buf[1] = CH_RACP_OPERATOR_NULL;
buf[2] = UINT16_TO_BYTE0(numRec);
buf[3] = UINT16_TO_BYTE1(numRec);
/* send indication */
AttsHandleValueInd(connId, PLXS_RECORD_ACCESS_HDL, PLXPS_RACP_RSP_LEN, buf);
plxpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Handle connection open.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void plxpsConnOpen(dmEvt_t *pMsg)
{
/* initialize */
plxpsCb.pCurrRec = NULL;
plxpsCb.aborting = FALSE;
plxpsCb.inProgress = FALSE;
plxpsCb.txReady = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Handle connection close.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void plxpsConnClose(dmEvt_t *pMsg)
{
plxpsCb.pCurrRec = NULL;
plxpsCb.aborting = FALSE;
PlxpsMeasStop();
}
/*************************************************************************************************/
/*!
* \brief Handle an ATT handle value confirm.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void plxpsHandleValueCnf(attEvt_t *pMsg)
{
dmConnId_t connId = (dmConnId_t) pMsg->hdr.param;
plxpsCb.txReady = TRUE;
/* send continuous measurement if necessary */
if (plxpsCb.cmTxPending == TRUE)
{
plxpsSendContinuousMeas((dmConnId_t) plxpsCb.measTimer.msg.param, &plxpsCb.plxpsCm);
plxpsCb.txReady = FALSE;
plxpsCb.cmTxPending = FALSE;
return;
}
/* if aborting finish that up */
if (plxpsCb.aborting)
{
plxpsCb.aborting = FALSE;
plxpsRacpSendRsp(connId, CH_RACP_OPCODE_ABORT, CH_RACP_RSP_SUCCESS);
}
/* if this is for RACP indication */
if (pMsg->handle == PLXS_RECORD_ACCESS_HDL)
{
/* procedure no longer in progress */
plxpsCb.inProgress = FALSE;
}
/* if this is for measurement or continuous notification */
else if (pMsg->handle == PLXS_SPOT_CHECK_HDL || pMsg->handle == PLXS_CONTINUOUS_HDL)
{
if (plxpsCb.pCurrRec != NULL)
{
/* if there is another record */
if (plxpsDbGetNextRecord(CH_RACP_OPERATOR_ALL, plxpsCb.pCurrRec, &plxpsCb.pCurrRec) == CH_RACP_RSP_SUCCESS)
{
/* send measurement */
plxpsSendSpotCheckMeas(connId, (plxpScm_t*) plxpsCb.pCurrRec);
}
/* else all records sent; send RACP response */
else
{
plxpsRacpSendRsp(connId, CH_RACP_OPCODE_REPORT, CH_RACP_RSP_SUCCESS);
}
}
}
}
/*************************************************************************************************/
/*!
* \brief Handle a RACP report stored records operation.
*
* \param connId Connection ID.
* \param oper Operator.
*
* \return None.
*/
/*************************************************************************************************/
static void plxpsRacpReport(dmConnId_t connId, uint8_t oper)
{
uint8_t status;
/* if record found */
if ((status = plxpsDbGetNextRecord(oper, NULL, &plxpsCb.pCurrRec)) == CH_RACP_RSP_SUCCESS)
{
/* send spot check measurement */
plxpsSendSpotCheckMeas(connId, (plxpScm_t *) plxpsCb.pCurrRec);
}
/* if not successful send response */
else
{
plxpsRacpSendRsp(connId, CH_RACP_OPCODE_REPORT, status);
}
}
/*************************************************************************************************/
/*!
* \brief Handle a RACP delete records operation.
*
* \param connId Connection ID.
* \param oper Operator.
*
* \return None.
*/
/*************************************************************************************************/
static void plxpsRacpDelete(dmConnId_t connId, uint8_t oper)
{
uint8_t status;
/* delete records */
status = plxpsDbDeleteRecords(oper);
/* send response */
plxpsRacpSendRsp(connId, CH_RACP_OPCODE_DELETE, status);
}
/*************************************************************************************************/
/*!
* \brief Handle a RACP abort operation.
*
* \param connId Connection ID.
*
* \return None.
*/
/*************************************************************************************************/
static void plxpsRacpAbort(dmConnId_t connId)
{
/* if operation in progress */
if (plxpsCb.inProgress)
{
/* abort operation and clean up */
plxpsCb.pCurrRec = NULL;
}
/* send response */
if (plxpsCb.txReady)
{
plxpsRacpSendRsp(connId, CH_RACP_OPCODE_ABORT, CH_RACP_RSP_SUCCESS);
}
else
{
plxpsCb.aborting = TRUE;
}
}
/*************************************************************************************************/
/*!
* \brief Handle a RACP report number of stored records operation.
*
* \param connId Connection ID.
* \param oper Operator.
* \param pOperand Operand data.
*
* \return None.
*/
/*************************************************************************************************/
static void plxpsRacpReportNum(dmConnId_t connId, uint8_t oper)
{
uint8_t status;
uint8_t numRec;
/* get number of records */
status = plxpsDbGetNumRecords(oper, &numRec);
if (status == CH_RACP_RSP_SUCCESS)
{
/* send response */
plxpsRacpSendNumRecRsp(connId, numRec);
}
else
{
plxpsRacpSendRsp(connId, CH_RACP_OPCODE_REPORT_NUM, status);
}
}
/*************************************************************************************************/
/*!
* \brief Initialize the Pulse Oximeter profile sensor.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsInit(wsfHandlerId_t handlerId, plxpsCfg_t *pCfg)
{
plxpsCb.measTimer.handlerId = handlerId;
plxpsCb.pCfg = pCfg;
plxpsDbInit();
}
/*************************************************************************************************/
/*!
* \brief This function is called by the application when the periodic measurement
* timer expires.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsMeasTimerExp(wsfMsgHdr_t *pMsg)
{
/* read pulse oximeter measurement data */
AppHwPlxcmRead(&plxpsCb.plxpsCm);
plxpsCb.cmTxPending = TRUE;
/* if ready to send measurements */
if (plxpsCb.txReady)
{
plxpsSendContinuousMeas((dmConnId_t) plxpsCb.measTimer.msg.param, &plxpsCb.plxpsCm);
plxpsCb.txReady = FALSE;
plxpsCb.cmTxPending = FALSE;
}
/* restart timer */
WsfTimerStartMs(&plxpsCb.measTimer, plxpsCb.pCfg->period);
}
/*************************************************************************************************/
/*!
* \brief This function is called by the application when a message that requires
* processing by the pulse oximeter profile sensor is received.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsProcMsg(wsfMsgHdr_t *pMsg)
{
if (pMsg->event == plxpsCb.measTimer.msg.event)
{
plxpsMeasTimerExp(pMsg);
}
else
{
switch(pMsg->event)
{
case DM_CONN_OPEN_IND:
plxpsConnOpen((dmEvt_t *) pMsg);
break;
case DM_CONN_CLOSE_IND:
plxpsConnClose((dmEvt_t *) pMsg);
break;
case ATTS_HANDLE_VALUE_CNF:
plxpsHandleValueCnf((attEvt_t *) pMsg);
break;
default:
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsBtn(dmConnId_t connId, uint8_t btn)
{
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_2_LONG:
/* generate a new record */
plxpsDbGenerateRecord();
break;
default:
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_2_LONG:
/* generate a new record */
plxpsDbGenerateRecord();
break;
case APP_UI_BTN_2_EX_LONG:
/* delete all records */
plxpsDbDeleteRecords(CH_RACP_OPERATOR_ALL);
break;
default:
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for pulse oximeter service record access control point. Use this
* function as a parameter to SvcPlxCbackRegister().
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t PlxpsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
uint8_t opcode;
uint8_t oprator;
/* sanity check on length */
if (len < PLXPS_RACP_MIN_WRITE_LEN)
{
return ATT_ERR_LENGTH;
}
/* if control point not configured for indication */
if (!AttsCccEnabled(connId, plxpsCb.racpCccIdx))
{
return ATT_ERR_CCCD;
}
/* parse opcode and operator and adjust remaining parameter length */
BSTREAM_TO_UINT8(opcode, pValue);
BSTREAM_TO_UINT8(oprator, pValue);
len -= 2;
/* handle a procedure in progress */
if (opcode != CH_RACP_OPCODE_ABORT && plxpsCb.inProgress)
{
return ATT_ERR_IN_PROGRESS;
}
/* handle record request when notifications not enabled */
if (opcode == CH_RACP_OPCODE_REPORT && !AttsCccEnabled(connId, plxpsCb.plxscCccIdx))
{
return ATT_ERR_CCCD;
}
/* verify opcode */
if (opcode < CH_RACP_OPCODE_REPORT || opcode > CH_RACP_OPCODE_RSP)
{
plxpsRacpSendRsp(connId, opcode, CH_RACP_RSP_OPCODE_NOT_SUP);
}
/* verify operator */
if ((opcode != CH_RACP_OPCODE_ABORT && oprator != CH_RACP_OPERATOR_ALL) ||
(opcode == CH_RACP_OPCODE_ABORT && oprator != CH_RACP_OPERATOR_NULL))
{
if (oprator > CH_RACP_OPERATOR_LAST)
plxpsRacpSendRsp(connId, opcode, CH_RACP_RSP_OPERATOR_NOT_SUP);
else
plxpsRacpSendRsp(connId, opcode, CH_RACP_RSP_INV_OPERATOR);
return ATT_SUCCESS;
}
/* verify operands */
if (len > 0)
{
plxpsRacpSendRsp(connId, opcode, CH_RACP_RSP_INV_OPERAND);
return ATT_SUCCESS;
}
switch (opcode)
{
/* report records */
case CH_RACP_OPCODE_REPORT:
plxpsRacpReport(connId, oprator);
break;
/* delete records */
case CH_RACP_OPCODE_DELETE:
plxpsRacpDelete(connId, oprator);
break;
/* abort current operation */
case CH_RACP_OPCODE_ABORT:
plxpsRacpAbort(connId);
break;
/* report number of records */
case CH_RACP_OPCODE_REPORT_NUM:
plxpsRacpReportNum(connId, oprator);
break;
/* unsupported opcode */
default:
plxpsRacpSendRsp(connId, opcode, CH_RACP_RSP_OPCODE_NOT_SUP);
break;
}
/* procedure now in progress */
plxpsCb.inProgress = TRUE;
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Set the supported features of the pulse oximeter sensor.
*
* \param feature Feature bitmask.
* \param measStatus Measurement status.
* \param sensorStatus Sensor status.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsSetFeature(uint16_t feature, uint16_t measStatus, uint32_t sensorStatus)
{
uint8_t buf[CH_PLXF_MAX_FEATURES_LEN];
uint8_t *p = buf;
UINT16_TO_BSTREAM(p, feature);
if (feature & CH_PLF_FLAG_MEAS_STATUS_SUP)
{
UINT16_TO_BSTREAM(p, measStatus);
}
if (feature & CH_PLF_FLAG_SENSOR_STATUS_SUP)
{
UINT24_TO_BSTREAM(p, sensorStatus);
}
AttsSetAttr(PLXS_FEATURES_HDL, (uint16_t)(p - buf), buf);
}
/*************************************************************************************************/
/*!
* \brief Set the CCCD index used by the application for pulse oximeter service characteristics.
*
* \param plxscCccIdx Pulse Oximeter spot check CCCD index.
* \param plxcCccIdx Pulse Oximeter continuous CCCD index.
* \param racpCccIdx Record access control point CCCD index.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsSetCccIdx(uint8_t plxscCccIdx, uint8_t plxcCccIdx, uint8_t racpCccIdx)
{
plxpsCb.plxscCccIdx = plxscCccIdx;
plxpsCb.plxcCccIdx = plxcCccIdx;
plxpsCb.racpCccIdx = racpCccIdx;
}
/*************************************************************************************************/
/*!
* \brief Start periodic pulse oximeter measurement. This function starts a timer to perform
* periodic measurements.
*
* \param connId DM connection identifier.
* \param timerEvt WSF event designated by the application for the timer.
* \param plxmCccIdx Index of pulse oximeter CCC descriptor in CCC descriptor handle table.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsMeasStart(dmConnId_t connId, uint8_t timerEvt, uint8_t plxmCccIdx)
{
/* initialize control block */
plxpsCb.measTimer.msg.param = connId;
plxpsCb.measTimer.msg.event = timerEvt;
plxpsCb.measTimer.msg.status = plxmCccIdx;
/* start timer */
WsfTimerStartMs(&plxpsCb.measTimer, plxpsCb.pCfg->period);
}
/*************************************************************************************************/
/*!
* \brief Stop periodic pulse oximeter measurement.
*
* \return None.
*/
/*************************************************************************************************/
void PlxpsMeasStop(void)
{
WsfTimerStop(&plxpsCb.measTimer);
}
@@ -0,0 +1,126 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Pulse Oximeter profile sensor internal interfaces.
*
* 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 PLXPS_MAIN_H
#define PLXPS_MAIN_H
#include "app_hw.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup PULSE_OXIMETER_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Minimum RACP write length */
#define PLXPS_RACP_MIN_WRITE_LEN 2
/*! \brief RACP response length */
#define PLXPS_RACP_RSP_LEN 4
/*! \brief Pulse Oximeter RACP number of stored records response length */
#define PLXPS_RACP_NUM_REC_RSP_LEN 4
/*! \brief RACP operand maximum length */
#define PLXPS_OPERAND_MAX ((CH_RACP_GLS_FILTER_TIME_LEN * 2) + 1)
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Pulse Oximeter measurement record */
typedef struct
{
plxpScm_t spotCheck; /*!< \brief Pulse Oximeter spot check measurement */
} plxpsRec_t;
/*************************************************************************************************/
/*!
* \brief Initialize the pulse oximeter record database.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsDbInit(void);
/*************************************************************************************************/
/*!
* \brief Get the next record that matches the given filter parameters that follows
* the given current record.
*
* \param oper Operator.
* \param pCurrRec Pointer to current record.
* \param pRec Return pointer to next record, if found.
*
* \return \ref CH_RACP_RSP_SUCCESS if a record is found, otherwise an error status is returned.
*/
/*************************************************************************************************/
uint8_t plxpsDbGetNextRecord(uint8_t oper, plxpsRec_t *pCurrRec, plxpsRec_t **pRec);
/*************************************************************************************************/
/*!
* \brief Delete records that match the given filter parameters.
*
* \param oper Operator.
*
* \return \ref CH_RACP_RSP_SUCCESS if records deleted, otherwise an error status is returned.
*/
/*************************************************************************************************/
uint8_t plxpsDbDeleteRecords(uint8_t oper);
/*************************************************************************************************/
/*!
* \brief Get the number of records matching the filter parameters.
*
* \param oper Operator.
* \param pNumRec Returns number of records which match filter parameters.
*
* \return RACP status.
*/
/*************************************************************************************************/
uint8_t plxpsDbGetNumRecords(uint8_t oper, uint8_t *pNumRec);
/*************************************************************************************************/
/*!
* \brief Generate a new record.
*
* \return None.
*/
/*************************************************************************************************/
void plxpsDbGenerateRecord(void);
/*! \} */ /* PULSE_OXIMETER_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* PLXPS_MAIN_H */
@@ -0,0 +1,113 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Running Speed and Cadence Profile API.
*
* 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.
*/
/*************************************************************************************************/
#ifndef RSCP_API_H
#define RSCP_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup RUNNING_SPEED_AND_CADENCE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/** \name Running Speed and Cadence Measurement Parameter Types
*
*/
/**@{*/
#define RSCP_SM_PARAM_SPEED 0 /*!< \brief Instantaneous Speed */
#define RSCP_SM_PARAM_CADENCE 1 /*!< \brief Instantaneous Cadence */
#define RSCP_SM_PARAM_STRIDE_LENGTH 2 /*!< \brief Instantaneous Stride Length */
#define RSCP_SM_PARAM_TOTAL_DISTANCE 3 /*!< \brief Total Distance */
#define RSCP_SM_PARAM_STATUS 4 /*!< \brief Walking or Running Status (0: walking, 1: running) */
/**@}*/
/** \name Running Speed and Cadence Running Status Values
*
*/
/**@{*/
#define RSCP_SM_STATUS_WALKING 0 /*!< \brief Walking */
#define RSCP_SM_STATUS_RUNNING 1 /*!< \brief Running */
/**@}*/
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Set a running speed measurement parameter.
*
* \param type Parameter identifier
* \param value Measurement value.
*
* \return none
*/
/*************************************************************************************************/
void RscpsSetParameter(uint8_t type, uint32_t value);
/*************************************************************************************************/
/*!
* \brief Set the sensor location attribute.
*
* \param location Sensor Location.
*
* \return none
*/
/*************************************************************************************************/
void RscpsSetSensorLocation(uint8_t location);
/*************************************************************************************************/
/*!
* \brief Set the features attribute.
*
* \param features Features bitmask.
*
* \return none
*/
/*************************************************************************************************/
void RscpsSetFeatures(uint16_t features);
/*************************************************************************************************/
/*!
* \brief Notifies the collector of a Running Speed and Cadence Measurement.
*
* \param connId Connection ID
*
* \return none
*/
/*************************************************************************************************/
void RscpsSendSpeedMeasurement(dmConnId_t connId);
/*! \} */ /* RUNNING_SPEED_AND_CADENCE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* RSCP_API_H */
@@ -0,0 +1,198 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Running Speed and Cadence Profile Sensor Implementation.
*
* 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 "util/bstream.h"
#include "dm_api.h"
#include "svc_rscs.h"
#include "svc_ch.h"
#include "rscp_api.h"
/*************************************************************************************************
* Constant Definitions
*************************************************************************************************/
/*! \brief The maximum length of a power measurement */
#define RSCPS_PM_MAX_LEN 10
/*! \brief Running Speed Measurement Flag Indicies */
enum
{
RSCPS_ISLP_FLAG_INDEX, /*! \brief Instantaneous Stride Length Present Present */
RSCPS_TDP_FLAG_INDEX, /*! \brief Total Distance Present */
RSCPS_WRS_FLAG_INDEX, /*! \brief Walking or Running Status bits */
RSCPS_NUM_FLAGS
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief Running Speed Measurement Data */
typedef struct
{
uint8_t flags; /*! \brief Speed Measurement Flags */
uint16_t speed; /*! \brief Instantaneous Speed */
uint8_t cadence; /*! \brief Instantaneous Cadence */
uint16_t stride; /*! \brief Instantaneous Stride Length */
uint32_t distance; /*! \brief Total Distance */
} rscpSmData_t;
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! \brief Measurement data */
rscpSmData_t rscpSmData;
/*************************************************************************************************/
/*!
* \brief Set the sensor location attribute.
*
* \param location Sensor Location.
*
* \return none
*/
/*************************************************************************************************/
void RscpsSetSensorLocation(uint8_t location)
{
AttsSetAttr(RSCS_SL_HDL, sizeof(uint8_t), &location);
}
/*************************************************************************************************/
/*!
* \brief Set the features attribute.
*
* \param features Features bitmask.
*
* \return none
*/
/*************************************************************************************************/
void RscpsSetFeatures(uint16_t features)
{
uint8_t tempData[2] = {UINT16_TO_BYTES(features)};
AttsSetAttr(RSCS_RSF_HDL, sizeof(tempData), tempData);
}
/*************************************************************************************************/
/*!
* \brief Set a running speed measurement parameter.
*
* \param type Parameter identifier
* \param value Measurement value.
*
* \return none
*/
/*************************************************************************************************/
void RscpsSetParameter(uint8_t type, uint32_t value)
{
switch (type)
{
case RSCP_SM_PARAM_SPEED:
rscpSmData.speed = (uint16_t) value;
break;
case RSCP_SM_PARAM_CADENCE:
rscpSmData.cadence = (uint8_t) value;
break;
case RSCP_SM_PARAM_STRIDE_LENGTH:
rscpSmData.flags |= (1 << RSCPS_ISLP_FLAG_INDEX);
rscpSmData.stride = (uint16_t) value;
break;
case RSCP_SM_PARAM_TOTAL_DISTANCE:
rscpSmData.flags |= (1 << RSCPS_TDP_FLAG_INDEX);
rscpSmData.distance = value;
break;
case RSCP_SM_PARAM_STATUS:
if (value)
{
rscpSmData.flags |= (1 << RSCPS_WRS_FLAG_INDEX);
}
else
{
rscpSmData.flags &= ~(1 << RSCPS_WRS_FLAG_INDEX);
}
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Notifies the collector of a Cycle Speed Measurement.
*
* \param connId Connection ID
*
* \return none
*/
/*************************************************************************************************/
void RscpsSendSpeedMeasurement(dmConnId_t connId)
{
int8_t i;
uint16_t len;
uint8_t msg[RSCPS_PM_MAX_LEN];
uint8_t *p = msg;
/* Add manditory parameters */
UINT8_TO_BSTREAM(p, rscpSmData.flags);
UINT16_TO_BSTREAM(p, rscpSmData.speed);
UINT8_TO_BSTREAM(p, rscpSmData.cadence);
/* Add optional parameters */
for (i = 0; i < RSCPS_NUM_FLAGS; i++)
{
if (rscpSmData.flags & (1 << i))
{
switch (i)
{
case RSCPS_ISLP_FLAG_INDEX:
UINT16_TO_BSTREAM(p, rscpSmData.stride);
break;
case RSCPS_TDP_FLAG_INDEX:
UINT32_TO_BSTREAM(p, rscpSmData.distance);
break;
default:
break;
}
}
}
/* Calculate message length */
len = (uint16_t) (p - msg);
/* Transmit notification */
AttsHandleValueNtf(connId, RSCS_RSM_HDL, len, msg);
/* Clear the measurement data */
memset(&rscpSmData, 0, sizeof(rscpSmData));
}
@@ -0,0 +1,79 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Scan Parameter Profile Server Application Interface.
*
* 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.
*/
/*************************************************************************************************/
#ifndef SCPPS_API_H
#define SCPPS_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup SCAN_PARAMETER_PROFILE
* \{ */
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*! \brief Application interval window callback */
typedef void ScppsAppCback_t(dmConnId_t connId, uint16_t interval, uint16_t window);
/*************************************************************************************************/
/*!
* \brief Called to register an application scan interval window callback function
*
* \param cback Application interval window callback
*
* \return Status
*/
/*************************************************************************************************/
void ScppsRegisterCback(ScppsAppCback_t *cback);
/*************************************************************************************************/
/*!
* \brief Called when the peer writes to SCPPS attributes
*
* \param connId DM connection identifier.
* \param handle ATT handle.
* \param operation ATT operation.
* \param offset Write offset.
* \param len Write length.
* \param pValue Value to write.
* \param pAttr Attribute to write.
*
* \return Status
*/
/*************************************************************************************************/
uint8_t ScppsAttsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr);
/*! \} */ /* SCAN_PARAMETER_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* SCPPS_API_H */
@@ -0,0 +1,75 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Scan Parameter Profile Server.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "app_api.h"
#include "scpps_api.h"
#include "svc_scpss.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
static ScppsAppCback_t *scppsAppCback;
/*************************************************************************************************/
/*!
* \brief Called to register an application callback function
*
* \param cback Application interval window callback
*
* \return Status
*/
/*************************************************************************************************/
void ScppsRegisterCback(ScppsAppCback_t *cback)
{
scppsAppCback = cback;
}
/*************************************************************************************************/
/*!
* \brief Called when the peer writes to SCPPS attributes
*
* \return Status
*/
/*************************************************************************************************/
uint8_t ScppsAttsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
if (scppsAppCback && handle == SCPSS_SIW_HDL)
{
uint16_t interval;
uint16_t window;
BSTREAM_TO_UINT16(interval, pValue);
BSTREAM_TO_UINT16(window, pValue);
/* Call the callback to the application layer */
(*scppsAppCback)(connId, interval, window);
}
return 0;
}
@@ -0,0 +1,97 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Example gyroscope service profile.
*
* 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 GYRO_API_H
#define GYRO_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GYROSCOPE_SERVICE_PROFILE
* \{ */
#include "wsf_types.h"
#include "wsf_os.h"
/*************************************************************************************************/
/*!
* \brief Start service.
*
* \param handlerId Handler ID.
* \param timerEvt Timer message event.
*
* \return None.
*/
/*************************************************************************************************/
void GyroStart(wsfHandlerId_t handlerId, uint8_t timerEvt);
/*************************************************************************************************/
/*!
* \brief Stop service.
*
* \return None.
*/
/*************************************************************************************************/
void GyroStop(void);
/*************************************************************************************************/
/*!
* \brief Measurement stop handler.
*
* \return None.
*/
/*************************************************************************************************/
void GyroMeasStop(void);
/*************************************************************************************************/
/*!
* \brief Measurement start handler.
*
* \return None.
*/
/*************************************************************************************************/
void GyroMeasStart(void);
/*************************************************************************************************/
/*!
* \brief Measurement complete handler.
*
* \param connId Connection ID.
* \param x Gyroscope x-axis reading.
* \param y Gyroscope y-axis reading.
* \param z Gyroscope z-axis reading.
*
* \return None.
*/
/*************************************************************************************************/
void GyroMeasComplete(dmConnId_t connId, int16_t x, int16_t y, int16_t z);
/*! \} */ /* GYROSCOPE_SERVICE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GYRO_API_H */
@@ -0,0 +1,236 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Example gyroscope service profile.
*
* 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.
*/
/*************************************************************************************************/
#include <stdlib.h>
#include "wsf_types.h"
#include "app_api.h"
#include "att_api.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "gyro_api.h"
#include "svc_gyro.h"
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! Control block. */
static struct
{
wsfTimer_t measTimer;
bool_t measTimerStarted;
} gyroCb;
/*************************************************************************************************/
/*!
* \brief Update measurement timer.
*
* \return None.
*/
/*************************************************************************************************/
static void gyroUpdateTimer(void)
{
uint8_t config;
uint8_t *pConfig = NULL;
uint8_t period;
uint8_t *pPeriod = NULL;
uint16_t attLen = 0;
/* Get config & period. */
AttsGetAttr(GYRO_HANDLE_CONFIG, &attLen, &pConfig);
if (pConfig == NULL)
{
WSF_TRACE_ERR0("gyro: unable to read config");
return;
}
config = *pConfig;
AttsGetAttr(GYRO_HANDLE_PERIOD, &attLen, &pPeriod);
if (pPeriod == NULL)
{
WSF_TRACE_ERR0("gyro: unable to read period");
return;
}
period = *pPeriod;
if (period < GYRO_ATT_PERIOD_MIN)
{
period = GYRO_ATT_PERIOD_MIN;
}
if (config == GYRO_ATT_CONFIG_ENABLE)
{
if (!gyroCb.measTimerStarted)
{
gyroCb.measTimerStarted = TRUE;
WsfTimerStartMs(&gyroCb.measTimer, period * 10u);
}
}
else
{
if (gyroCb.measTimerStarted)
{
gyroCb.measTimerStarted = FALSE;
WsfTimerStop(&gyroCb.measTimer);
}
}
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for gyroscope profile.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t gyroWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue,
attsAttr_t *pAttr)
{
switch (handle)
{
case GYRO_HANDLE_CONFIG:
{
uint8_t config;
/* Check attribute value. */
if (len != 1)
{
return ATT_ERR_LENGTH;
}
config = *pValue;
if ((config != GYRO_ATT_CONFIG_DISABLE) && (config != GYRO_ATT_CONFIG_ENABLE))
{
return ATT_ERR_RANGE;
}
/* Save value. */
AttsSetAttr(GYRO_HANDLE_CONFIG, len, pValue);
/* Enable or disable timer. */
gyroUpdateTimer();
return ATT_SUCCESS;
}
case GYRO_HANDLE_PERIOD:
{
uint8_t period;
if (len != 1)
{
return ATT_ERR_LENGTH;
}
period = *pValue;
if ((period < GYRO_ATT_PERIOD_MIN) || (period > GYRO_ATT_PERIOD_MAX))
{
return ATT_ERR_RANGE;
}
AttsSetAttr(GYRO_HANDLE_PERIOD, len, pValue);
return ATT_SUCCESS;
}
}
return ATT_ERR_NOT_SUP;
}
/*************************************************************************************************/
/*!
* \brief Start service.
*
* \param handlerId Handler ID.
* \param timerEvt Timer message event.
*
* \return None.
*/
/*************************************************************************************************/
void GyroStart(wsfHandlerId_t handlerId, uint8_t timerEvt)
{
SvcGyroAddGroup();
SvcGyroCbackRegister(gyroWriteCback);
gyroCb.measTimer.handlerId = handlerId;
gyroCb.measTimer.msg.event = timerEvt;
gyroCb.measTimerStarted = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Stop service.
*
* \return None.
*/
/*************************************************************************************************/
void GyroStop(void)
{
gyroCb.measTimerStarted = FALSE;
WsfTimerStop(&gyroCb.measTimer);
SvcGyroRemoveGroup();
}
/*************************************************************************************************/
/*!
* \brief Measurement stop handler.
*
* \return None.
*/
/*************************************************************************************************/
void GyroMeasStop(void)
{
gyroCb.measTimerStarted = FALSE;
WsfTimerStop(&gyroCb.measTimer);
}
/*************************************************************************************************/
/*!
* \brief Measurement start handler.
*
* \return None.
*/
/*************************************************************************************************/
void GyroMeasStart(void)
{
gyroUpdateTimer();
}
/*************************************************************************************************/
/*!
* \brief Measurement complete handler.
*
* \param connId Connection ID.
* \param x Gyroscope x-axis reading.
* \param y Gyroscope y-axis reading.
* \param z Gyroscope z-axis reading.
*
* \return None.
*/
/*************************************************************************************************/
void GyroMeasComplete(dmConnId_t connId, int16_t x, int16_t y, int16_t z)
{
gyroCb.measTimerStarted = FALSE;
uint8_t gyroData[6] = {UINT16_TO_BYTES(x), UINT16_TO_BYTES(y), UINT16_TO_BYTES(z)};
AttsSetAttr(GYRO_HANDLE_DATA, sizeof(gyroData), gyroData);
AttsHandleValueNtf(connId, GYRO_HANDLE_DATA, sizeof(gyroData), gyroData);
gyroUpdateTimer();
}
@@ -0,0 +1,95 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Example temperature service profile.
*
* 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 TEMP_API_H
#define TEMP_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup TEMPERATURE_SERVICE_PROFILE
* \{ */
#include "wsf_types.h"
#include "wsf_os.h"
/*************************************************************************************************/
/*!
* \brief Start service.
*
* \param handlerId Handler ID.
* \param timerEvt Timer message event.
*
* \return None.
*/
/*************************************************************************************************/
void TempStart(wsfHandlerId_t handlerId, uint8_t timerEvt);
/*************************************************************************************************/
/*!
* \brief Stop service.
*
* \return None.
*/
/*************************************************************************************************/
void TempStop(void);
/*************************************************************************************************/
/*!
* \brief Measurement stop handler.
*
* \return None.
*/
/*************************************************************************************************/
void TempMeasStop(void);
/*************************************************************************************************/
/*!
* \brief Measurement start handler.
*
* \return None.
*/
/*************************************************************************************************/
void TempMeasStart(void);
/*************************************************************************************************/
/*!
* \brief Measurement complete handler.
*
* \param connId Connection ID.
* \param temp Temperature reading.
*
* \return None.
*/
/*************************************************************************************************/
void TempMeasComplete(dmConnId_t connId, int16_t temp);
/*! \} */ /* TEMPERATURE_SERVICE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* TEMP_API_H */
@@ -0,0 +1,232 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Example temperature service profile.
*
* 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.
*/
/*************************************************************************************************/
#include <stdlib.h>
#include "wsf_types.h"
#include "app_api.h"
#include "att_api.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "temp_api.h"
#include "svc_temp.h"
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! Control block. */
static struct
{
wsfTimer_t measTimer;
bool_t measTimerStarted;
} tempCb;
/*************************************************************************************************/
/*!
* \brief Update measurement timer.
*
* \return None.
*/
/*************************************************************************************************/
static void tempUpdateTimer(void)
{
uint8_t config;
uint8_t *pConfig = NULL;
uint8_t period;
uint8_t *pPeriod = NULL;
uint16_t attLen = 0;
/* Get config & period. */
AttsGetAttr(TEMP_HANDLE_CONFIG, &attLen, &pConfig);
if (pConfig == NULL)
{
WSF_TRACE_ERR0("temp: unable to read config");
return;
}
config = *pConfig;
AttsGetAttr(TEMP_HANDLE_PERIOD, &attLen, &pPeriod);
if (pPeriod == NULL)
{
WSF_TRACE_ERR0("temp: unable to read period");
return;
}
period = *pPeriod;
if (period < TEMP_ATT_PERIOD_MIN)
{
period = TEMP_ATT_PERIOD_MIN;
}
if (config == TEMP_ATT_CONFIG_ENABLE)
{
if (!tempCb.measTimerStarted)
{
tempCb.measTimerStarted = TRUE;
WsfTimerStartMs(&tempCb.measTimer, period * 10u);
}
}
else
{
if (tempCb.measTimerStarted)
{
tempCb.measTimerStarted = FALSE;
WsfTimerStop(&tempCb.measTimer);
}
}
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for temperature profile.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t tempWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue,
attsAttr_t *pAttr)
{
switch (handle)
{
case TEMP_HANDLE_CONFIG:
{
uint8_t config;
/* Check attribute value. */
if (len != 1)
{
return ATT_ERR_LENGTH;
}
config = *pValue;
if ((config != TEMP_ATT_CONFIG_DISABLE) && (config != TEMP_ATT_CONFIG_ENABLE))
{
return ATT_ERR_RANGE;
}
/* Save value. */
AttsSetAttr(TEMP_HANDLE_CONFIG, len, pValue);
/* Enable or disable timer. */
tempUpdateTimer();
return ATT_SUCCESS;
}
case TEMP_HANDLE_PERIOD:
{
uint8_t period;
if (len != 1)
{
return ATT_ERR_LENGTH;
}
period = *pValue;
if ((period < TEMP_ATT_PERIOD_MIN) || (period > TEMP_ATT_PERIOD_MAX))
{
return ATT_ERR_RANGE;
}
AttsSetAttr(TEMP_HANDLE_PERIOD, len, pValue);
return ATT_SUCCESS;
}
}
return ATT_ERR_NOT_SUP;
}
/*************************************************************************************************/
/*!
* \brief Start service.
*
* \param handlerId Handler ID.
* \param timerEvt Timer message event.
*
* \return None.
*/
/*************************************************************************************************/
void TempStart(wsfHandlerId_t handlerId, uint8_t timerEvt)
{
SvcTempAddGroup();
SvcTempCbackRegister(tempWriteCback);
tempCb.measTimer.handlerId = handlerId;
tempCb.measTimer.msg.event = timerEvt;
tempCb.measTimerStarted = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Stop service.
*
* \return None.
*/
/*************************************************************************************************/
void TempStop(void)
{
TempMeasStop();
SvcTempRemoveGroup();
}
/*************************************************************************************************/
/*!
* \brief Measurement stop handler.
*
* \return None.
*/
/*************************************************************************************************/
void TempMeasStop(void)
{
tempCb.measTimerStarted = FALSE;
WsfTimerStop(&tempCb.measTimer);
}
/*************************************************************************************************/
/*!
* \brief Measurement start handler.
*
* \return None.
*/
/*************************************************************************************************/
void TempMeasStart(void)
{
tempUpdateTimer();
}
/*************************************************************************************************/
/*!
* \brief Measurement complete handler.
*
* \param connId Connection ID.
* \param temp Temperature reading.
*
* \return None.
*/
/*************************************************************************************************/
void TempMeasComplete(dmConnId_t connId, int16_t temp)
{
tempCb.measTimerStarted = FALSE;
uint8_t tempData[2] = {UINT16_TO_BYTES(temp)};
AttsSetAttr(TEMP_HANDLE_DATA, sizeof(tempData), tempData);
AttsHandleValueNtf(connId, TEMP_HANDLE_DATA, sizeof(tempData), tempData);
tempUpdateTimer();
}
@@ -0,0 +1,90 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Time profile client.
*
* 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 TIPC_API_H
#define TIPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup TIME_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Current Time service enumeration of handle indexes of characteristics to be discovered */
enum
{
TIPC_CTS_CT_HDL_IDX, /*!< \brief Current time */
TIPC_CTS_CT_CCC_HDL_IDX, /*!< \brief Current time client characteristic configuration descriptor */
TIPC_CTS_LTI_HDL_IDX, /*!< \brief Local time information */
TIPC_CTS_RTI_HDL_IDX, /*!< \brief Reference time information */
TIPC_CTS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Current Time service. Parameter
* pHdlList must point to an array of length \ref TIPC_CTS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void TipcCtsDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref TIPC_CTS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t TipcCtsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* TIME_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* TIPC_API_H */
@@ -0,0 +1,176 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Time profile client.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "app_api.h"
#include "tipc_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Current Time service
*/
/* Characteristics for discovery */
/*! Current time */
static const attcDiscChar_t tipcCtsCt =
{
attCtChUuid,
ATTC_SET_REQUIRED
};
/*! Current time client characteristic configuration descriptor */
static const attcDiscChar_t tipcCtsCtCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Local time information */
static const attcDiscChar_t tipcCtsLti =
{
attLtiChUuid,
0
};
/*! Reference time information */
static const attcDiscChar_t tipcCtsRti =
{
attRtiChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *tipcCtsDiscCharList[] =
{
&tipcCtsCt, /* Current time */
&tipcCtsCtCcc, /* Current time client characteristic configuration descriptor */
&tipcCtsLti, /* Local time information */
&tipcCtsRti /* Reference time information */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(TIPC_CTS_HDL_LIST_LEN == ((sizeof(tipcCtsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Current Time service. Parameter
* pHdlList must point to an array of length TIPC_CTS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void TipcCtsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attCtsSvcUuid,
TIPC_CTS_HDL_LIST_LEN, (attcDiscChar_t **) tipcCtsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length TIPC_CTS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t TipcCtsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
uint8_t *p;
uint16_t year;
uint8_t month, day, hour, min, sec, sec256, dayOfWeek, adjustReason;
int8_t timeZone;
uint8_t dstOffset, source, accuracy;
/* Suppress unused variable compile warning */
(void)month; (void)day; (void)hour; (void)min; (void)sec; (void)dayOfWeek; (void)adjustReason;
(void)year; (void)sec256; (void)dstOffset; (void)accuracy; (void)timeZone; (void)source;
/* current time */
if (pMsg->handle == pHdlList[TIPC_CTS_CT_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT16(year, p);
BSTREAM_TO_UINT8(month, p);
BSTREAM_TO_UINT8(day, p);
BSTREAM_TO_UINT8(hour, p);
BSTREAM_TO_UINT8(min, p);
BSTREAM_TO_UINT8(sec, p);
BSTREAM_TO_UINT8(dayOfWeek, p);
BSTREAM_TO_UINT8(sec256, p);
BSTREAM_TO_UINT8(adjustReason, p);
APP_TRACE_INFO3("Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3("Time: %02d:%02d:%02d", hour, min, sec);
APP_TRACE_INFO3("dayOfWeek:%d sec256:%d adjustReason:%d", dayOfWeek, sec256, adjustReason);
}
/* local time information */
else if (pMsg->handle == pHdlList[TIPC_CTS_LTI_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(timeZone, p);
BSTREAM_TO_UINT8(dstOffset, p);
APP_TRACE_INFO2("timeZone:%d dstOffset:%d", timeZone, dstOffset);
}
/* reference time information */
else if (pMsg->handle == pHdlList[TIPC_CTS_RTI_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(source, p);
BSTREAM_TO_UINT8(accuracy, p);
BSTREAM_TO_UINT8(day, p);
BSTREAM_TO_UINT8(hour, p);
APP_TRACE_INFO2("Ref. time source:%d accuracy:%d", source, accuracy);
APP_TRACE_INFO2("Last update days:%d hours:%d", day, hour);
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,228 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief User Data Service Collector.
*
* Copyright (c) 2017-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 UDSC_API_H
#define UDSC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup USER_DATA_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief UDSC service enumeration of handle indexes of characteristics to be discovered */
enum
{
UDSC_DBCI_HDL_IDX, /*!< \brief Database Change Interval */
UDSC_DCBI_CCC_HDL_IDX, /*!< \brief Database Change Interval CCC descriptor */
UDSC_UI_HDL_IDX, /*!< \brief User Index */
UDSC_UCP_IDX, /*!< \brief User Control Point */
UDSC_UCP_CCC_HDL_IDX, /*!< \brief User Control Point CCC descriptor */
UDSC_HDL_LIST_LEN /*!< \brief Handle list length */
};
/** \name User Control Point Opcodes
*
*/
/**@{*/
#define UDSC_UCP_OPCODE_RNU 0x01 /*!< \brief Register New User */
#define UDSC_UCP_OPCODE_CONSENT 0x02 /*!< \brief Consent */
#define UDSC_UCP_OPCODE_DUD 0x03 /*!< \brief Delete User Data */
#define UDSC_UCP_OPCODE_RESPONSE 0x20 /*!< \brief Command Response */
/**@}*/
/** \name User Control Point Response Values
*
*/
/**@{*/
#define UDSC_UCP_RSP_SUCCESS 0x01 /*!< \brief Success */
#define UDSC_UCP_RSP_OP_NOT_SUPPORTED 0x02 /*!< \brief Opcode not supported */
#define UDSC_UCP_RSP_INVALID_PARAMETER 0x03 /*!< \brief Invalid Parameter */
#define UDSC_UCP_RSP_OP_FAILED 0x04 /*!< \brief Operation Failed */
#define UDSC_UCP_RSP_NOT_AUTHORIZED 0x05 /*!< \brief User Not Authorized */
/**@}*/
/**************************************************************************************************
Callback Function Datatypes
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \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 \ref UDSC_UCP_OPCODE_RNU)
*
* \return None
*/
/*************************************************************************************************/
typedef void (*UdsRspCback_t)(dmConnId_t connId, uint8_t opcode, uint8_t response, uint8_t index);
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for User Data service. Parameter
* pHdlList must point to an array of length \ref UDSC_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void UdscDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref UDSC_HDL_LIST_LEN.
* If the ATT handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t UdscValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Read the user index characteristic.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
*
* \return None.
*/
/*************************************************************************************************/
void UdscReadUserIndex(dmConnId_t connId, uint16_t handle);
/*************************************************************************************************/
/*!
* \brief Read the database change increment characteristic.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
*
* \return None.
*/
/*************************************************************************************************/
void UdscReadDatabaseChangeIncrement(dmConnId_t connId, uint16_t handle);
/*************************************************************************************************/
/*!
* \brief Write the database change increment characteristic.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param increment DB Change Increment
*
* \return None.
*/
/*************************************************************************************************/
void UdscWriteDatabaseChangeIncrement(dmConnId_t connId, uint16_t handle, uint32_t increment);
/*************************************************************************************************/
/*!
* \brief Write to the user control point characteristic - Register New User.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param consentCode Consent code (0-9999)
*
* \return None.
*/
/*************************************************************************************************/
void UdscRegisterNewUser(dmConnId_t connId, uint16_t handle, uint16_t consentCode);
/*************************************************************************************************/
/*!
* \brief Write to the user control point characteristic - Consent.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param index User Index
* \param consentCode Consent code (0-9999 - provided when user was registered)
*
* \return None.
*/
/*************************************************************************************************/
void UdscConsent(dmConnId_t connId, uint16_t handle, uint8_t index, uint16_t consentCode);
/*************************************************************************************************/
/*!
* \brief Write to the user control point characteristic - Delete User Data.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
*
* \return None.
*/
/*************************************************************************************************/
void UdscDeleteUserData(dmConnId_t connId, uint16_t handle);
/*************************************************************************************************/
/*!
* \brief Called by the application when a connection closes.
*
* \return None.
*/
/*************************************************************************************************/
void UdscClose(void);
/*************************************************************************************************/
/*!
* \brief Initialize User Data Service collector callbacks.
*
* \param handlerId Application task handler ID.
* \param timerEvent Application timer event for control point timeout.
* \param rspCback Callback to receive control point response messages.
*
* \return None.
*/
/*************************************************************************************************/
void UdscInit(wsfHandlerId_t handlerId, uint8_t timerEvent, UdsRspCback_t rspCback);
/*! \} */ /* USER_DATA_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* UDSC_API_H */
@@ -0,0 +1,393 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief User Data Service Collector.
*
* Copyright (c) 2017-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 "util/bstream.h"
#include "svc_ch.h"
#include "app_api.h"
#include "udsc_api.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/* Time to wait for control point response in seconds */
#define UDSC_RESPONSE_TIMEOUT 45
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
UdsRspCback_t rspCback; /* Control Point Response Callback */
wsfTimer_t rspTimer;
} UdscCb;
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* User data service characteristics for discovery
*/
/*! Database change increment */
static const attcDiscChar_t udscDbci =
{
attDbciChUuid,
0
};
/*! Database change increment CCC descriptor */
static const attcDiscChar_t udscDcbiCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! User index */
static const attcDiscChar_t udscUi =
{
attUiChUuid,
0
};
/*! User control point */
static const attcDiscChar_t udscUcp =
{
attUcpChUuid,
0
};
/*! User control point CCC descriptor */
static const attcDiscChar_t udscUcpCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *udscDiscCharList[] =
{
&udscDbci, /*! Database Change Increment */
&udscDcbiCcc, /*! Database Change Interval CCC descriptor */
&udscUi, /*! User Index */
&udscUcp, /*! User control point */
&udscUcpCcc, /*! User control point CCC descriptor */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(UDSC_HDL_LIST_LEN == ((sizeof(udscDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for User Data service. Parameter
* pHdlList must point to an array of length UDSC_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void UdscDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attUdsSvcUuid,
UDSC_HDL_LIST_LEN, (attcDiscChar_t **) udscDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Parse a User Control Point message.
*
* \param connId Connection identifier.
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
static void udscParseUcp(dmConnId_t connId, uint8_t *pValue, uint16_t len)
{
uint8_t opcode;
BSTREAM_TO_INT8(opcode, pValue);
if (opcode == UDSC_UCP_OPCODE_RESPONSE)
{
/* Opcode is a response message */
uint8_t responseOpcode;
uint8_t response, index = 0;
BSTREAM_TO_INT8(responseOpcode, pValue);
BSTREAM_TO_INT8(response, pValue);
/* Stop the timer waiting for a control point response */
WsfTimerStop(&UdscCb.rspTimer);
if ((response == UDSC_UCP_RSP_SUCCESS) && (responseOpcode == UDSC_UCP_OPCODE_RNU))
{
BSTREAM_TO_INT8(index, pValue);
}
if (UdscCb.rspCback)
{
UdscCb.rspCback(connId, responseOpcode, response, index);
}
}
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length UDSC_PLXS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t UdscValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* User Control Point */
if (pMsg->handle == pHdlList[UDSC_UCP_IDX])
{
APP_TRACE_INFO0("UDSC: User Control Point notification");
/* parse value */
udscParseUcp((dmConnId_t) pMsg->hdr.param, pMsg->pValue, pMsg->valueLen);
}
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Read the user index characteristic.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
*
* \return None.
*/
/*************************************************************************************************/
void UdscReadUserIndex(dmConnId_t connId, uint16_t handle)
{
if (handle != ATT_HANDLE_NONE)
{
AttcReadReq(connId, handle);
}
}
/*************************************************************************************************/
/*!
* \brief Read the database change increment characteristic.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
*
* \return None.
*/
/*************************************************************************************************/
void UdscReadDatabaseChangeIncrement(dmConnId_t connId, uint16_t handle)
{
if (handle != ATT_HANDLE_NONE)
{
AttcReadReq(connId, handle);
}
}
/*************************************************************************************************/
/*!
* \brief Write the database change increment characteristic.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param increment DB change increment
*
* \return None.
*/
/*************************************************************************************************/
void UdscWriteDatabaseChangeIncrement(dmConnId_t connId, uint16_t handle, uint32_t increment)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t *p = buf;
if (handle != ATT_HANDLE_NONE)
{
/* build command */
UINT32_TO_BSTREAM(p, increment);
AttcWriteReq(connId, handle, (uint16_t) (p - buf), buf);
}
}
/*************************************************************************************************/
/*!
* \brief Write to the control point and set a timer waiting for the response.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param valueLen Size of pValue in bytes
* \param pValue Value to write
*
* \return None.
*/
/*************************************************************************************************/
static void udscWriteControlPoint(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
{
/* Start timer waiting for response */
WsfTimerStartSec(&UdscCb.rspTimer, UDSC_RESPONSE_TIMEOUT);
/* Send write request */
AttcWriteReq(connId, handle, valueLen, pValue);
}
/*************************************************************************************************/
/*!
* \brief Write to the user control point characteristic - Register New User.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param consentCode Consent code (0-9999)
*
* \return None.
*/
/*************************************************************************************************/
void UdscRegisterNewUser(dmConnId_t connId, uint16_t handle, uint16_t consentCode)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t *p = buf;
if (handle != ATT_HANDLE_NONE)
{
/* build command */
UINT8_TO_BSTREAM(p, UDSC_UCP_OPCODE_RNU);
UINT16_TO_BSTREAM(p, consentCode);
udscWriteControlPoint(connId, handle, (uint16_t) (p - buf), buf);
}
}
/*************************************************************************************************/
/*!
* \brief Write to the user control point characteristic - Consent.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
* \param index User index
* \param consentCode Consent code (0-9999 - provided when user was registered)
*
* \return None.
*/
/*************************************************************************************************/
void UdscConsent(dmConnId_t connId, uint16_t handle, uint8_t index, uint16_t consentCode)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t *p = buf;
if (handle != ATT_HANDLE_NONE)
{
/* build command */
UINT8_TO_BSTREAM(p, UDSC_UCP_OPCODE_CONSENT);
UINT8_TO_BSTREAM(p, index);
UINT16_TO_BSTREAM(p, consentCode);
udscWriteControlPoint(connId, handle, (uint16_t) (p - buf), buf);
}
}
/*************************************************************************************************/
/*!
* \brief Write to the user control point characteristic - Delete User Data.
*
* \param connId Connection identifier.
* \param handle Attribute handle.
*
* \return None.
*/
/*************************************************************************************************/
void UdscDeleteUserData(dmConnId_t connId, uint16_t handle)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t *p = buf;
if (handle != ATT_HANDLE_NONE)
{
/* build command */
UINT8_TO_BSTREAM(p, UDSC_UCP_OPCODE_DUD);
udscWriteControlPoint(connId, handle, (uint16_t) (p - buf), buf);
}
}
/*************************************************************************************************/
/*!
* \brief Called by the application when a connection closes.
*
* \return None.
*/
/*************************************************************************************************/
void UdscClose(void)
{
/* Stop the timer waiting for a control point response */
WsfTimerStop(&UdscCb.rspTimer);
}
/*************************************************************************************************/
/*!
* \brief Initialize User Data Service collector callbacks.
*
* \param handlerId Application task handler ID.
* \param timerEvent Application timer event for control point timeout.
* \param rspCback Callback to receive control point response messages.
*
* \return None.
*/
/*************************************************************************************************/
void UdscInit(wsfHandlerId_t handlerId, uint8_t timerEvent, UdsRspCback_t rspCback)
{
UdscCb.rspTimer.handlerId = handlerId;
UdscCb.rspTimer.msg.event = timerEvent;
UdscCb.rspCback = rspCback;
}
@@ -0,0 +1,140 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief UriBeacon configuration profile.
*
* 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 URICFG_API_H
#define URICFG_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup URIBEACON_CONFIGURATION_PROFILE
* \{ */
#include "wsf_types.h"
#include "att_api.h"
#include "svc_uricfg.h"
#include "uricfg_defs.h"
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Attribute write callback.
*
* \param handle Attribute handle.
* \param valueLen Length of value data.
* \param pValue Pointer to value data.
*
* \return None.
*/
/*************************************************************************************************/
typedef void (*uriCfgAttWriteCback_t)(uint16_t handle, uint16_t valueLen, const uint8_t *pValue);
/*************************************************************************************************/
/*!
* \brief Lock change callback.
*
* \param lockState New lock state.
* \param lock Lock value.
*
* \return None.
*/
/*************************************************************************************************/
typedef void (*uriCfgLockChangeCback_t)(uint8_t lockState, const uint8_t *pLock);
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start UriBeacon configuration service.
*
* \param pUriData Initial URI data value
* \param uriDataLen Length of URI data value
* \param uriFlags Initial URI flags value
* \param pAdvTxPwrLevels Initial advertised tx power levels value
* \param txPwrMode Initial tx power mode value
* \param beaconPeriod Initial beacon period value
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgStart(const uint8_t *pUriData, uint8_t uriDataLen, uint8_t uriFlags,
int8_t *pAdvTxPwrLevels, uint8_t txPwrMode, uint16_t beaconPeriod);
/*************************************************************************************************/
/*!
* \brief Stop UriBeacon configuration service.
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgStop(void);
/*************************************************************************************************/
/*!
* \brief Register callback for written UriBeacon attributes.
*
* \param cback Callback to invoke when an attribute changes.
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgAttWriteCbackRegister(uriCfgAttWriteCback_t cback);
/*************************************************************************************************/
/*!
* \brief Make UriBeacon lockable.
*
* \param lockState Initial lock state value.
* \param pLock Initial lock value.
* \param cback Callback to invoke when lock changes.
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgMakeLockable(uint8_t lockState, uint8_t *pLock, uriCfgLockChangeCback_t cback);
/*************************************************************************************************/
/*!
* \brief Set reset value of URI data.
*
* \param pUriData Reset value of URI data.
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgSetUriDataResetValue(const uint8_t *pUriData);
/*! \} */ /* URIBEACON_CONFIGURATION_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* URICFG_API_H */
@@ -0,0 +1,111 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief UriBeacon configuration service defines.
*
* 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 URICFG_DEFS_H
#define URICFG_DEFS_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup URIBEACON_CONFIGURATION_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief UriBeacon configuration service-related UUIDs */
#define URICFG_UUID_BYTES(id) 0xd8, 0x81, 0xc9, 0x1a, 0xb9, 0x99, \
0x96, 0xab, \
0xba, 0x40, \
0x86, 0x87, \
(id & 0xFF), ((id >> 8) & 0xFF), 0x0c, 0xee
/*! \brief UriBeacon configuration service-related UUIDs */
enum
{
URICFG_UUID_SVC = 0x2080,
URICFG_UUID_CHR_LOCKSTATE = 0x2081,
URICFG_UUID_CHR_LOCK = 0x2082,
URICFG_UUID_CHR_UNLOCK = 0x2083,
URICFG_UUID_CHR_URIDATA = 0x2084,
URICFG_UUID_CHR_URIFLAGS = 0x2085,
URICFG_UUID_CHR_TXPWRLEVELS = 0x2086,
URICFG_UUID_CHR_TXPWRMODE = 0x2087,
URICFG_UUID_CHR_BEACONPERIOD = 0x2088,
URICFG_UUID_CHR_RESET = 0x2089
};
/*! \brief Transmit power modes */
enum
{
URICFG_ATT_TXPWRMODE_LOWEST = 0,
URICFG_ATT_TXPWRMODE_LOW = 1,
URICFG_ATT_TXPWRMODE_MEDIUM = 2,
URICFG_ATT_TXPWRMODE_HIGH = 3
};
/** \name URI Config Attributes Sizes
*
*/
/**@{*/
#define URICFG_MAXSIZE_URIDATA_ATT 18 /*!< \brief Size of URI data attribute */
#define URICFG_SIZE_TXPWRLEVELS_ATT 4 /*!< \brief Size of transmit power levels attribute */
#define URICFG_SIZE_LOCK_ATT 16 /*!< \brief Size of lock attribute */
/**@}*/
/** \name Beacon period Attribute Values
*
*/
/**@{*/
#define URICFG_ATT_BEACONPERIOD_MIN 20 /*!< \brief Minimum period */
#define URICFG_ATT_BEACONPERIOD_MAX 10240 /*!< \brief Maximum period */
#define URICFG_ATT_BEACONPERIOD_DISABLE 0 /*!< \brief Value to disable beacon */
/**@}*/
/** \name Default (Reset) Values of Attributes
*
*/
/**@{*/
#define URICFG_ATT_URIFLAGS_DEFAULT 0x00 /*!< \brief Default URI flags */
#define URICFG_ATT_TXPWRMODE_DEFAULT URICFG_ATT_TXPWRMODE_LOW /*!< \brief Default TX power mode */
#define URICFG_ATT_BEACONPERIOD_DEFAULT 1000 /*!< \brief Default beacon period in milliseconds (1 second) */
#define URICFG_ATT_LOCK_DEFAULT_BYTES 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, \
0x00, 0x00, \
0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*!< \brief Default lock bytes */
/**@}*/
/*! \brief UriBeacon service UUID for advertising data */
#define URICFG_SERVICE_UUID 0xFED8
/*! \} */ /* URIBEACON_CONFIGURATION_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* URICFG_DEFS_H */
@@ -0,0 +1,369 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief UriBeacon configuration 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 "uricfg_api.h"
#include "app_api.h"
#include "att_api.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "uricfg_defs.h"
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! \brief callback when attributes are written */
static uriCfgAttWriteCback_t uriCfgAttWriteCback;
/*! \brief callback when lock status changes */
static uriCfgLockChangeCback_t uriCfgLockChangeCback;
/*! \brief reset value of URI data */
static uint8_t uriCfgDataResetValue[URICFG_MAXSIZE_URIDATA_ATT];
/*************************************************************************************************/
/*!
* \brief ATTS write callback for UriBeacon service.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t uriCfgBeaconWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
uint8_t lockState = 0;
uint8_t *pLockState = NULL;
uint16_t attLen;
if (AttsGetAttr(URICFG_HANDLE_LOCKSTATE, &attLen, &pLockState) == ATT_SUCCESS)
{
lockState = *pLockState;
}
switch (handle)
{
case URICFG_HANDLE_LOCK:
{
if (uriCfgLockChangeCback != NULL)
{
if (!lockState)
{
APP_TRACE_INFO0("URI profile locking");
lockState = 1u;
AttsSetAttr(URICFG_HANDLE_LOCK, URICFG_SIZE_LOCK_ATT, pValue);
AttsSetAttr(URICFG_HANDLE_LOCKSTATE, sizeof(lockState), &lockState);
uriCfgLockChangeCback(lockState, pValue);
}
else
{
APP_TRACE_INFO0("URI profile already locked");
return ATT_ERR_AUTHOR;
}
}
break;
}
case URICFG_HANDLE_UNLOCK:
{
if (uriCfgLockChangeCback != NULL)
{
uint8_t *pLock = NULL;
if (lockState)
{
if (AttsGetAttr(URICFG_HANDLE_LOCK, &attLen, &pLock) != ATT_SUCCESS)
{
APP_TRACE_INFO0("URI profile failed to get lock value");
return ATT_ERR_AUTHOR;
}
if (memcmp(pLock, pValue, len) != 0)
{
APP_TRACE_INFO0("URI profile lock mismatch");
return ATT_ERR_AUTHOR;
}
APP_TRACE_INFO0("URI profile unlocking");
lockState = 0u;
AttsSetAttr(URICFG_HANDLE_LOCKSTATE, sizeof(lockState), &lockState);
uriCfgLockChangeCback(lockState, pValue);
}
else
{
APP_TRACE_INFO0("URI profile already unlocked");
}
}
break;
}
case URICFG_HANDLE_URIDATA:
{
/* check lock */
if (lockState)
{
return ATT_ERR_AUTHOR;
}
/* save value */
AttsSetAttr(handle, len, pValue);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(handle, len, pValue);
}
break;
}
case URICFG_HANDLE_URIFLAGS:
case URICFG_HANDLE_TXPWRLEVELS:
{
/* check lock */
if (lockState)
{
return ATT_ERR_AUTHOR;
}
/* save value */
AttsSetAttr(handle, len, pValue);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(handle, len, pValue);
}
break;
}
case URICFG_HANDLE_TXPWRMODE:
{
uint8_t txPwrMode;
/* check lock */
if (lockState)
{
return ATT_ERR_AUTHOR;
}
/* check value */
txPwrMode = (uint8_t)*pValue;
if (txPwrMode > URICFG_ATT_TXPWRMODE_HIGH)
{
return ATT_ERR_WRITE_REJ;
}
/* save value */
AttsSetAttr(handle, sizeof(txPwrMode), &txPwrMode);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(handle, sizeof(txPwrMode), &txPwrMode);
}
break;
}
case URICFG_HANDLE_BEACONPERIOD:
{
uint16_t beaconPeriod;
if (lockState)
{
return ATT_ERR_AUTHOR;
}
/* check value */
BYTES_TO_UINT16(beaconPeriod, pValue);
/* save value */
if (beaconPeriod != URICFG_ATT_BEACONPERIOD_DISABLE)
{
if (beaconPeriod < URICFG_ATT_BEACONPERIOD_MIN)
{
beaconPeriod = URICFG_ATT_BEACONPERIOD_MIN;
}
}
uint8_t beaconPeriodVal[2] = {UINT16_TO_BYTES(beaconPeriod)};
AttsSetAttr(handle, sizeof(beaconPeriod), beaconPeriodVal);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(handle, sizeof(beaconPeriod), (uint8_t *)&beaconPeriod);
}
break;
}
case URICFG_HANDLE_RESET:
{
uint8_t resetValue;
if (lockState)
{
return ATT_ERR_AUTHOR;
}
/* check value */
resetValue = *pValue;
if (resetValue != 0)
{
uint8_t uriFlags;
uint8_t txPwrMode;
uint16_t beaconPeriod;
uint8_t lock[] = {URICFG_ATT_LOCK_DEFAULT_BYTES};
/* reset URI data */
AttsSetAttr(URICFG_HANDLE_URIDATA, sizeof(uriCfgDataResetValue), uriCfgDataResetValue);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(URICFG_HANDLE_URIDATA, sizeof(uriCfgDataResetValue), uriCfgDataResetValue);
}
/* reset URI flags */
uriFlags = URICFG_ATT_URIFLAGS_DEFAULT;
AttsSetAttr(URICFG_HANDLE_URIFLAGS, sizeof(uriFlags), &uriFlags);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(URICFG_HANDLE_URIFLAGS, sizeof(uriFlags), &uriFlags);
}
/* reset tx power mode */
txPwrMode = URICFG_ATT_TXPWRMODE_DEFAULT;
AttsSetAttr(URICFG_HANDLE_TXPWRMODE, sizeof(txPwrMode), &txPwrMode);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(URICFG_HANDLE_TXPWRMODE, sizeof(txPwrMode), &txPwrMode);
}
/* reset beacon period */
beaconPeriod = URICFG_ATT_BEACONPERIOD_DEFAULT;
uint8_t beaconPeriodVal[2] = {UINT16_TO_BYTES(beaconPeriod)};
AttsSetAttr(URICFG_HANDLE_BEACONPERIOD, sizeof(beaconPeriod), beaconPeriodVal);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(URICFG_HANDLE_BEACONPERIOD, sizeof(beaconPeriod), (uint8_t *)&beaconPeriod);
}
/* reset lock */
AttsSetAttr(URICFG_HANDLE_LOCK, URICFG_SIZE_LOCK_ATT, lock);
if (uriCfgAttWriteCback != NULL)
{
uriCfgAttWriteCback(URICFG_HANDLE_LOCK, sizeof(lock), lock);
}
}
break;
}
default:
return ATT_ERR_NOT_SUP;
}
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Start UriBeacon configuration service.
*
* \param pUriData Initial URI data value
* \param uriDataLen Length of URI data value
* \param uriFlags Initial URI flags value
* \param advTxPwrLevels Initial advertised tx power levels value
* \param txPwrMode Initial tx power mode value
* \param beaconPeriod Initial beacon period value
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgStart(const uint8_t *pUriData, uint8_t uriDataLen, uint8_t uriFlags,
int8_t *pAdvTxPwrLevels, uint8_t txPwrMode, uint16_t beaconPeriod)
{
SvcUriCfgAddGroup();
SvcUriCfgCbackRegister(uriCfgBeaconWriteCback);
uint8_t lockState = 0;
AttsSetAttr(URICFG_HANDLE_LOCKSTATE, sizeof(lockState), &lockState);
AttsSetAttr(URICFG_HANDLE_URIDATA, uriDataLen, (uint8_t *)pUriData);
AttsSetAttr(URICFG_HANDLE_URIFLAGS, sizeof(uriFlags), &uriFlags);
AttsSetAttr(URICFG_HANDLE_TXPWRLEVELS, URICFG_SIZE_TXPWRLEVELS_ATT,(uint8_t *)pAdvTxPwrLevels);
AttsSetAttr(URICFG_HANDLE_TXPWRMODE, sizeof(txPwrMode), &txPwrMode);
uint8_t beaconPeriodVal[2] = {UINT16_TO_BYTES(beaconPeriod)};
AttsSetAttr(URICFG_HANDLE_BEACONPERIOD, sizeof(beaconPeriod), beaconPeriodVal);
uriCfgAttWriteCback = NULL;
uriCfgLockChangeCback = NULL;
memset(uriCfgDataResetValue, 0xFFu, URICFG_MAXSIZE_URIDATA_ATT);
}
/*************************************************************************************************/
/*!
* \brief Stop UriBeacon service.
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgStop(void)
{
SvcUriCfgRemoveGroup();
}
/*************************************************************************************************/
/*!
* \brief Register callback for written UriBeacon attributes.
*
* \param cback Callback to invoke when an attribute changes.
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgAttWriteCbackRegister(uriCfgAttWriteCback_t cback)
{
uriCfgAttWriteCback = cback;
}
/*************************************************************************************************/
/*!
* \brief Make UriBeacon lockable.
*
* \param lockState Initial lock state value.
* \param lock Initial lock value.
* \param cback Callback to invoke when lock changes.
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgMakeLockable(uint8_t lockState, uint8_t *pLock, uriCfgLockChangeCback_t cback)
{
AttsSetAttr(URICFG_HANDLE_LOCKSTATE, sizeof(lockState), &lockState);
AttsSetAttr(URICFG_HANDLE_LOCK, URICFG_SIZE_LOCK_ATT, pLock);
uriCfgLockChangeCback = cback;
}
/*************************************************************************************************/
/*!
* \brief Set reset value of URI data.
*
* \param pUriData Reset value of URI data.
*
* \return None.
*/
/*************************************************************************************************/
void UriCfgSetUriDataResetValue(const uint8_t *pUriData)
{
memcpy(uriCfgDataResetValue, pUriData, URICFG_MAXSIZE_URIDATA_ATT);
}
@@ -0,0 +1,262 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile application interface.
*
* Copyright (c) 2017-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 WDXC_API_H
#define WDXC_API_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup WIRELESS_DATA_EXCHANGE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief WDXC enumeration of handle indexes of characteristics to be discovered */
enum
{
WDXC_DC_HDL_IDX, /*!< \brief WDX Device Configuration */
WDXC_DC_CCC_HDL_IDX, /*!< \brief WDX Device Configuration CCC descriptor */
WDXC_FTC_HDL_IDX, /*!< \brief WDX File Transfer Control */
WDXC_FTC_CCC_HDL_IDX, /*!< \brief WDX File Transfer Control CCC descriptor */
WDXC_FTD_HDL_IDX, /*!< \brief WDX File Transfer Data */
WDXC_FTD_CCC_HDL_IDX, /*!< \brief WDX File Transfer Data CCC descriptor */
WDXC_AU_HDL_IDX, /*!< \brief WDX Authenticationa */
WDXC_AU_CCC_HDL_IDX, /*!< \brief WDX Authentication CCC descriptor */
WDXC_HDL_LIST_LEN /*!< \brief WDX handle list length. */
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/**************************************************************************************************
Callback Function Datatypes
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief WDXC File Transfer Data callback.
*
* \param connId Connection ID.
* \param handle Handle of the file.
* \param len length of pData in bytes.
* \param pData File data.
*
* \return None.
*/
/*************************************************************************************************/
typedef void WdxcFtdCallback_t(dmConnId_t connId, uint16_t handle, uint16_t len, uint8_t *pData);
/*************************************************************************************************/
/*!
* \brief WDXC File Transfer Control callback.
*
* \param connId Connection ID.
* \param handle Handle of the file.
* \param op Control operation.
* \param status Status of operation.
*
* \return None.
*/
/*************************************************************************************************/
typedef void WdxcFtcCallback_t(dmConnId_t connId, uint16_t handle, uint8_t op, uint8_t status);
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Initialize the WDXC.
*
* \param pFtdCallback Application Callback for File Transfer Data.
* \param pFtcCallback Application Callback for File Transfer Control.
*
* \return None.
*/
/*************************************************************************************************/
void WdxcInit(WdxcFtdCallback_t *pFtdCallback, WdxcFtcCallback_t *pFtcCallback);
/*************************************************************************************************/
/*!
* \brief Called by application to notify the WDXC of DM and ATT Events.
*
* \param pEvt Pointer to the Event
*
* \return None.
*/
/*************************************************************************************************/
void WdxcProcMsg(wsfMsgHdr_t *pEvt);
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Wireless Data Exchange service.
* Parameter pHdlList must point to an array of length \ref WDXC_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void WdxcWdxsDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Perform File Discovery
*
* \param connId Connection ID.
* \param pFileInfo Buffer to hold information about files
* \param maxFiles Size of pFileInfo in number of \ref wsfEfsFileInfo_t objects
*
* \note When file discovery completes, the ftcCallback() will be called with operator equal to
* \ref WDX_FTC_OP_EOF and file handle equal to \ref WDX_FLIST_HANDLE.
*
* \return None.
*/
/*************************************************************************************************/
void WdxcDiscoverFiles(dmConnId_t connId, wsfEfsFileInfo_t *pFileInfo, uint8_t maxFiles);
/*************************************************************************************************/
/*!
* \brief Send a request to start a stream of a given file handle on the given connection.
*
* \param connId Connection ID.
* \param fileHdl Handle of the file.
*
* \return none.
*/
/*************************************************************************************************/
void WdxcStreamStart(dmConnId_t connId, uint16_t fileHdl);
/*************************************************************************************************/
/*!
* \brief Stop the active stream.
*
* \param connId Connection ID.
*
* \return None.
*/
/*************************************************************************************************/
void WdxcStreamStop(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Send a request to abort a wdx operation.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to abort operation on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendAbort(dmConnId_t connId, uint16_t fileHdl);
/*************************************************************************************************/
/*!
* \brief Send a request to erase a file.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to erase on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendEraseFile(dmConnId_t connId, uint16_t fileHdl);
/*************************************************************************************************/
/*!
* \brief Send a request to verify a file.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to verify on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendVerifyFile(dmConnId_t connId, uint16_t fileHdl);
/*************************************************************************************************/
/*!
* \brief Send a request to put a block of data into a file on the peer device
*
* \param connId Connection ID.
* \param fileHdl Handle of file on peer device
* \param offset The offset from the beginning of the file in bytes
* \param len The number of bytes to put
* \param fileSize The size of the file in bytes
* \param type reserved
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendPutReq(dmConnId_t connId, uint16_t fileHdl, uint32_t offset,
uint32_t len, uint32_t fileSize, uint8_t type);
/*************************************************************************************************/
/*!
* \brief Send a request to perform a get a block of data from a file on the peer device
*
* \param connId Connection ID.
* \param fileHdl Handle of file to verify on peer device
* \param offset The offset from the beginning of the file in bytes
* \param len The number of bytes to get
* \param type reserved
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendGetReq(dmConnId_t connId, uint16_t fileHdl, uint32_t offset, uint32_t len, uint8_t type);
/*************************************************************************************************/
/*!
* \brief Send a data block to the peer device
*
* \param connId Connection ID.
* \param len Size of pData in bytes
* \param pData Data to put to the file
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtdSendBlock(dmConnId_t connId, uint32_t len, uint8_t *pData);
/*! \} */ /* WIRELESS_DATA_EXCHANGE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* WDXC_MAIN_H */
@@ -0,0 +1,211 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile client - File Transfer.
*
* Copyright (c) 2017-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 <stddef.h>
#include "wsf_types.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "util/bstream.h"
#include "svc_wdxs.h"
#include "wdx_defs.h"
#include "wdxc_api.h"
#include "wdxc_main.h"
#include "dm_api.h"
#include "app_api.h"
/**************************************************************************************************
External Variables
**************************************************************************************************/
extern wdxcCb_t wdxcCb;
/*************************************************************************************************/
/*!
* \brief Send a request to abort a wdx operation.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to abort operation on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendAbort(dmConnId_t connId, uint16_t fileHdl)
{
uint8_t buf[WDX_FTC_ABORT_LEN];
uint8_t *p = buf;
uint16_t handle;
WSF_ASSERT(wdxcCb.conn[connId - 1].pHdlList != NULL)
handle = wdxcCb.conn[connId - 1].pHdlList[WDXC_FTC_HDL_IDX];
UINT8_TO_BSTREAM(p, WDX_FTC_OP_ABORT);
UINT16_TO_BSTREAM(p, fileHdl);
AttcWriteReq(connId, handle, WDX_FTC_ABORT_LEN, buf);
}
/*************************************************************************************************/
/*!
* \brief Send a request to erase a file.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to erase on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendEraseFile(dmConnId_t connId, uint16_t fileHdl)
{
uint8_t buf[WDX_FTC_ERASE_LEN];
uint8_t *p = buf;
uint16_t handle;
WSF_ASSERT(wdxcCb.conn[connId - 1].pHdlList != NULL)
handle = wdxcCb.conn[connId - 1].pHdlList[WDXC_FTC_HDL_IDX];
UINT8_TO_BSTREAM(p, WDX_FTC_OP_ERASE_REQ);
UINT16_TO_BSTREAM(p, fileHdl);
AttcWriteReq(connId, handle, WDX_FTC_ERASE_LEN, buf);
}
/*************************************************************************************************/
/*!
* \brief Send a request to verify a file.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to verify on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendVerifyFile(dmConnId_t connId, uint16_t fileHdl)
{
uint8_t buf[WDX_FTC_VERIFY_LEN];
uint8_t *p = buf;
uint16_t handle;
WSF_ASSERT(wdxcCb.conn[connId - 1].pHdlList != NULL)
handle = wdxcCb.conn[connId - 1].pHdlList[WDXC_FTC_HDL_IDX];
UINT8_TO_BSTREAM(p, WDX_FTC_OP_VERIFY_REQ);
UINT16_TO_BSTREAM(p, fileHdl);
AttcWriteReq(connId, handle, WDX_FTC_VERIFY_LEN, buf);
}
/*************************************************************************************************/
/*!
* \brief Send a request to put a block of data into a file on the peer device
*
* \param connId Connection ID.
* \param fileHdl Handle of file on peer device
* \param offset The offset from the beginning of the file in bytes
* \param len The number of bytes to put
* \param fileSize The size of the file in bytes
* \param type reserved
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendPutReq(dmConnId_t connId, uint16_t fileHdl, uint32_t offset,
uint32_t len, uint32_t fileSize, uint8_t type)
{
uint8_t buf[WDX_FTC_PUT_LEN];
uint8_t *p = buf;
uint16_t handle;
WSF_ASSERT(wdxcCb.conn[connId - 1].pHdlList != NULL)
handle = wdxcCb.conn[connId - 1].pHdlList[WDXC_FTC_HDL_IDX];
UINT8_TO_BSTREAM(p, WDX_FTC_OP_PUT_REQ);
UINT16_TO_BSTREAM(p, fileHdl);
UINT32_TO_BSTREAM(p, offset);
UINT32_TO_BSTREAM(p, len);
UINT32_TO_BSTREAM(p, fileSize);
UINT8_TO_BSTREAM(p, type);
AttcWriteReq(connId, handle, WDX_FTC_PUT_LEN, buf);
}
/*************************************************************************************************/
/*!
* \brief Send a request to perform a get a block of data from a file on the peer device
*
* \param connId Connection ID.
* \param fileHdl Handle of file to verify on peer device
* \param offset The offset from the beginning of the file in bytes
* \param len The number of bytes to get
* \param type reserved
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendGetReq(dmConnId_t connId, uint16_t fileHdl, uint32_t offset, uint32_t len, uint8_t type)
{
uint8_t buf[WDX_FTC_GET_LEN];
uint8_t *p = buf;
uint16_t handle;
WSF_ASSERT(wdxcCb.conn[connId - 1].pHdlList != NULL)
handle = wdxcCb.conn[connId - 1].pHdlList[WDXC_FTC_HDL_IDX];
UINT8_TO_BSTREAM(p, WDX_FTC_OP_GET_REQ);
UINT16_TO_BSTREAM(p, fileHdl);
UINT32_TO_BSTREAM(p, offset);
UINT32_TO_BSTREAM(p, len);
UINT8_TO_BSTREAM(p, type);
AttcWriteReq(connId, handle, WDX_FTC_GET_LEN, buf);
}
/*************************************************************************************************/
/*!
* \brief Send a data block to the peer device
*
* \param connId Connection ID.
* \param len Size of pData in bytes
* \param pData Data to put to the file
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtdSendBlock(dmConnId_t connId, uint32_t len, uint8_t *pData)
{
uint16_t handle;
WSF_ASSERT(wdxcCb.conn[connId - 1].pHdlList != NULL)
handle = wdxcCb.conn[connId - 1].pHdlList[WDXC_FTD_HDL_IDX];
AttcWriteReq(connId, handle, (uint16_t) len, pData);
}
@@ -0,0 +1,434 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile client.
*
* Copyright (c) 2013-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 "util/wstr.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "wsf_os.h"
#include "sec_api.h"
#include "util/bstream.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "svc_wdxs.h"
#include "wdx_defs.h"
#include "wdxc_api.h"
#include "wdxc_main.h"
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! WDXC control block */
wdxcCb_t wdxcCb;
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! UUIDs */
static const uint8_t wdxcDcUuid[ATT_128_UUID_LEN] = {WDX_DC_UUID}; /* WDX Device Configuration Characteristic */
static const uint8_t wdxcFtcUuid[ATT_128_UUID_LEN] = {WDX_FTC_UUID}; /* WDX File Transfer Control Characteristic */
static const uint8_t wdxcFtdUuid[ATT_128_UUID_LEN] = {WDX_FTD_UUID}; /* WDX File Transfer Data Characteristic */
static const uint8_t wdxcAuUuid[ATT_128_UUID_LEN] = {WDX_AU_UUID}; /* WDX Authentication Characteristic */
/*! WDXC Device Configuration */
static const attcDiscChar_t wdxcWdxsDc =
{
wdxcDcUuid,
ATTC_SET_UUID_128
};
/*! WDXC Device Configuration CCC descriptor */
static const attcDiscChar_t wdxcWdxsDcCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! WDXC File Transfer Control */
static const attcDiscChar_t wdxcWdxsFtc =
{
wdxcFtcUuid,
ATTC_SET_UUID_128
};
/*! WDXC File Transfer Control CCC descriptor */
static const attcDiscChar_t wdxcWdxsFtcCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! WDXC File Transfer Data */
static const attcDiscChar_t wdxcWdxsFtd =
{
wdxcFtdUuid,
ATTC_SET_UUID_128
};
/*! WDXC File Transfer Data CCC descriptor */
static const attcDiscChar_t wdxcWdxsFtdCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! WDXC Authentication */
static const attcDiscChar_t wdxcWdxsAu =
{
wdxcAuUuid,
ATTC_SET_UUID_128
};
/*! WDXC Authentication CCC descriptor */
static const attcDiscChar_t wdxcWdxsAuCcc =
{
attCliChCfgUuid,
ATTC_SET_DESCRIPTOR
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *wdxcWdxsDiscCharList[] =
{
&wdxcWdxsDc, /*! WDXC Device Configuration */
&wdxcWdxsDcCcc, /*! WDXC Device Configuration CCC descriptor */
&wdxcWdxsFtc, /*! WDXC File Transfer Control */
&wdxcWdxsFtcCcc, /*! WDXC File Transfer Control CCC descriptor */
&wdxcWdxsFtd, /*! WDXC File Transfer Data */
&wdxcWdxsFtdCcc, /*! WDXC File Transfer Data CCC descriptor */
&wdxcWdxsAu, /*! WDXC Authentication */
&wdxcWdxsAuCcc, /*! WDXC Authentication CCC descriptor */
};
/*! sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(WDXC_HDL_LIST_LEN == ((sizeof(wdxcWdxsDiscCharList) / sizeof(attcDiscChar_t *))));
const uint8_t wdxcSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(WDX_SVC_UUID)};
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Wireless Data Exchange service.
* Parameter pHdlList must point to an array of length WDXC_WDX_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void WdxcWdxsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
wdxcConnCb_t *pConnCb = &wdxcCb.conn[connId - 1];
/* Store pointer to the attribute handles in the control block */
pConnCb->pHdlList = pHdlList;
/* Perform service discovery */
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) wdxcSvcUuid,
WDXC_HDL_LIST_LEN, (attcDiscChar_t **) wdxcWdxsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Perform File Discovery
*
* \param connId Connection ID.
* \param pFileInfo Buffer to hold information about files
* \param maxFiles Size of pFileInfo in number of wsfEfsFileInfo_t objects
*
* \note When discovery is complete, the ftcCallback will be called with op equal to
* WDX_FTC_OP_EOF and the file handle equal to WDX_FLIST_HANDLE.
*
* \return None.
*/
/*************************************************************************************************/
void WdxcDiscoverFiles(dmConnId_t connId, wsfEfsFileInfo_t *pFileInfo, uint8_t maxFiles)
{
wdxcConnCb_t *pConnCb = &wdxcCb.conn[connId - 1];
/* Verify WDXC is Idle */
if (pConnCb->fileHdl == WSF_EFS_INVALID_HANDLE)
{
uint16_t len = maxFiles * WDX_FLIST_RECORD_SIZE + WDX_FLIST_HDR_SIZE;
/* Update control information */
pConnCb->pFileList = pFileInfo;
pConnCb->fileCount = 0;
pConnCb->maxFiles = maxFiles;
pConnCb->fileHdl = WDX_FLIST_HANDLE;
pConnCb->fDlPos = 0;
/* Perform a get on file zero (the file list handle) where the length of the get is
* the most file information pFileInfo can hold */
WdxcFtcSendGetReq(connId, WDX_FLIST_HANDLE, 0, len, 0);
}
}
/*************************************************************************************************/
/*!
* \brief Parse File Listing FTD data
*
* \param pValue FTD Data.
* \param len Size of pValue in bytes
*
* \return None.
*/
/*************************************************************************************************/
static void wdxsParseFileList(dmConnId_t connId, uint8_t *pValue, uint16_t len)
{
wdxcConnCb_t *pConnCb = &wdxcCb.conn[connId - 1];
uint16_t pos = 0;
/* Depending on the MTU, blocks of FTD data from the file list may end mid-value.
* Maintain a global position called wdxcCb.fDlPos, and process FTD data byte by byte */
while (pos < len)
{
if (pConnCb->fDlPos < WDX_FLIST_HDR_SIZE)
{
/* Ignore file list header */
}
else
{
/* Find the file index and the position within the file index (the mark) */
uint8_t mark = (pConnCb->fDlPos - WDX_FLIST_HDR_SIZE) % WDX_FLIST_RECORD_SIZE;
uint8_t file = (pConnCb->fDlPos - WDX_FLIST_HDR_SIZE) / WDX_FLIST_RECORD_SIZE;
wsfEfsFileInfo_t *pInfo;
/* Ignore data if there is insufficient space in pConnCb->pFileList */
if (file >= pConnCb->maxFiles)
{
return;
}
pInfo = &pConnCb->pFileList[file];
/* Process a byte of data */
switch (mark)
{
case 0: pConnCb->fileCount++; pInfo->handle = pValue[pos]; break;
case 1: pInfo->handle |= ((uint16_t)pValue[pos]) << 8; break;
case 2: pInfo->attributes.type = pValue[pos]; break;
case 3: pInfo->attributes.permissions = pValue[pos]; break;
case 4: pInfo->size = pValue[pos]; break;
case 5: pInfo->size |= ((uint32_t)pValue[pos]) << 8; break;
case 6: pInfo->size |= ((uint32_t)pValue[pos]) << 16; break;
case 7: pInfo->size |= ((uint32_t)pValue[pos]) << 24; break;
default:
if (mark > 7 && mark < 8 + WSF_EFS_NAME_LEN)
{
pInfo->attributes.name[mark - 8] = pValue[pos];
}
else
{
pInfo->attributes.version[mark - (8 + WSF_EFS_NAME_LEN)] = pValue[pos];
}
break;
}
}
pConnCb->fDlPos++;
pos++;
}
}
/*************************************************************************************************/
/*!
* \brief Parse a file transfer control message.
*
* \param connId Connection identifier.
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
static void wdxcParseFtc(dmConnId_t connId, uint8_t *pValue, uint16_t len)
{
/* Notification on a File Transfer Control (FTC) Attribute */
wdxcConnCb_t *pConnCb = &wdxcCb.conn[connId - 1];
uint8_t *p = pValue;
uint8_t op, status;
uint16_t handle;
BSTREAM_TO_UINT8(op, p);
BSTREAM_TO_UINT16(handle, p);
if (op == WDX_FTC_OP_ABORT || op == WDX_FTC_OP_EOF)
{
pConnCb->fileHdl = WSF_EFS_INVALID_HANDLE;
status = WDX_FTC_ST_SUCCESS;
}
else
{
BSTREAM_TO_UINT8(status, p)
}
/* Call appliation callback */
if (wdxcCb.pFtdCallback)
{
(*wdxcCb.pFtcCallback)(connId, handle, op, status);
}
}
/*************************************************************************************************/
/*!
* \brief Parse a wdxc file transfer data message.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
static void wdxcParseFtd(dmConnId_t connId, uint8_t *pValue, uint16_t len)
{
wdxcConnCb_t *pConnCb = &wdxcCb.conn[connId - 1];
if (pConnCb->fileHdl == WDX_FLIST_HANDLE)
{
/* File Discovery is in progress */
wdxsParseFileList(connId, pValue, len);
}
else if (pConnCb->fileHdl != WSF_EFS_INVALID_HANDLE)
{
/* Notify application of File Transfer Data (FTD) */
if (wdxcCb.pFtdCallback)
{
(*wdxcCb.pFtdCallback)(connId, pConnCb->fileHdl, len, pValue);
}
}
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length WDXC_WDX_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
static uint8_t wdxcWdxsValueUpdate(attEvt_t *pMsg)
{
uint16_t *pHdlList = wdxcCb.conn[pMsg->hdr.param-1].pHdlList;
uint8_t status = ATT_SUCCESS;
/* device configuration */
if (pMsg->handle == pHdlList[WDXC_DC_HDL_IDX])
{
/* Not Supported */
}
/* file transfer control */
else if (pMsg->handle == pHdlList[WDXC_FTC_HDL_IDX])
{
APP_TRACE_INFO0("WDXC file transfer control.");
wdxcParseFtc((dmConnId_t) pMsg->hdr.param, pMsg->pValue, pMsg->valueLen);
}
/* file transfer data */
else if (pMsg->handle == pHdlList[WDXC_FTD_HDL_IDX])
{
wdxcParseFtd((dmConnId_t) pMsg->hdr.param, pMsg->pValue, pMsg->valueLen);
}
/* authentication */
else if (pMsg->handle == pHdlList[WDXC_AU_HDL_IDX])
{
/* Not Supported */
}
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Called by application to notify the WDXC of System Events.
*
* \param pEvt Pointer to the Event
*
* \return None.
*/
/*************************************************************************************************/
void WdxcProcMsg(wsfMsgHdr_t *pEvt)
{
switch (pEvt->event)
{
case ATTC_HANDLE_VALUE_NTF:
wdxcWdxsValueUpdate((attEvt_t *)pEvt);
break;
case DM_CONN_CLOSE_IND:
{
wdxcConnCb_t *pConnCb = &wdxcCb.conn[pEvt->param - 1];
/* Set file handle to invalid to indicate no file operation is in progress */
pConnCb->fileHdl = WSF_EFS_INVALID_HANDLE;
}
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Initialize the WDXC.
*
* \param pFtdCallback File transfer data callback
* \param pFtcCallback File transfer control callback
*
* \return None.
*/
/*************************************************************************************************/
void WdxcInit(WdxcFtdCallback_t *pFtdCallback, WdxcFtcCallback_t *pFtcCallback)
{
uint8_t i;
APP_TRACE_INFO0("WDXC: WdxcHandlerInit");
/* Initialize the control block */
memset(&wdxcCb, 0, sizeof(wdxcCb));
for (i = 0; i < DM_CONN_MAX; i++)
{
wdxcCb.conn[i].fileHdl = WSF_EFS_INVALID_HANDLE;
}
/* Store callbacks */
wdxcCb.pFtcCallback = pFtcCallback;
wdxcCb.pFtdCallback = pFtdCallback;
}
@@ -0,0 +1,141 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation.
*
* Copyright (c) 2013-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 WDXC_MAIN_H
#define WDXC_MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup WIRELESS_DATA_EXCHANGE_PROFILE
* \{ */
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief WDXC Connection Control Block */
typedef struct
{
uint16_t *pHdlList; /*!< \brief Attrtibute Handles */
/* Operation in progress Control */
uint16_t fileHdl; /*!< \brief File Handle */
/* File Listing Control */
wsfEfsFileInfo_t *pFileList; /*!< \brief File Listing storage */
uint16_t maxFiles; /*!< \brief Size of pFileList in number of wsfEfsFileInfo_t objects */
uint16_t fileCount; /*!< \brief Number of files on peer device */
uint16_t fDlPos; /*!< \brief Position in the download of file information */
} wdxcConnCb_t;
/*! \brief WDXC Control Block */
typedef struct
{
wdxcConnCb_t conn[DM_CONN_MAX]; /*!< \brief Connection control */
WdxcFtdCallback_t *pFtdCallback; /*!< \brief File Transfer Data Application Callback */
WdxcFtcCallback_t *pFtcCallback; /*!< \brief File Transfer Control Application Callback */
} wdxcCb_t;
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Send a request to abort a wdx operation.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to abort operation on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendAbort(dmConnId_t connId, uint16_t fileHdl);
/*************************************************************************************************/
/*!
* \brief Send a request to erase a file.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to erase on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendEraseFile(dmConnId_t connId, uint16_t fileHdl);
/*************************************************************************************************/
/*!
* \brief Send a request to verify a file.
*
* \param connId Connection ID.
* \param fileHdl Handle of file to verify on peer device
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendVerifyFile(dmConnId_t connId, uint16_t fileHdl);
/*************************************************************************************************/
/*!
* \brief Send a request to put a block of data into a file on the peer device
*
* \param connId Connection ID.
* \param fileHdl Handle of file on peer device
* \param offset The offset from the beginning of the file in bytes
* \param len The number of bytes to put
* \param fileSize The size of the file in bytes
* \param type reserved
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendPutReq(dmConnId_t connId, uint16_t fileHdl, uint32_t offset,
uint32_t len, uint32_t fileSize, uint8_t type);
/*************************************************************************************************/
/*!
* \brief Send a request to perform a get a block of data from a file on the peer device
*
* \param connId Connection ID.
* \param fileHdl Handle of file to verify on peer device
* \param offset The offset from the beginning of the file in bytes
* \param len The number of bytes to get
* \param type reserved
*
* \return None.
*/
/*************************************************************************************************/
void WdxcFtcSendGetReq(dmConnId_t connId, uint16_t fileHdl, uint32_t offset, uint32_t len, uint8_t type);
/*! \} */ /* WIRELESS_DATA_EXCHANGE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* WDXC_MAIN_H */
@@ -0,0 +1,84 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile client - Stream utility functions.
*
* Copyright (c) 2017-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 "util/wstr.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "util/bstream.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "wdx_defs.h"
#include "wdxc_api.h"
#include "wdxc_main.h"
/**************************************************************************************************
External Variables
**************************************************************************************************/
extern wdxcCb_t wdxcCb;
/*************************************************************************************************/
/*!
* \brief Send a request to start a stream of a given file handle on the given connection.
*
* \param connId Connection ID.
* \param fileHdl Handle of the file.
*
* \return None.
*/
/*************************************************************************************************/
void WdxcStreamStart(dmConnId_t connId, uint16_t fileHdl)
{
wdxcConnCb_t *pConnCb = &wdxcCb.conn[connId - 1];
/* Verify WDXC is Idle */
if (pConnCb->fileHdl == WSF_EFS_INVALID_HANDLE)
{
pConnCb->fileHdl = fileHdl;
/* Send a get request with len set the MTU length (offset is ignored for streams) */
WdxcFtcSendGetReq(connId, fileHdl, 0, AttGetMtu(connId), 0);
}
}
/*************************************************************************************************/
/*!
* \brief Stop the active stream.
*
* \return None.
*/
/*************************************************************************************************/
void WdxcStreamStop(dmConnId_t connId)
{
wdxcConnCb_t *pConnCb = &wdxcCb.conn[connId - 1];
/* Send an abort to stop the stream */
WdxcFtcSendAbort(connId, pConnCb->fileHdl);
/* Set file handle to invalid to indicate no file operation is in progress */
pConnCb->fileHdl = WSF_EFS_INVALID_HANDLE;
}
@@ -0,0 +1,163 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile application interface.
*
* Copyright (c) 2013-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 WDXS_API_H
#define WDXS_API_H
#include "att_api.h"
#include "wdx_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup WIRELESS_DATA_EXCHANGE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Size of RAM Media used by the application */
#define WDXS_APP_RAM_MEDIA_SIZE 256
/*! \brief Device Model Name */
#ifndef WDXS_DEVICE_MODEL
#define WDXS_DEVICE_MODEL "WDXS App"
#endif
/*************************************************************************************************/
/*!
* \brief Called at startup to configure WDXS authentication.
*
* \param reqLevel Level of authentication that is required for a client to use WDXS
* \param pKey Authentication key (set to NULL if no authentication is required)
*
* \return None.
*/
/*************************************************************************************************/
void WdxsAuthenticationCfg(bool_t reqLevel, uint8_t *pKey);
/*************************************************************************************************/
/*!
* \brief Handle WSF events for WDXS.
*
* \param event event
* \param pMsg message assiciated with event
*
* \return None.
*/
/*************************************************************************************************/
void WdxsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief WSF Task Initialization for WDXS task.
*
* \param handlerId ID of the WDXS task
*
* \return None.
*/
/*************************************************************************************************/
void WdxsHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief Called by application to notify the WDXS of ATT Events.
*
* \param pEvt Pointer to the ATT Event
*
* \return TRUE if the application should ignore the event, else FALSE.
*/
/*************************************************************************************************/
uint8_t WdxsAttCback(attEvt_t *pEvt);
/*************************************************************************************************/
/*!
* \brief Called by application to notify the WDXS of DM Events.
*
* \param pEvt Pointer to the DM Event
*
* \return None.
*/
/*************************************************************************************************/
void WdxsProcDmMsg(dmEvt_t *pEvt);
/*************************************************************************************************/
/*!
* \brief Set the CCCD index used by the application for WDXS service characteristics.
*
* \param dcCccIdx Device Control CCCD index.
* \param auCccIdx Authentication CCCD index.
* \param ftcCccIdx File Transfer Control CCCD index.
* \param ftdCccIdx File Transfer Data CCCD index.
*
* \return None.
*/
/*************************************************************************************************/
void WdxsSetCccIdx(uint8_t dcCccIdx, uint8_t auCccIdx, uint8_t ftcCccIdx, uint8_t ftdCccIdx);
/*************************************************************************************************/
/*!
* \brief Registers the platform dependent Flash Media with the Embedded File System (EFS)
*
* \return None.
*/
/*************************************************************************************************/
void WdxsFlashMediaInit(void);
/*************************************************************************************************/
/*!
* \brief Registers the platform dependent OTA Media with the Embedded File System (EFS)
*
* \return None.
*/
/*************************************************************************************************/
void WdxsOtaMediaInit(void);
/*************************************************************************************************/
/*!
* \brief Resets the system.
*
* \return None.
*/
/*************************************************************************************************/
void WdxsResetSystem(void);
/*************************************************************************************************/
/*!
* \brief Initialize WDXS Device Configuration PHY.
*
* \return None.
*/
/*************************************************************************************************/
void WdxsPhyInit(void);
/*! \} */ /* WIRELESS_DATA_EXCHANGE_PROFILE */
#ifdef __cplusplus
}
#endif
#endif /* WDXS_API_H */
@@ -0,0 +1,218 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation - Authentication.
*
* Copyright (c) 2013-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 "util/bstream.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "wsf_os.h"
#include "sec_api.h"
#include "svc_wdxs.h"
#include "wdxs_api.h"
#include "wdxs_main.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#if WDXS_AU_ENABLED == TRUE
/* WDXS Authentication Control Block */
wdxsAuCb_t wdxsAuCb;
/*************************************************************************************************/
/*!
* \brief Transmit to authentication characteristic.
*
* \return ATT status.
*/
/*************************************************************************************************/
void wdxsAuSend(dmConnId_t connId)
{
APP_TRACE_INFO0("WDXS: AuSend");
/* if notification enabled */
if (AttsCccEnabled(connId, wdxsCb.auCccIdx))
{
/* send notification */
AttsHandleValueNtf(connId, WDXS_AU_HDL, wdxsAuCb.auMsgLen, wdxsAuCb.auMsgBuf);
wdxsCb.txReadyMask &= ~(WDXS_TX_MASK_AU_BIT | WDXS_TX_MASK_READY_BIT);
wdxsAuCb.authState = WDXS_AU_STATE_WAIT_REPLY;
}
}
/*************************************************************************************************/
/*!
* \brief Called by WDXS event handler when the WSF Sec operation is complete.
*
* \return None.
*/
/*************************************************************************************************/
void WdxsAuSecComplete(secAes_t *pAes)
{
uint8_t *p = wdxsAuCb.auMsgBuf;
/* Record the hash */
memcpy(wdxsAuCb.auHash, pAes->pCiphertext, WDX_AU_HASH_LEN);
/* Build challenge message */
UINT8_TO_BSTREAM(p, WDX_AU_OP_CHALLENGE);
memcpy(p, wdxsAuCb.auRand, WDX_AU_RAND_LEN);
wdxsAuCb.auMsgLen = WDX_AU_RAND_LEN + WDX_AU_HDR_LEN;
/* Update State */
wdxsAuCb.authState = WDXS_AU_STATE_WAIT_REPLY;
/* Indicate TX Ready */
wdxsCb.txReadyMask &= ~(WDXS_TX_MASK_AU_BIT | WDXS_TX_MASK_READY_BIT);
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
}
/*************************************************************************************************/
/*!
* \brief Process a request to start authentication.
*
* \return None.
*/
/*************************************************************************************************/
static uint8_t wdxsAuProcStart(dmConnId_t connId, uint8_t len, uint8_t *pValue)
{
/* Verify parameter length */
if (len != WDX_AU_PARAM_LEN_START)
{
return ATT_ERR_LENGTH;
}
/* Parse parameters */
BSTREAM_TO_UINT8(wdxsAuCb.authLevel, pValue);
BSTREAM_TO_UINT8(wdxsAuCb.authMode, pValue);
/* Generate random number */
SecRand(wdxsAuCb.auRand, WDX_AU_RAND_LEN);
/* Encrypt the random number to create the hash */
SecAes(wdxsAuCb.sessionKey, wdxsAuCb.auRand, wdxsCb.handlerId, connId, WDXS_EVT_AU_SEC_COMPLETE);
/* Update State */
wdxsAuCb.authState = WDXS_AU_STATE_WAIT_SEC;
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a reply to the authentication challenge.
*
* \return None.
*/
/*************************************************************************************************/
static uint8_t wdxsAuProcReply(uint8_t len, uint8_t *pValue)
{
/* Verify parameter length */
if (len != WDX_AU_PARAM_LEN_REPLY)
{
return ATT_ERR_LENGTH;
}
if (wdxsAuCb.authState == WDXS_AU_STATE_WAIT_REPLY)
{
/* Verify [0-7] bytes of cipher text against what was sent by client */
if (memcmp(wdxsAuCb.auHash, pValue, WDX_AU_HASH_LEN) == 0)
{
/* Successful challenge */
wdxsAuCb.authState = WDXS_AU_STATE_AUTHORIZED;
return ATT_SUCCESS;
}
return WDX_AU_ST_AUTH_FAILED;
}
return WDX_AU_ST_INVALID_STATE;
}
/*************************************************************************************************/
/*!
* \brief Process a write to the authentication characteristic.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsAuWrite(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t op;
uint8_t status;
/* Sanity check message length */
if (len < WDX_AU_HDR_LEN)
{
return ATT_ERR_LENGTH;
}
/* Get Operation ID */
BSTREAM_TO_UINT8(op, pValue);
len -= WDX_AU_HDR_LEN;
APP_TRACE_INFO1("WDXS: AuWrite: op=%d", op);
switch (op)
{
case WDX_AU_OP_START:
status = wdxsAuProcStart(connId, (uint8_t) len, pValue);
break;
case WDX_AU_OP_REPLY:
status = wdxsAuProcReply((uint8_t) len, pValue);
break;
default:
status = ATT_ERR_RANGE;
break;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Called at startup to configure WDXS authentication.
*
* \param reqLevel Level of authentication that is required for a client to use WDXS
* \param key Authentication key (set to NULL if no authentication is required)
*
* \return None.
*/
/*************************************************************************************************/
void WdxsAuthenticationCfg(bool_t reqLevel, uint8_t *pKey)
{
wdxsAuCb.reqAuthLevel = reqLevel;
if (pKey)
{
memcpy(wdxsAuCb.sessionKey, pKey, WDX_AU_KEY_LEN);
}
}
#endif /* WDXS_AU_ENABLED */
@@ -0,0 +1,608 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation - Device Configuration.
*
* Copyright (c) 2013-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 "util/wstr.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "util/bstream.h"
#include "svc_wdxs.h"
#include "wdxs_api.h"
#include "wdxs_main.h"
#include "dm_api.h"
#include "app_api.h"
#include "app_hw.h"
#if WDXS_DC_ENABLED == TRUE
/* WDXS Device Configuration Control Block */
wdxsDcCb_t wdxsDcCb;
/*************************************************************************************************/
/*!
* \brief Send device configuration notification
*
* \return None.
*/
/*************************************************************************************************/
void wdxsDcSend(dmConnId_t connId)
{
APP_TRACE_INFO0("WDXS: DcSend");
/* if notification enabled */
if (AttsCccEnabled(connId, wdxsCb.dcCccIdx))
{
/* send notification */
AttsHandleValueNtf(connId, WDXS_DC_HDL, wdxsDcCb.dcMsgLen, wdxsDcCb.dcMsgBuf);
wdxsCb.txReadyMask &= ~(WDXS_TX_MASK_DC_BIT | WDXS_TX_MASK_READY_BIT);
}
}
/*************************************************************************************************/
/*!
* \brief Send update message for connection parameters.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsDcUpdateConnParam(dmConnId_t connId, uint8_t status)
{
uint8_t *p;
/* if update already waiting to be sent */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
return ATT_ERR_IN_PROGRESS;
}
/* build update to global buffer */
p = wdxsDcCb.dcMsgBuf;
UINT8_TO_BSTREAM(p, WDX_DC_OP_UPDATE);
UINT8_TO_BSTREAM(p, WDX_DC_ID_CONN_PARAM);
UINT8_TO_BSTREAM(p, status);
UINT16_TO_BSTREAM(p, wdxsCb.connInterval);
UINT16_TO_BSTREAM(p, wdxsCb.connLatency);
UINT16_TO_BSTREAM(p, wdxsCb.supTimeout);
wdxsDcCb.dcMsgLen = WDX_DC_LEN_CONN_PARAM + WDX_DC_HDR_LEN;
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_DC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Send update message for PHY.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsDcUpdatePhy(dmConnId_t connId, uint8_t status)
{
uint8_t *p;
/* if update already waiting to be sent */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
return ATT_ERR_IN_PROGRESS;
}
/* build update to global buffer */
p = wdxsDcCb.dcMsgBuf;
UINT8_TO_BSTREAM(p, WDX_DC_OP_UPDATE);
UINT8_TO_BSTREAM(p, WDX_DC_ID_PHY);
UINT8_TO_BSTREAM(p, status);
UINT8_TO_BSTREAM(p, wdxsCb.txPhy);
UINT8_TO_BSTREAM(p, wdxsCb.rxPhy);
wdxsDcCb.dcMsgLen = WDX_DC_LEN_PHY + WDX_DC_HDR_LEN;
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_DC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process Update Diagnostics Complete.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsDcUpdateDiagnosticsComplete(dmConnId_t connId)
{
uint8_t *p;
/* if update already waiting to be sent */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
return ATT_ERR_IN_PROGRESS;
}
/* build update to global buffer */
p = wdxsDcCb.dcMsgBuf;
UINT8_TO_BSTREAM(p, WDX_DC_OP_UPDATE);
UINT8_TO_BSTREAM(p, WDX_DC_ID_DIAGNOSTICS_COMPLETE);
wdxsDcCb.dcMsgLen = WDX_DC_LEN_DIAG_COMPLETE + WDX_DC_HDR_LEN;
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_DC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process set connection paramter request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcSetConnParamReq(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
hciConnSpec_t connSpec;
/* verify parameter length */
if (len != WDX_DC_LEN_CONN_PARAM_REQ)
{
return ATT_ERR_LENGTH;
}
/* parse parameters */
BSTREAM_TO_UINT16(connSpec.connIntervalMin, pValue);
BSTREAM_TO_UINT16(connSpec.connIntervalMax, pValue);
BSTREAM_TO_UINT16(connSpec.connLatency, pValue);
BSTREAM_TO_UINT16(connSpec.supTimeout, pValue);
/* request update to connection parameters */
DmConnUpdate(connId, &connSpec);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process set diagnostics.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcSetEnterDiadnostics(dmConnId_t connId)
{
return wdxsDcUpdateDiagnosticsComplete(connId);
}
/*************************************************************************************************/
/*!
* \brief Process a Set Disconnect request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcSetDisconnectReq(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
AppConnClose(connId);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a Set Security request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcSetSecurityReq(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t secLevel;
/* verify parameter length */
if (len != WDX_DC_LEN_SEC_LEVEL)
{
return ATT_ERR_LENGTH;
}
/* parse parameters */
BSTREAM_TO_UINT8(secLevel, pValue);
/* Enable Security */
if (DmConnSecLevel(connId) != DM_SEC_LEVEL_NONE)
{
DmSecSlaveReq(connId, secLevel);
}
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a Set Service Changed request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcSetServiceChanged(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
/* TBD */
return ATT_ERR_NOT_SUP;
}
/*************************************************************************************************/
/*!
* \brief Process a Set Delete Bonds request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcSetDeleteBonds(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
/* TBD */
return ATT_ERR_NOT_SUP;
}
/*************************************************************************************************/
/*!
* \brief Process a Set Disconnect And Reset request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcSetDisconnectAndReset(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
wdxsDcCb.doReset = TRUE;
AppConnClose(connId);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a Get Connection Parameter request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcGetConnParam(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
return wdxsDcUpdateConnParam(connId, HCI_SUCCESS);
}
/*************************************************************************************************/
/*!
* \brief Process a Get Security Level request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcGetSecurityLevel(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t *p;
/* if update already waiting to be sent */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
return ATT_ERR_IN_PROGRESS;
}
/* build update to global buffer */
p = wdxsDcCb.dcMsgBuf;
UINT8_TO_BSTREAM(p, WDX_DC_OP_UPDATE);
UINT8_TO_BSTREAM(p, WDX_DC_ID_CONN_SEC_LEVEL);
UINT8_TO_BSTREAM(p, DmConnSecLevel(connId));
wdxsDcCb.dcMsgLen = WDX_DC_LEN_SEC_LEVEL + WDX_DC_HDR_LEN;
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_DC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a Get Current ATT MTU request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcGetAttMtu(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t *p;
/* if update already waiting to be sent */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
return ATT_ERR_IN_PROGRESS;
}
/* build update to global buffer */
p = wdxsDcCb.dcMsgBuf;
UINT8_TO_BSTREAM(p, WDX_DC_OP_UPDATE);
UINT8_TO_BSTREAM(p, WDX_DC_ID_ATT_MTU);
UINT16_TO_BSTREAM(p, AttGetMtu(connId));
wdxsDcCb.dcMsgLen = WDX_DC_LEN_ATT_MTU + WDX_DC_HDR_LEN;
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_DC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a Get Battery Level request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcGetBatteryLevel(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t *p;
/* if update already waiting to be sent */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
return ATT_ERR_IN_PROGRESS;
}
/* build update to global buffer */
p = wdxsDcCb.dcMsgBuf;
UINT8_TO_BSTREAM(p, WDX_DC_OP_UPDATE);
UINT8_TO_BSTREAM(p, WDX_DC_ID_BATTERY_LEVEL);
/* add battery level */
AppHwBattRead(p);
wdxsDcCb.dcMsgLen = WDX_DC_LEN_BATTERY_LEVEL + WDX_DC_HDR_LEN;
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_DC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a Get Device Model Number request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcGetDeviceModel(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t *p;
/* TODO: Add Device Model */
char *pModelTxt = WDXS_DEVICE_MODEL;
/* if update already waiting to be sent */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
return ATT_ERR_IN_PROGRESS;
}
/* build update to global buffer */
p = wdxsDcCb.dcMsgBuf;
UINT8_TO_BSTREAM(p, WDX_DC_OP_UPDATE);
UINT8_TO_BSTREAM(p, WDX_DC_ID_MODEL_NUMBER);
/* Potential buffer overrun is intentional to zero out fixed length field */
/* coverity[overrun-buffer-arg] */
WstrnCpy((char *)p, pModelTxt, WDX_DC_LEN_DEVICE_MODEL);
wdxsDcCb.dcMsgLen = WDX_DC_LEN_DEVICE_MODEL + WDX_DC_HDR_LEN;
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_DC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a Get Firmware Revision request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcGetFirmwareRev(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t *p;
/* TODO: Add Firmware Revision */
char *pFirmwareRev = "1.0";
/* if update already waiting to be sent */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
return ATT_ERR_IN_PROGRESS;
}
/* build update to global buffer */
p = wdxsDcCb.dcMsgBuf;
UINT8_TO_BSTREAM(p, WDX_DC_OP_UPDATE);
UINT8_TO_BSTREAM(p, WDX_DC_ID_FIRMWARE_REV);
/* Potential buffer overrun is intentional to zero out fixed length field */
/* coverity[overrun-buffer-arg] */
WstrnCpy((char *)p, pFirmwareRev, WDX_DC_LEN_FIRMWARE_REV);
wdxsDcCb.dcMsgLen = WDX_DC_LEN_FIRMWARE_REV + WDX_DC_HDR_LEN;
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_DC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a write to the device configuration characteristic.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsDcWrite(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t op;
uint8_t id;
uint8_t status = ATT_SUCCESS;
/* sanity check on message length */
if (len < WDX_DC_HDR_LEN)
{
return ATT_ERR_LENGTH;
}
/* verify notifications are enabled */
if (!AttsCccEnabled(connId, wdxsCb.dcCccIdx))
{
return ATT_ERR_CCCD;
}
/* get operation and parameter ID */
BSTREAM_TO_UINT8(op, pValue);
BSTREAM_TO_UINT8(id, pValue);
/* skip over header (note pValue was incremented above) */
len -= WDX_DC_HDR_LEN;
/* set operation */
if (op == WDX_DC_OP_SET)
{
switch (id)
{
case WDX_DC_ID_CONN_UPDATE_REQ:
status = wdxsDcSetConnParamReq(connId, len, pValue);
break;
case WDX_DC_ID_DISCONNECT_REQ:
status = wdxsDcSetDisconnectReq(connId, len, pValue);
break;
case WDX_DC_ID_SECURITY_REQ:
status = wdxsDcSetSecurityReq(connId, len, pValue);
break;
case WDX_DC_ID_SERVICE_CHANGED:
status = wdxsDcSetServiceChanged(connId, len, pValue);
break;
case WDX_DC_ID_DELETE_BONDS:
status = wdxsDcSetDeleteBonds(connId, len, pValue);
break;
case WDX_DC_ID_ENTER_DIAGNOSTICS:
status = wdxsDcSetEnterDiadnostics(connId);
break;
case WDX_DC_ID_DISCONNECT_AND_RESET:
status = wdxsDcSetDisconnectAndReset(connId, len, pValue);
break;
case WDX_DC_ID_PHY_UPDATE_REQ:
/* if device configuration phy callback registered */
if (wdxsDcCb.phyWriteCback != NULL)
{
status = (*wdxsDcCb.phyWriteCback)(connId, op, id, len, pValue);
break;
}
/* else fall through */
default:
status = ATT_ERR_RANGE;
break;
}
}
/* get operation */
else if (op == WDX_DC_OP_GET)
{
switch (id)
{
case WDX_DC_ID_CONN_PARAM:
status = wdxsDcGetConnParam(connId, len, pValue);
break;
case WDX_DC_ID_CONN_SEC_LEVEL:
status = wdxsDcGetSecurityLevel(connId, len, pValue);
break;
case WDX_DC_ID_ATT_MTU:
status = wdxsDcGetAttMtu(connId, len, pValue);
break;
case WDX_DC_ID_BATTERY_LEVEL:
status = wdxsDcGetBatteryLevel(connId, len, pValue);
break;
case WDX_DC_ID_MODEL_NUMBER:
status = wdxsDcGetDeviceModel(connId, len, pValue);
break;
case WDX_DC_ID_FIRMWARE_REV:
status = wdxsDcGetFirmwareRev(connId, len, pValue);
break;
case WDX_DC_ID_PHY:
/* if device configuration phy callback registered */
if (wdxsDcCb.phyWriteCback != NULL)
{
status = (*wdxsDcCb.phyWriteCback)(connId, op, id, len, pValue);
break;
}
/* else fall through */
default:
status = ATT_ERR_RANGE;
break;
}
}
else
{
status = ATT_ERR_RANGE;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Register a PHY write callback for the device configuration characteristic.
*
* \param cback PHY callback function.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsDcPhyRegister(wdxsDcPhyWriteCback_t cback)
{
wdxsDcCb.phyWriteCback = cback;
}
#endif /* WDXS_DC_ENABLED */
@@ -0,0 +1,518 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation - File Transfer.
*
* Copyright (c) 2013-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 <stddef.h>
#include "wsf_types.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "util/bstream.h"
#include "svc_wdxs.h"
#include "wdxs_api.h"
#include "wdxs_main.h"
#include "dm_api.h"
#include "app_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Read data from file.
*
* \return TRUE if EOF.
*/
/*************************************************************************************************/
static bool_t wdxsFileRead(uint16_t handle, uint32_t offset, uint32_t *pReadLen, uint8_t *pData)
{
bool_t eof = FALSE;
uint32_t fileSize = WsfEfsGetFileSize(handle);
if (fileSize && (offset + *pReadLen > fileSize))
{
*pReadLen = fileSize - offset;
eof = TRUE;
}
WsfEfsGet(handle, offset, pData, (uint16_t) *pReadLen);
return eof;
}
/*************************************************************************************************/
/*!
* \brief Prepare for FTD data.
*
* \return None.
*/
/*************************************************************************************************/
static uint8_t wdxsInitializeForPut(dmConnId_t connId, uint16_t handle)
{
uint32_t availableSize = WsfEfsGetFileMaxSize(handle);
/* verify file total length
* verify offset+length is not more than total length
*/
if ((wdxsCb.ftTotalLen > availableSize) ||
((wdxsCb.ftOffset + wdxsCb.ftLen) > wdxsCb.ftTotalLen))
{
return WDX_FTC_ST_INVALID_OP_DATA;
}
/* Erase on offset of zero */
if (wdxsCb.ftOffset == 0)
{
WsfEfsErase(handle);
}
/* set up file put operation */
wdxsCb.ftHandle = handle;
wdxsCb.ftInProgress = WDX_FTC_OP_PUT_REQ;
return WDX_FTC_ST_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Send a file transfer control characteristic notification.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsFtcSend(dmConnId_t connId)
{
APP_TRACE_INFO0("WDXS: FTC Send");
/* if notification enabled */
if (AttsCccEnabled(connId, wdxsCb.ftcCccIdx))
{
/* send notification */
AttsHandleValueNtf(connId, WDXS_FTC_HDL, wdxsCb.ftcMsgLen, wdxsCb.ftcMsgBuf);
wdxsCb.txReadyMask &= ~(WDXS_TX_MASK_FTC_BIT | WDXS_TX_MASK_READY_BIT);
}
}
/*************************************************************************************************/
/*!
* \brief Send a file transfer response message.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsFtcSendRsp(dmConnId_t connId, uint8_t op, uint16_t handle, uint8_t status)
{
uint8_t *p;
/* there should not be another response message set up */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_FTC_BIT)
{
APP_TRACE_WARN0("WDXS: FTC message overflow");
return;
}
APP_TRACE_INFO3("WDXS: FTC SendRsp op=%d handle=%d status=%d", op, handle, status);
/* build message */
p = wdxsCb.ftcMsgBuf;
UINT8_TO_BSTREAM(p, op);
UINT16_TO_BSTREAM(p, handle);
if (op != WDX_FTC_OP_ABORT && op != WDX_FTC_OP_EOF)
{
UINT8_TO_BSTREAM(p, status);
}
if (op == WDX_FTC_OP_GET_RSP || op == WDX_FTC_OP_PUT_RSP)
{
UINT8_TO_BSTREAM(p, WDX_FTC_TRANSPORT_TYPE);
UINT16_TO_BSTREAM(p, WDX_FTC_TRANSPORT_ID);
}
wdxsCb.ftcMsgLen = (uint16_t) (p - wdxsCb.ftcMsgBuf);
/* Indicate TX Ready */
wdxsCb.txReadyMask |= WDXS_TX_MASK_FTC_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
}
/*************************************************************************************************/
/*!
* \brief Process a file get request.
*
* \return None.
*/
/*************************************************************************************************/
static void wdxsFtcProcGetReq(dmConnId_t connId, uint16_t handle, uint16_t len, uint8_t *pValue)
{
uint8_t status;
APP_TRACE_INFO2("WDXS: FTC GetReq handle=%d len=%d", handle, len);
/* verify operation not already in progress */
if (wdxsCb.ftInProgress != WDX_FTC_OP_NONE)
{
status = WDX_FTC_ST_IN_PROGRESS;
}
else if ((WsfEfsGetFilePermissions(handle) & WSF_EFS_REMOTE_GET_PERMITTED) == 0)
{
status = WDX_FTC_ST_INVALID_OP_FILE;
}
else
{
if (handle == WDX_FLIST_HANDLE)
{
WdxsUpdateListing();
}
/* parse operation data */
BSTREAM_TO_UINT32(wdxsCb.ftOffset, pValue);
BSTREAM_TO_UINT32(wdxsCb.ftLen, pValue);
BSTREAM_TO_UINT8(wdxsCb.ftPrefXferType, pValue);
/* set up file get operation */
wdxsCb.ftHandle = handle;
wdxsCb.ftInProgress = WDX_FTC_OP_GET_REQ;
wdxsCb.txReadyMask |= WDXS_TX_MASK_FTD_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
status = WDX_FTC_ST_SUCCESS;
}
/* send response */
wdxsFtcSendRsp(connId, WDX_FTC_OP_GET_RSP, handle, status);
}
/*************************************************************************************************/
/*!
* \brief Process a file put request.
*
* \return None.
*/
/*************************************************************************************************/
static void wdxsFtcProcPutReq(dmConnId_t connId, uint16_t handle, uint16_t len, uint8_t *pValue)
{
uint8_t status;
/* verify operation not already in progress */
if (wdxsCb.ftInProgress != WDX_FTC_OP_NONE)
{
status = WDX_FTC_ST_IN_PROGRESS;
}
/* verify permissions */
else if ((WsfEfsGetFilePermissions(handle) & WSF_EFS_REMOTE_PUT_PERMITTED) == 0)
{
status = WDX_FTC_ST_INVALID_HANDLE;
}
else
{
/* parse operation data */
wdxsCb.ftHandle = handle;
BSTREAM_TO_UINT32(wdxsCb.ftOffset, pValue);
BSTREAM_TO_UINT32(wdxsCb.ftLen, pValue);
BSTREAM_TO_UINT32(wdxsCb.ftTotalLen, pValue);
BSTREAM_TO_UINT8(wdxsCb.ftPrefXferType, pValue);
APP_TRACE_INFO3("WDXS: FTC PutReq handle=%d offset=%d, len=%d", handle, wdxsCb.ftOffset, wdxsCb.ftLen);
/* Initialize transfer*/
status = wdxsInitializeForPut(connId, handle);
}
APP_TRACE_INFO2("WDXS: FTC PutReq handle=%d status=%d", handle, status);
/* send response */
wdxsFtcSendRsp(connId, WDX_FTC_OP_PUT_RSP, handle, status);
}
/*************************************************************************************************/
/*!
* \brief Process a file verify request.
*
* \return None.
*/
/*************************************************************************************************/
static void wdxsFtcProcVerifyReq(dmConnId_t connId, uint16_t handle)
{
uint8_t status;
APP_TRACE_INFO1("WDXS: FTC VerifyReq: handle=%d", handle);
/* verify operation not already in progress */
if (wdxsCb.ftInProgress != WDX_FTC_OP_NONE)
{
status = WDX_FTC_ST_IN_PROGRESS;
}
else if ((WsfEfsGetFilePermissions(handle) & WSF_EFS_REMOTE_VERIFY_PERMITTED) == 0)
{
status = WDX_FTC_ST_INVALID_HANDLE;
}
else
{
/* Call the media specific validate command */
status = WsfEfsMediaSpecificCommand(handle, WSF_EFS_VALIDATE_CMD, 0);
}
/* send response */
wdxsFtcSendRsp(connId, WDX_FTC_OP_VERIFY_RSP, handle, status);
}
/*************************************************************************************************/
/*!
* \brief Process a file erase request.
*
* \return None.
*/
/*************************************************************************************************/
static void wdxsFtcProcEraseReq(dmConnId_t connId, uint16_t handle)
{
uint8_t status = WDX_FTC_ST_SUCCESS;
APP_TRACE_INFO1("WDXS: FTC EraseReq: handle=%d", handle);
/* verify operation not already in progress */
if (wdxsCb.ftInProgress != WDX_FTC_OP_NONE)
{
status = WDX_FTC_ST_IN_PROGRESS;
}
/* verify file handle */
else if ((WsfEfsGetFilePermissions(handle) & WSF_EFS_REMOTE_ERASE_PERMITTED) == 0)
{
status = WDX_FTC_ST_INVALID_OP_FILE;
}
else
{
/* do file erase */
WsfEfsErase(handle);
}
/* send response */
wdxsFtcSendRsp(connId, WDX_FTC_OP_ERASE_RSP, handle, status);
}
/*************************************************************************************************/
/*!
* \brief Process a file abort.
*
* \return None.
*/
/*************************************************************************************************/
static void wdxsFtcProcAbort(dmConnId_t connId, uint16_t handle)
{
APP_TRACE_INFO1("WDXS: FTC AbortReq: handle=%d", handle);
if (wdxsCb.ftInProgress != WDX_FTC_OP_NONE)
{
/* abort operation */
if (WsfEfsGetFileType(handle) == WSF_EFS_FILE_TYPE_STREAM)
{
wdxsCb.ftInProgress = WDX_FTC_OP_ABORT;
}
else
{
wdxsCb.ftInProgress = WDX_FTC_OP_NONE;
}
wdxsCb.ftLen = 0;
wdxsCb.ftOffset = 0;
}
}
/*************************************************************************************************/
/*!
* \brief Process a write to the file transfer data characteristic.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsFtdWrite(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
/* verify put operation in progress */
if (wdxsCb.ftInProgress != WDX_FTC_OP_PUT_REQ)
{
return ATT_ERR_UNLIKELY;
}
/* verify data length */
if (len <= WDX_FTD_HDR_LEN)
{
return ATT_ERR_LENGTH;
}
/* verify more data is expected */
if (wdxsCb.ftLen >= len)
{
WsfEfsPut(wdxsCb.ftHandle, wdxsCb.ftOffset, pValue, len);
/* update remaining length of put request */
wdxsCb.ftOffset += len;
wdxsCb.ftLen -= len;
/* if end of put req reached */
if (wdxsCb.ftLen == 0)
{
if (wdxsCb.ftOffset == wdxsCb.ftTotalLen)
{
/* Call the media specific WDXS Put Complete command */
WsfEfsMediaSpecificCommand(wdxsCb.ftHandle, WSF_EFS_WDXS_PUT_COMPLETE_CMD, wdxsCb.ftTotalLen);
}
/* put req done */
wdxsCb.ftInProgress = WDX_FTC_OP_NONE;
/* send eof */
wdxsFtcSendRsp(connId, WDX_FTC_OP_EOF, wdxsCb.ftHandle, 0);
}
}
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a write to the file transfer control characteristic.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsFtcWrite(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t op;
uint16_t handle;
APP_TRACE_INFO1("WDXS: FTC Write: len=%d", len);
/* sanity check on message length */
if (len < WDX_FTC_HDR_LEN + WDX_FTC_HANDLE_LEN)
{
return ATT_ERR_LENGTH;
}
/* get operation and file handle */
BSTREAM_TO_UINT8(op, pValue);
BSTREAM_TO_UINT16(handle, pValue);
APP_TRACE_INFO2("WDXS: FTC Write: op=%d handle=%d", op, handle);
len -= WDX_FTC_HANDLE_LEN + WDX_FTC_HDR_LEN;
switch (op)
{
case WDX_FTC_OP_GET_REQ:
wdxsFtcProcGetReq(connId, handle, len, pValue);
break;
case WDX_FTC_OP_PUT_REQ:
wdxsFtcProcPutReq(connId, handle, len, pValue);
break;
case WDX_FTC_OP_VERIFY_REQ:
wdxsFtcProcVerifyReq(connId, handle);
break;
case WDX_FTC_OP_ERASE_REQ:
wdxsFtcProcEraseReq(connId, handle);
break;
case WDX_FTC_OP_ABORT:
wdxsFtcProcAbort(connId, handle);
break;
default:
break;
}
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Send a file transfer data characteristic notification.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsFtdSend(dmConnId_t connId)
{
/* if notification enabled */
if (AttsCccEnabled(connId, wdxsCb.ftdCccIdx))
{
uint8_t *pBuf;
uint32_t readLen = AttGetMtu(connId) - ATT_VALUE_NTF_LEN;
bool_t eof;
uint8_t fileType = WsfEfsGetFileType(wdxsCb.ftHandle);
/* Check for abort when Streaming */
if ((fileType == WSF_EFS_FILE_TYPE_STREAM) && (wdxsCb.ftInProgress == WDX_FTC_OP_ABORT))
{
eof = TRUE;
readLen = 0;
}
else
{
eof = FALSE;
}
readLen = (readLen < wdxsCb.ftLen) ? readLen : wdxsCb.ftLen;
if (readLen && (pBuf = AttMsgAlloc((uint16_t)readLen, ATT_PDU_VALUE_NTF)) != NULL)
{
/* read data from file */
eof = wdxsFileRead(wdxsCb.ftHandle, wdxsCb.ftOffset, &readLen, pBuf);
if (readLen > 0)
{
/* update stored offset and length (non-streaming file) */
if (fileType == WSF_EFS_FILE_TYPE_BULK)
{
wdxsCb.ftLen -= readLen;
wdxsCb.ftOffset += readLen;
}
/* send notification */
AttsHandleValueNtfZeroCpy(connId, WDXS_FTD_HDL, (uint16_t)readLen, pBuf);
wdxsCb.txReadyMask &= ~(WDXS_TX_MASK_READY_BIT);
}
else
{
AttMsgFree(pBuf, ATT_PDU_VALUE_NTF);
}
}
/* check if end of transfer reached */
if (wdxsCb.ftLen == 0 || readLen == 0 || eof || wdxsCb.ftInProgress == WDX_FTC_OP_ABORT)
{
wdxsCb.ftInProgress = WDX_FTC_OP_NONE;
wdxsCb.txReadyMask &= ~(WDXS_TX_MASK_FTD_BIT);
}
if (eof)
{
/* send EOF */
wdxsFtcSendRsp(connId, WDX_FTC_OP_EOF, wdxsCb.ftHandle, 0);
}
}
}
@@ -0,0 +1,539 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation.
*
* Copyright (c) 2013-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 "util/wstr.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "wsf_os.h"
#include "sec_api.h"
#include "util/bstream.h"
#include "svc_wdxs.h"
#include "wdxs_api.h"
#include "wdxs_main.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! application control block */
wdxsCb_t wdxsCb;
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! RAM File Media Configuration */
#define WDXS_RAM_LOCATION ((uint32_t)WdxsRamBlock)
#define WDXS_RAM_SIZE (WDX_FLIST_MAX_LEN + WDXS_APP_RAM_MEDIA_SIZE)
#define WDXS_RAM_END (WDXS_RAM_LOCATION + WDXS_RAM_SIZE)
/**************************************************************************************************
Local Function Prototypes
**************************************************************************************************/
static uint8_t WdxsRamErase(uint32_t address, uint32_t size);
static uint8_t WdxsRamRead(uint8_t *pBuf, uint32_t address, uint32_t size);
static uint8_t WdxsRamWrite(const uint8_t *pBuf, uint32_t address, uint32_t size);
/**************************************************************************************************
Function Prototypes
**************************************************************************************************/
void WdxsAuSecComplete(secAes_t *pAes);
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! Reserve RAM for use by the RAM EFS Media */
static uint8_t WdxsRamBlock[WDXS_RAM_SIZE];
/*! EFS RAM Media Control Block */
static const wsfEfsMedia_t WDXS_RamMediaCtrl =
{
WDXS_RAM_LOCATION,
WDXS_RAM_LOCATION + WDXS_RAM_SIZE,
1,
NULL,
WdxsRamErase,
WdxsRamRead,
WdxsRamWrite,
NULL
};
/*************************************************************************************************/
/*!
* \brief Erase function for the EFS RAM media.
*
* \return none.
*
*/
/*************************************************************************************************/
static uint8_t WdxsRamErase(uint32_t address, uint32_t size)
{
uint8_t *pMem = (uint8_t *) address;
memset(pMem, 0xFF, size);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Read function for the EFS RAM media.
*
* \return none.
*
*/
/*************************************************************************************************/
static uint8_t WdxsRamRead(uint8_t *pBuf, uint32_t address, uint32_t size)
{
uint8_t *pMem = (uint8_t *) address;
memcpy(pBuf, pMem, size);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Write function for the EFS RAM media.
*
* \return none.
*
*/
/*************************************************************************************************/
static uint8_t WdxsRamWrite(const uint8_t *pBuf, uint32_t address, uint32_t size)
{
uint8_t *pMem = (uint8_t *) address;
memcpy(pMem, pBuf, size);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Format file list information for the given file.
*
* \return none.
*/
/*************************************************************************************************/
static void wdxsFormatFileResource(uint8_t *pData, wsfEfsHandle_t handle)
{
UINT16_TO_BSTREAM(pData, handle);
UINT8_TO_BSTREAM(pData, WsfEfsGetFileType(handle));
UINT8_TO_BSTREAM(pData, WsfEfsGetFilePermissions(handle) & WSF_EFS_REMOTE_PERMISSIONS_MASK);
UINT32_TO_BSTREAM(pData, WsfEfsGetFileSize(handle));
WstrnCpy((char *)pData, WsfEfsGetFileName(handle), WSF_EFS_NAME_LEN);
WstrnCpy((char *)pData+WSF_EFS_NAME_LEN, WsfEfsGetFileVersion(handle), WSF_EFS_VERSION_LEN);
}
/*************************************************************************************************/
/*!
* \brief Create the file list.
*
* \return none.
*/
/*************************************************************************************************/
void WdxsUpdateListing(void)
{
uint8_t *pTmp;
uint8_t header[WDX_FLIST_HDR_SIZE];
uint8_t record[WDX_FLIST_RECORD_SIZE];
uint32_t position = 0, totalSize = 0;
uint32_t fileCount = 0;
uint8_t i;
position = WDX_FLIST_HDR_SIZE;
for (i=0; i<WSF_EFS_MAX_FILES; i++)
{
if (WsfEfsGetFileByHandle(i) && (WsfEfsGetFilePermissions(i) & WSF_EFS_REMOTE_VISIBLE))
{
/* Update the total size and file count */
totalSize += WsfEfsGetFileSize(i);
fileCount++;
wdxsFormatFileResource(record, i);
/* Write the record */
WsfEfsPut(WDX_FLIST_HANDLE, position, record, WDX_FLIST_RECORD_SIZE);
position += WDX_FLIST_RECORD_SIZE;
}
}
/* Add the header after calculating the total_size and file_count */
pTmp = header;
UINT8_TO_BSTREAM(pTmp, WDX_FLIST_FORMAT_VER);
UINT16_TO_BSTREAM(pTmp, fileCount);
UINT32_TO_BSTREAM(pTmp, totalSize);
/* Write the header */
WsfEfsPut(WDX_FLIST_HANDLE, 0, header, WDX_FLIST_HDR_SIZE);
}
/*************************************************************************************************/
/*!
* \brief Set the CCCD index used by the application for WDXS service characteristics.
*
* \param dcCccIdx Device Control CCCD index.
* \param auCccIdx Authentication CCCD index.
* \param ftcCccIdx File Transfer Control CCCD index.
* \param ftdCccIdx File Transfer Data CCCD index.
*
* \return None.
*/
/*************************************************************************************************/
void WdxsSetCccIdx(uint8_t dcCccIdx, uint8_t auCccIdx, uint8_t ftcCccIdx, uint8_t ftdCccIdx)
{
wdxsCb.dcCccIdx = dcCccIdx;
wdxsCb.auCccIdx = auCccIdx;
wdxsCb.ftcCccIdx = ftcCccIdx;
wdxsCb.ftdCccIdx = ftdCccIdx;
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for proprietary service.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
uint8_t status;
#if WDXS_AU_ENABLED == TRUE
/* Require peer authentication before writing to any characteristic
(except for the authentication characteristic) */
if ((wdxsAuCb.reqAuthLevel != WDX_AU_LVL_NONE) && (handle != WDXS_AU_HDL))
{
if ((wdxsAuCb.authState != WDXS_AU_STATE_AUTHORIZED) || (wdxsAuCb.authLevel < wdxsAuCb.reqAuthLevel))
{
APP_TRACE_INFO1("WDXS: WriteCback unauthorized state=%d", wdxsAuCb.authState);
return WDX_APP_AUTH_REQUIRED;
}
}
#endif /* WDXS_AU_ENABLED */
switch (handle)
{
#if WDXS_DC_ENABLED == TRUE
/* Device configuration */
case WDXS_DC_HDL:
status = wdxsDcWrite(connId, len, pValue);
break;
#endif /* WDXS_DC_ENABLED */
/* File transfer control */
case WDXS_FTC_HDL:
status = wdxsFtcWrite(connId, len, pValue);
break;
/* File transfer data */
case WDXS_FTD_HDL:
status = wdxsFtdWrite(connId, len, pValue);
break;
#if WDXS_AU_ENABLED == TRUE
/* Authentication */
case WDXS_AU_HDL:
status = wdxsAuWrite(connId, len, pValue);
break;
#endif /* WDXS_AU_ENABLED */
default:
APP_TRACE_INFO1("WDXS: WriteCback unexpected handle=%d", handle);
status = ATT_ERR_HANDLE;
break;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Process TX data path
*
* \return None.
*/
/*************************************************************************************************/
static void wdxsProcTxPath(void)
{
dmConnId_t connId;
/* Check for a connection */
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
{
/* Check if ready to transmit a message */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_READY_BIT)
{
#if WDXS_DC_ENABLED == TRUE
/* Device configuration */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_DC_BIT)
{
wdxsDcSend(connId);
return;
}
#endif /* WDXS_DC_ENABLED */
/* File Transfer Control */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_FTC_BIT)
{
wdxsFtcSend(connId);
return;
}
#if WDXS_AU_ENABLED == TRUE
/* Authentication */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_AU_BIT)
{
wdxsAuSend(connId);
return;
}
#endif /* WDXS_AU_ENABLED */
/* File Transfer Data */
if (wdxsCb.txReadyMask & WDXS_TX_MASK_FTD_BIT)
{
wdxsFtdSend(connId);
return;
}
}
}
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void WdxsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
APP_TRACE_INFO1("WDXS: Task Handler Evt=%d", event);
if (event & WDXS_EVT_TX_PATH)
{
wdxsProcTxPath();
}
#if WDXS_AU_ENABLED == TRUE
if (event & WDXS_EVT_AU_SEC_COMPLETE)
{
WdxsAuSecComplete((secAes_t*) pMsg);
}
#endif /* WDXS_AU_ENABLED */
}
/*************************************************************************************************/
/*!
* \brief Called by application to notify the WDXS of DM Events.
*
* \param pEvt Pointer to the DM Event
*
* \return None.
*/
/*************************************************************************************************/
void WdxsProcDmMsg(dmEvt_t *pEvt)
{
switch (pEvt->hdr.event)
{
case DM_CONN_CLOSE_IND:
if (wdxsDcCb.doReset)
{
WdxsResetSystem();
}
break;
case DM_CONN_OPEN_IND:
/* Initialize connection parameters */
wdxsCb.txReadyMask = WDXS_TX_MASK_READY_BIT;
wdxsCb.ftInProgress = WDX_FTC_OP_NONE;
wdxsCb.ftLen = 0;
wdxsCb.ftOffset = 0;
#if WDXS_AU_ENABLED == TRUE
wdxsAuCb.authLevel = WDX_AU_LVL_NONE;
wdxsAuCb.authState = WDXS_AU_STATE_UNAUTHORIZED;
#endif /* WDXS_AU_ENABLED */
wdxsCb.connInterval = pEvt->connOpen.connInterval;
wdxsCb.connLatency = pEvt->connOpen.connLatency;
wdxsCb.supTimeout = pEvt->connOpen.supTimeout;
wdxsCb.txPhy= HCI_PHY_LE_1M_BIT;
wdxsCb.rxPhy = HCI_PHY_LE_1M_BIT;
break;
case DM_CONN_UPDATE_IND:
if (pEvt->hdr.status == HCI_SUCCESS)
{
wdxsCb.connInterval = pEvt->connUpdate.connInterval;
wdxsCb.connLatency = pEvt->connUpdate.connLatency;
wdxsCb.supTimeout = pEvt->connUpdate.supTimeout;
}
wdxsDcUpdateConnParam((dmConnId_t) pEvt->hdr.param, pEvt->hdr.status);
break;
case DM_PHY_UPDATE_IND:
if (pEvt->hdr.status == HCI_SUCCESS)
{
wdxsCb.txPhy = pEvt->phyUpdate.txPhy;
wdxsCb.rxPhy = pEvt->phyUpdate.rxPhy;
}
wdxsDcUpdatePhy((dmConnId_t) pEvt->hdr.param, pEvt->hdr.status);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Called by application to notify the WDXS of ATT Events.
*
* \param pEvt Pointer to the ATT Event
*
* \return None.
*/
/*************************************************************************************************/
uint8_t WdxsAttCback(attEvt_t *pEvt)
{
if (pEvt->handle < WDXS_START_HDL || pEvt->handle > WDXS_END_HDL)
{
return FALSE;
}
APP_TRACE_INFO2("WDXS: AttHook handle=%d event=%d", pEvt->handle, pEvt->hdr.event);
/* trigger tx data path on confirm */
if (pEvt->hdr.event == ATTS_HANDLE_VALUE_CNF &&
pEvt->hdr.status == ATT_SUCCESS)
{
wdxsCb.txReadyMask |= WDXS_TX_MASK_READY_BIT;
WsfSetEvent(wdxsCb.handlerId, WDXS_EVT_TX_PATH);
}
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void WdxsHandlerInit(wsfHandlerId_t handlerId)
{
wsfEsfAttributes_t attr;
APP_TRACE_INFO0("WDXS: WdxsHandlerInit");
/* Initialize the control block */
memset(&wdxsCb, 0, sizeof(wdxsCb));
wdxsCb.txReadyMask = WDXS_TX_MASK_READY_BIT;
/* Store Handler ID */
wdxsCb.handlerId = handlerId;
/* Initialize the device configuration control block */
memset(&wdxsDcCb, 0, sizeof(wdxsDcCb));
/* Register the WDXS Service */
SvcWdxsRegister(wdxsWriteCback);
SvcWdxsAddGroup();
/* Initialize the embedded file system */
WsfEfsInit();
/* Register the RAM Media */
memset(WdxsRamBlock, 0xFF, sizeof(WdxsRamBlock));
WsfEfsRegisterMedia(&WDXS_RamMediaCtrl, WDX_RAM_MEDIA);
/* Set attributes for the WDXS File List */
attr.type = WSF_EFS_FILE_TYPE_BULK;
attr.permissions = WSF_EFS_LOCAL_PUT_PERMITTED | WSF_EFS_REMOTE_GET_PERMITTED;
WstrnCpy(attr.name, "Listing", WSF_EFS_NAME_LEN);
WstrnCpy(attr.version, "1.0", WSF_EFS_VERSION_LEN);
/* Create a file in RAM to contain the list WDXS File List */
WsfEfsAddFile(WDX_FLIST_MAX_LEN, WDX_RAM_MEDIA, &attr, WSF_EFS_FILE_OFFSET_ANY);
}
/*************************************************************************************************/
/*!
* \fn WdxsOtaMediaInit
*
* \brief Registers the platform dependent OTA Media with the Embedded File System (EFS)
*
* \param None
*
* \return None.
*/
/*************************************************************************************************/
void WdxsOtaMediaInit(void)
{
}
/*************************************************************************************************/
/*!
* \fn WdxsResetSystem
*
* \brief Resets the system.
*
* \param None
*
* \return None.
*/
/*************************************************************************************************/
void WdxsResetSystem(void)
{
}
/*************************************************************************************************/
/*!
* \fn WdxsFlashMediaInit
*
* \brief Registers the platform dependent Flash Media with the Embedded File System (EFS)
*
* \param None
*
* \return None.
*/
/*************************************************************************************************/
void WdxsFlashMediaInit(void)
{
}
@@ -0,0 +1,327 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation.
*
* Copyright (c) 2013-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 WDXS_MAIN_H
#define WDXS_MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup WIRELESS_DATA_EXCHANGE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/** \name WDXS Default Feature Set
*
*/
/**@{*/
#ifndef WDXS_DC_ENABLED
#define WDXS_DC_ENABLED TRUE
#endif
#ifndef WDXS_AU_ENABLED
#define WDXS_AU_ENABLED TRUE
#endif
#ifndef WDXS_OTA_ENABLED
#define WDXS_OTA_ENABLED TRUE
#endif
/**@}*/
/*! \brief Special length for streaming file */
#define WDXS_STREAM_FILE_LEN 0xFFFFFFFF
/** \name WSF event types for application event handler
*
*/
/**@{*/
#define WDXS_EVT_TX_PATH 0x01 /*!< \brief Trigger tx data path */
#define WDXS_EVT_AU_SEC_COMPLETE 0x02 /*!< \brief AU encryption of challenge ready */
/**@}*/
/** \name TX Ready Mask Bits
*
*/
/**@{*/
#define WDXS_TX_MASK_READY_BIT (1<<0) /*!< \brief Ready bit */
#define WDXS_TX_MASK_DC_BIT (1<<1) /*!< \brief DC bit */
#define WDXS_TX_MASK_FTC_BIT (1<<2) /*!< \brief FTC bit */
#define WDXS_TX_MASK_FTD_BIT (1<<3) /*!< \brief FTD bit */
#define WDXS_TX_MASK_AU_BIT (1<<4) /*!< \brief AU bit */
/**@}*/
/** \name Authentication states
*
*/
/**@{*/
#define WDXS_AU_STATE_UNAUTHORIZED 0x00 /*!< \brief Authentication has not started */
#define WDXS_AU_STATE_HASHING 0x01 /*!< \brief Authentication hash is being calculated */
#define WDXS_AU_STATE_WAIT_SEC 0x02 /*!< \brief Authentication challenge sent */
#define WDXS_AU_STATE_WAIT_REPLY 0x03 /*!< \brief Authentication waiting for challenge reply */
#define WDXS_AU_STATE_AUTHORIZED 0x04 /*!< \brief Authentication completed successfully */
/**@}*/
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! \brief WDXS Device Configuration PHY Write Callback */
typedef uint8_t (*wdxsDcPhyWriteCback_t)(dmConnId_t connId, uint8_t op, uint8_t id, uint16_t len,
uint8_t *pValue);
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! \brief WDXS profile control block */
typedef struct
{
wsfHandlerId_t handlerId; /*!< \brief WSF handler ID */
uint8_t txReadyMask; /*!< \brief Bits indicate DC, FTC, FTD, and/or AU wish to transmit */
/* connection parameters */
uint16_t connInterval; /*!< \brief connection interval */
uint16_t connLatency; /*!< \brief connection latency */
uint16_t supTimeout; /*!< \brief supervision timeout */
/* Phy parameters */
uint8_t txPhy; /*!< \brief transmitter PHY */
uint8_t rxPhy; /*!< \brief receiver PHY */
/* for file transfer */
uint32_t ftOffset; /*!< \brief file data offset */
uint32_t ftLen; /*!< \brief remaining data length for current operation */
uint32_t ftTotalLen; /*!< \brief file total length */
uint16_t ftHandle; /*!< \brief file handle */
uint16_t ftcMsgLen; /*!< \brief message length */
uint8_t ftcMsgBuf[ATT_DEFAULT_PAYLOAD_LEN]; /*!< \brief message buffer */
uint8_t ftInProgress; /*!< \brief operation in progress */
uint8_t ftPrefXferType; /*!< \brief Preferred transport type */
/* ccc index */
uint8_t dcCccIdx; /*!< \brief device configuration ccc index */
uint8_t auCccIdx; /*!< \brief authentication ccc index */
uint8_t ftcCccIdx; /*!< \brief file transfer control ccc index */
uint8_t ftdCccIdx; /*!< \brief file transfer data ccc index */
} wdxsCb_t;
/*! \brief WDXS Device Configuration Control Block */
typedef struct
{
uint16_t dcMsgLen; /*!< \brief message length */
uint8_t dcMsgBuf[ATT_DEFAULT_PAYLOAD_LEN]; /*!< \brief message buffer */
bool_t doReset; /*!< \brief Reset device after disconnect */
wdxsDcPhyWriteCback_t phyWriteCback; /*!< \brief Device config PHY write callback */
} wdxsDcCb_t;
/*! \brief WDXS Authentication Control Block */
typedef struct
{
uint8_t auMsgBuf[ATT_DEFAULT_PAYLOAD_LEN]; /*!< \brief message buffer */
uint8_t auRand[WDX_AU_RAND_LEN]; /*!< \brief random challenge */
uint8_t sessionKey[WDX_AU_KEY_LEN]; /*!< \brief session key */
uint8_t auHash[WDX_AU_HASH_LEN]; /*!< \brief session key */
uint16_t auMsgLen; /*!< \brief message length */
uint8_t authLevel; /*!< \brief current authentication level */
uint8_t authMode; /*!< \brief current authentication mode */
uint8_t reqAuthLevel; /*!< \brief requested authentication level */
uint8_t authState; /*!< \brief authentication protocol state */
} wdxsAuCb_t;
/*! \brief WDXS event message union */
typedef union
{
wsfMsgHdr_t hdr; /*!< header */
dmEvt_t dm; /*!< DM event */
attsCccEvt_t ccc; /*!< ATT CCC event */
} wdxsMsg_t;
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/** \name WDXS Control Block External Declaration
*
*/
/**@{*/
extern wdxsCb_t wdxsCb; /*!< \brief WDXS control block */
extern wdxsAuCb_t wdxsAuCb; /*!< \brief WDXS AU control block */
extern wdxsDcCb_t wdxsDcCb; /*!< \brief WDXS DC control block */
/**@}*/
/**************************************************************************************************
Global Function Prototypes
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Send device configuration notification
*
* \param connId DM connection identifier.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsDcSend(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Send a file transfer control characteristic notification.
*
* \param connId DM connection identifier.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsFtcSend(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Send a file transfer data characteristic notification.
*
* \param connId DM connection identifier.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsFtdSend(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Transmit to authentication characteristic.
*
* \param connId DM connection identifier.
*
* \return ATT status.
*/
/*************************************************************************************************/
void wdxsAuSend(dmConnId_t connId);
/*************************************************************************************************/
/*!
* \brief Process a write to the device configuration characteristic.
*
* \param connId DM connection identifier.
* \param len Length to write.
* \param pValue value to write.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsDcWrite(dmConnId_t connId, uint16_t len, uint8_t *pValue);
/*************************************************************************************************/
/*!
* \brief Process a write to the file transfer control characteristic.
*
* \param connId DM connection identifier.
* \param len Length to write.
* \param pValue Value to write.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsFtcWrite(dmConnId_t connId, uint16_t len, uint8_t *pValue);
/*************************************************************************************************/
/*!
* \brief Process a write to the file transfer data characteristic.
*
* \param connId DM connection identifier.
* \param len Length to write.
* \param pValue Value to write.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsFtdWrite(dmConnId_t connId, uint16_t len, uint8_t *pValue);
/*************************************************************************************************/
/*!
* \brief Process a write to the authentication characteristic.
*
* \param connId DM connection identifier.
* \param len Length to write.
* \param pValue Value to write
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsAuWrite(dmConnId_t connId, uint16_t len, uint8_t *pValue);
/*************************************************************************************************/
/*!
* \brief Send update message for connection parameters.
*
* \param connId DM connection identifier.
* \param status Update status.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsDcUpdateConnParam(dmConnId_t connId, uint8_t status);
/*************************************************************************************************/
/*!
* \brief Send update message for PHY.
*
* \param connId DM connection identifier.
* \param status Update status.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t wdxsDcUpdatePhy(dmConnId_t connId, uint8_t status);
/*************************************************************************************************/
/*!
* \brief Register a PHY write callback for the device configuration characteristic.
*
* \param cback PHY callback function.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsDcPhyRegister(wdxsDcPhyWriteCback_t cback);
/*************************************************************************************************/
/*!
* \brief Create the file list.
*
* \return none.
*/
/*************************************************************************************************/
void WdxsUpdateListing(void);
/*! \} */ /* WIRELESS_DATA_EXCHANGE_PROFILE */
#ifdef __cplusplus
}
#endif
#endif /* WDXS_MAIN_H */
@@ -0,0 +1,147 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation - Device PHY Configuration.
*
* Copyright (c) 2013-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 "util/wstr.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "util/bstream.h"
#include "svc_wdxs.h"
#include "wdxs_api.h"
#include "wdxs_main.h"
#include "dm_api.h"
#include "app_api.h"
#include "app_hw.h"
#if WDXS_DC_ENABLED == TRUE
/*************************************************************************************************/
/*!
* \brief Process set PHY request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcSetPhyReq(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
uint8_t allPhys;
uint8_t txPhys;
uint8_t rxPhys;
uint16_t phyOptions;
/* verify parameter length */
if (len != WDX_DC_LEN_PHY_UPDATE_REQ)
{
return ATT_ERR_LENGTH;
}
/* parse parameters */
BSTREAM_TO_UINT8(allPhys, pValue);
BSTREAM_TO_UINT8(txPhys, pValue);
BSTREAM_TO_UINT8(rxPhys, pValue);
BSTREAM_TO_UINT16(phyOptions, pValue);
/* request update to PHY */
DmSetPhy(connId, allPhys, txPhys, rxPhys, phyOptions);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
* \brief Process a Get PHY request.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcGetPhy(dmConnId_t connId, uint16_t len, uint8_t *pValue)
{
return wdxsDcUpdatePhy(connId, HCI_SUCCESS);
}
/*************************************************************************************************/
/*!
* \brief Process a PHY write to the device configuration characteristic.
*
* \return ATT status.
*/
/*************************************************************************************************/
static uint8_t wdxsDcPhyWrite(dmConnId_t connId, uint8_t op, uint8_t id, uint16_t len, uint8_t *pValue)
{
uint8_t status;
/* set operation */
if (op == WDX_DC_OP_SET)
{
switch (id)
{
case WDX_DC_ID_PHY_UPDATE_REQ:
status = wdxsDcSetPhyReq(connId, len, pValue);
break;
default:
status = ATT_ERR_RANGE;
break;
}
}
/* get operation */
else if (op == WDX_DC_OP_GET)
{
switch (id)
{
case WDX_DC_ID_PHY:
status = wdxsDcGetPhy(connId, len, pValue);
break;
default:
status = ATT_ERR_RANGE;
break;
}
}
else
{
status = ATT_ERR_RANGE;
}
return status;
}
/*************************************************************************************************/
/*!
* \brief Initialize WDXS Device Configuration PHY.
*
* \param None
*
* \return None.
*/
/*************************************************************************************************/
void WdxsPhyInit(void)
{
/* register device configuration phy write callback */
wdxsDcPhyRegister(wdxsDcPhyWrite);
}
#endif /* WDXS_DC_ENABLED */
@@ -0,0 +1,264 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation - Stream Example.
*
* Copyright (c) 2013-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 "util/wstr.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "wsf_efs.h"
#include "util/bstream.h"
#include "svc_wdxs.h"
#include "wdxs_api.h"
#include "wdxs_main.h"
#include "wdxs_stream.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
/*! Type of waveform to output from the Example Stream */
static uint8_t wdxsStreamWaveform = WDXS_STREAM_WAVEFORM_SINE;
/*************************************************************************************************/
/*
* Note: This file contains an example of creating Wireless Data Exchange
* (WDXS) Streams. WDXS Streams are implemented as virtual Physical Media in
* the Embedded File System (EFS). A Stream can be created in three steps:
*
* Step 1: Create a FileMedia_t (EFS Media Control structure) for the stream
* containing the read function created in step 2.
*
* Step 2: Implement a read function for the stream. The WDXS and EFS will
* call the read function to get data from the stream.
*
* Step 3: Register the media with the EFS, and add a file to the embedded
* file system that uses the media created in step 2.
*/
/*************************************************************************************************/
/*************************************************************************************************/
/* Step 1: Media Control Block */
/*************************************************************************************************/
/* Prototype of stream read function */
static uint8_t wdxsStreamRead(uint8_t *pBuf, uint32_t address, uint32_t len);
/* Example media control structure for a stream */
static const wsfEfsMedia_t WDXS_StreamMedia =
{
0,
0,
0,
NULL,
NULL,
wdxsStreamRead,
NULL,
NULL
};
/*************************************************************************************************/
/* Step 2: Read Function */
/*************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Example of a media read function that generates a Sine Wave.
*
* \param pBuf buffer to hold stream data.
* \param address unused in streams.
* \param len size of pBuf in bytes.
*
* \return None.
*/
/*************************************************************************************************/
static uint8_t wdxsSineRead(uint8_t *pBuf, uint32_t address, uint32_t len)
{
static int8_t incr = 1;
static uint8_t dataVal = 0;
/* Build data in sine waveform */
memset(pBuf, dataVal, len);
if (dataVal <= 127)
{
incr++;
}
else
{
incr--;
}
dataVal += (incr / 2);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Example of a media read function that generates a Step Wave.
*
* \param pBuf buffer to hold stream data.
* \param address unused in streams.
* \param len size of pBuf in bytes.
*
* \return None.
*/
/*************************************************************************************************/
static uint8_t wdxsStepRead(uint8_t *pBuf, uint32_t address, uint32_t len)
{
static int8_t count = 0;
static int8_t incr = 25;
static uint8_t dataVal = 0;
/* Build data in step waveform */
memset(pBuf, dataVal, len);
if (count++ == 5)
{
count = 0;
dataVal += incr;
if (dataVal == 0)
incr = 25;
if (dataVal == 250)
incr = -25;
}
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Example of a media read function that generates a Sawtooth Wave.
*
* \param pBuf buffer to hold stream data.
* \param address unused in streams.
* \param len size of pBuf in bytes.
*
* \return None.
*/
/*************************************************************************************************/
static uint8_t wdxsSawtoothRead(uint8_t *pBuf, uint32_t address, uint32_t len)
{
static int8_t incr = 1;
static uint8_t dataVal = 0;
uint32_t i;
/* Build data in sawtooth waveform */
for (i=0; i<len; i++)
{
*pBuf++ = dataVal;
dataVal += incr;
if (dataVal == 0)
incr = 1;
else if (dataVal == 255)
incr = -1;
}
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Example of a media read function.
*
* \param pBuf buffer to hold stream data.
* \param address unused in streams.
* \param len size of pBuf in bytes.
*
* \return None.
*/
/*************************************************************************************************/
static uint8_t wdxsStreamRead(uint8_t *pBuf, uint32_t address, uint32_t len)
{
switch(wdxsStreamWaveform)
{
case WDXS_STREAM_WAVEFORM_SINE:
wdxsSineRead(pBuf, address, len);
break;
case WDXS_STREAM_WAVEFORM_STEP:
wdxsStepRead(pBuf, address, len);
break;
case WDXS_STREAM_WAVEFORM_SAWTOOTH:
wdxsSawtoothRead(pBuf, address, len);
break;
}
return TRUE;
}
/*************************************************************************************************/
/* Step 3: Register the stream media and adding the stream file. */
/*************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Example of creating a WDXS stream.
*
* \param none
*
* \return None.
*/
/*************************************************************************************************/
void wdxsStreamInit(void)
{
wsfEsfAttributes_t attr;
/* Register the media for the stream */
WsfEfsRegisterMedia(&WDXS_StreamMedia, WDX_STREAM_MEDIA);
/* Set the attributes for the stream */
attr.permissions = WSF_EFS_REMOTE_VISIBLE | WSF_EFS_REMOTE_GET_PERMITTED;
attr.type = WSF_EFS_FILE_TYPE_STREAM;
/* Potential buffer overrun is intentional to zero out fixed length field */
/* coverity[overrun-buffer-arg] */
WstrnCpy(attr.name, "Stream", WSF_EFS_NAME_LEN);
/* coverity[overrun-buffer-arg] */
WstrnCpy(attr.version, "1.0", WSF_EFS_VERSION_LEN);
/* Add a file for the stream */
WsfEfsAddFile(0, WDX_STREAM_MEDIA, &attr, 0);
}
/*************************************************************************************************/
/*!
* \brief Changes the type of waveform transmitted by the stream.
*
* \param type - Identifier of the waveform
*
* \return None.
*/
/*************************************************************************************************/
void wdxsSetStreamWaveform(uint8_t type)
{
if (type <= WDXS_STREAM_WAVEFORM_SAWTOOTH)
{
wdxsStreamWaveform = type;
}
}
@@ -0,0 +1,79 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Wireless Data Exchange profile implementation - Stream Example.
*
* Copyright (c) 2013-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 WDXS_STREAM_H
#define WDXS_STREAM_H
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup WIRELESS_DATA_EXCHANGE_PROFILE
* \{ */
/**************************************************************************************************
Constant Definitions
**************************************************************************************************/
/** \name WDXS Stream Waveform Types
* Type of waveform to output from the Example Stream
*/
/**@{*/
#define WDXS_STREAM_WAVEFORM_SINE 0
#define WDXS_STREAM_WAVEFORM_STEP 1
#define WDXS_STREAM_WAVEFORM_SAWTOOTH 2
/**@}*/
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Example of creating a WDXS stream.
*
* \return None.
*/
/*************************************************************************************************/
void wdxsStreamInit(void);
/*************************************************************************************************/
/*!
* \brief Changes the type of waveform transmitted by the stream.
*
* \param type - Identifier of the waveform
*
* \return None.
*/
/*************************************************************************************************/
void wdxsSetStreamWaveform(uint8_t type);
/*! \} */ /* WIRELESS_DATA_EXCHANGE_PROFILE */
#ifdef __cplusplus
}
#endif
#endif /* WDXS_STREAM_H */
@@ -0,0 +1,73 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Arm Ltd. proprietary profile client.
*
* 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 WPC_API_H
#define WPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup ARM_LTD_PROPRIETARY_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Arm Ltd. proprietary service P1 enumeration of handle indexes of characteristics to be discovered */
enum
{
WPC_P1_DAT_HDL_IDX, /*!< \brief Proprietary data */
WPC_P1_NA_CCC_HDL_IDX, /*!< \brief Proprietary data client characteristic configuration descriptor */
WPC_P1_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Arm Ltd. proprietary service P1.
* Parameter pHdlList must point to an array of length \ref WPC_P1_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void WpcP1Discover(dmConnId_t connId, uint16_t *pHdlList);
/*! \} */ /* ARM_LTD_PROPRIETARY_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* WPC_API_H */
@@ -0,0 +1,88 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Arm Ltd. proprietary profile client.
*
* 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_assert.h"
#include "util/bstream.h"
#include "app_api.h"
#include "wpc_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Arm Ltd. proprietary service P1
*/
/* UUIDs */
static const uint8_t wpcP1SvcUuid[] = {ATT_UUID_P1_SERVICE}; /*! Proprietary service P1 */
static const uint8_t wpcD1ChUuid[] = {ATT_UUID_D1_DATA}; /*! Proprietary data D1 */
/* Characteristics for discovery */
/*! Proprietary data */
static const attcDiscChar_t wpcP1Dat =
{
wpcD1ChUuid,
ATTC_SET_REQUIRED | ATTC_SET_UUID_128
};
/*! Proprietary data descriptor */
static const attcDiscChar_t wpcP1datCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *wpcP1DiscCharList[] =
{
&wpcP1Dat, /*! Proprietary data */
&wpcP1datCcc /*! Proprietary data descriptor */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(WPC_P1_HDL_LIST_LEN == ((sizeof(wpcP1DiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Arm Ltd. proprietary service P1.
* Parameter pHdlList must point to an array of length WPC_P1_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void WpcP1Discover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_128_UUID_LEN, (uint8_t *) wpcP1SvcUuid,
WPC_P1_HDL_LIST_LEN, (attcDiscChar_t **) wpcP1DiscCharList, pHdlList);
}
@@ -0,0 +1,89 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Weight Scale profile client.
*
* 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 WSPC_API_H
#define WSPC_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup WEIGHT_SCALE_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Weight Scale service enumeration of handle indexes of characteristics to be discovered */
enum
{
WSPC_WSS_WSM_HDL_IDX, /*!< \brief Weight scale measurement */
WSPC_WSS_WSM_CCC_HDL_IDX, /*!< \brief Weight scale measurement CCC descriptor */
WSPC_WSS_WSF_HDL_IDX, /*!< \brief Weight scale feature */
WSPC_WSS_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Weight Scale service.
* Parameter pHdlList must point to an array of length \ref WSPC_WSS_HDL_LIST_LEN.
* If discovery is successful the handles of discovered characteristics and
* descriptors will be set in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void WspcWssDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref WSPC_WSS_HDL_LIST_LEN.
* If the ATT handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t WspcWssValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* WEIGHT_SCALE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* WSPC_API_H */
@@ -0,0 +1,191 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Weight Scale profile collector.
*
* 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_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "svc_ch.h"
#include "app_api.h"
#include "wspc_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*!
* Weight Scale service characteristics for discovery
*/
/*! Weight scale measurement */
static const attcDiscChar_t wspcWssWsm =
{
attWmChUuid,
ATTC_SET_REQUIRED
};
/*! Weight scale measurement CCC descriptor */
static const attcDiscChar_t wspcWssWsmCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Weight scale feature */
static const attcDiscChar_t wspcWssWsf =
{
attWsfChUuid,
ATTC_SET_REQUIRED
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *wspcWssDiscCharList[] =
{
&wspcWssWsm, /*! Weight scale measurement */
&wspcWssWsmCcc, /*! Weight scale measurement CCC descriptor */
&wspcWssWsf /*! Weight scale feature */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(WSPC_WSS_HDL_LIST_LEN == ((sizeof(wspcWssDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Parse a weight scale measurement.
*
* \param pValue Pointer to buffer containing value.
* \param len length of buffer.
*
* \return None.
*/
/*************************************************************************************************/
void wspcWssParseWsm(uint8_t *pValue, uint16_t len)
{
uint8_t flags;
uint16_t weight;
uint16_t year;
uint8_t month, day, hour, min, sec;
uint16_t minLen = CH_WSM_FLAGS_LEN + CH_WSM_MEAS_LEN;
/* Suppress unused variable compile warning */
(void)month; (void)day; (void)hour; (void)min; (void)sec; (void)year; (void)weight;
if (len > 0)
{
/* get flags */
BSTREAM_TO_UINT8(flags, pValue);
/* determine expected minimum length based on flags */
if (flags & CH_WSM_FLAG_TIMESTAMP)
{
minLen += CH_WSM_TIMESTAMP_LEN;
}
}
/* verify length */
if (len < minLen)
{
APP_TRACE_INFO2("Weight Scale meas len:%d minLen:%d", len, minLen);
return;
}
/* weight */
BSTREAM_TO_UINT16(weight, pValue);
if (flags & CH_WSM_FLAG_UNITS_LBS)
{
APP_TRACE_INFO2(" Weight: %d.%02d", (weight / 100), (weight % 100));
}
else /* CH_WSM_FLAG_UNITS_KG */
{
APP_TRACE_INFO2(" Weight: %d.%03d", (weight / 200), ((weight % 200) * 5));
}
/* timestamp */
if (flags & CH_WSM_FLAG_TIMESTAMP)
{
BSTREAM_TO_UINT16(year, pValue);
BSTREAM_TO_UINT8(month, pValue);
BSTREAM_TO_UINT8(day, pValue);
BSTREAM_TO_UINT8(hour, pValue);
BSTREAM_TO_UINT8(min, pValue);
BSTREAM_TO_UINT8(sec, pValue);
APP_TRACE_INFO3(" Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3(" Time: %02d:%02d:%02d", hour, min, sec);
}
APP_TRACE_INFO1(" Flags:0x%02x", flags);
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Weight Scale service. Parameter
* pHdlList must point to an array of length WSPC_WSS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void WspcWssDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attWssSvcUuid,
WSPC_WSS_HDL_LIST_LEN, (attcDiscChar_t **) wspcWssDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length WSPC_WSS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t WspcWssValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* weight scale measurement */
if (pMsg->handle == pHdlList[WSPC_WSS_WSM_HDL_IDX])
{
APP_TRACE_INFO0("Weight measurement");
/* parse value */
wspcWssParseWsm(pMsg->pValue, pMsg->valueLen);
}
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
@@ -0,0 +1,67 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Weight Scale profile sensor.
*
* 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 WSPS_API_H
#define WSPS_API_H
#include "wsf_timer.h"
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup WEIGHT_SCALE_PROFILE
* \{ */
/*************************************************************************************************/
/*!
* \brief Weight scale measurement complete.
*
* \param connId DM connection identifier.
* \param wsmCccIdx Index of weight scale measurement CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void WspsMeasComplete(dmConnId_t connId, uint8_t wsmCccIdx);
/*************************************************************************************************/
/*!
* \brief Set the weight scale measurement flags.
*
* \param flags Weight scale measurement flags.
*
* \return None.
*/
/*************************************************************************************************/
void WspsSetWsmFlags(uint8_t flags);
/*! \} */ /* WEIGHT_SCALE_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* WSPS_API_H */
@@ -0,0 +1,128 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Weight Scale profile sensor.
*
* 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 "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_wss.h"
#include "app_api.h"
#include "app_hw.h"
#include "wsps_api.h"
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
appWsm_t wsm; /* weight scale measurement */
uint8_t wsmFlags; /* flags */
} wspsCb;
/*************************************************************************************************/
/*!
* \brief Build a weight scale measurement characteristic.
*
* \param pBuf Pointer to buffer to hold the built weight scale measurement characteristic.
* \param pWsm Weight measurement values.
*
* \return Length of pBuf in bytes.
*/
/*************************************************************************************************/
static uint8_t wspsBuildWsm(uint8_t *pBuf, appWsm_t *pWsm)
{
uint8_t *p = pBuf;
uint8_t flags = pWsm->flags;
/* flags */
UINT8_TO_BSTREAM(p, flags);
/* measurement */
UINT16_TO_BSTREAM(p, pWsm->weight);
/* time stamp */
if (flags & CH_WSM_FLAG_TIMESTAMP)
{
UINT16_TO_BSTREAM(p, pWsm->timestamp.year);
UINT8_TO_BSTREAM(p, pWsm->timestamp.month);
UINT8_TO_BSTREAM(p, pWsm->timestamp.day);
UINT8_TO_BSTREAM(p, pWsm->timestamp.hour);
UINT8_TO_BSTREAM(p, pWsm->timestamp.min);
UINT8_TO_BSTREAM(p, pWsm->timestamp.sec);
}
/* return length */
return (uint8_t) (p - pBuf);
}
/*************************************************************************************************/
/*!
* \brief Weight measurement complete.
*
* \param connId DM connection identifier.
* \param wsmCccIdx Index of weight scale measurement CCC descriptor in CCC descriptor
* handle table.
*
* \return None.
*/
/*************************************************************************************************/
void WspsMeasComplete(dmConnId_t connId, uint8_t wsmCccIdx)
{
uint8_t buf[ATT_DEFAULT_PAYLOAD_LEN];
uint8_t len;
/* if indications enabled */
if (AttsCccEnabled(connId, wsmCccIdx))
{
/* read weight scale measurement sensor data */
AppHwWsmRead(&wspsCb.wsm);
/* set flags */
wspsCb.wsm.flags = wspsCb.wsmFlags;
/* build weight scale measurement characteristic */
len = wspsBuildWsm(buf, &wspsCb.wsm);
/* send weight scale measurement indication */
AttsHandleValueInd(connId, WSS_WM_HDL, len, buf);
}
}
/*************************************************************************************************/
/*!
* \brief Set the weight scale measurement flags.
*
* \param flags Weight measurement flags.
*
* \return None.
*/
/*************************************************************************************************/
void WspsSetWsmFlags(uint8_t flags)
{
wspsCb.wsmFlags = flags;
}