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