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,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 */