pinebuds/services/ble_profiles/htp/htpt/src/htpt.c

553 lines
17 KiB
C

/**
****************************************************************************************
* @addtogroup HTPT
* @{
****************************************************************************************
*/
/*
* INCLUDE FILES
****************************************************************************************
*/
#include "rwip_config.h"
#if (BLE_HT_THERMOM)
#include "attm.h"
#include "co_utils.h"
#include "htpt.h"
#include "htpt_task.h"
#include "prf_utils.h"
#include "co_utils.h"
#include "ke_mem.h"
/*
* HTPT PROFILE ATTRIBUTES
****************************************************************************************
*/
/// Full HTS Database Description - Used to add attributes into the database
const struct attm_desc htpt_att_db[HTS_IDX_NB] = {
// Health Thermometer Service Declaration
[HTS_IDX_SVC] = {ATT_DECL_PRIMARY_SERVICE, PERM(RD, ENABLE), 0, 0},
// Temperature Measurement Characteristic Declaration
[HTS_IDX_TEMP_MEAS_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0,
0},
// Temperature Measurement Characteristic Value
[HTS_IDX_TEMP_MEAS_VAL] = {ATT_CHAR_TEMPERATURE_MEAS, PERM(IND, ENABLE),
PERM(RI, ENABLE), 0},
// Temperature Measurement Characteristic - Client Characteristic
// Configuration Descriptor
[HTS_IDX_TEMP_MEAS_IND_CFG] = {ATT_DESC_CLIENT_CHAR_CFG,
PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE),
0, 0},
// Temperature Type Characteristic Declaration
[HTS_IDX_TEMP_TYPE_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0,
0},
// Temperature Type Characteristic Value
[HTS_IDX_TEMP_TYPE_VAL] = {ATT_CHAR_TEMPERATURE_TYPE, PERM(RD, ENABLE),
PERM(RI, ENABLE), 0},
// Intermediate Measurement Characteristic Declaration
[HTS_IDX_INTERM_TEMP_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0,
0},
// Intermediate Measurement Characteristic Value
[HTS_IDX_INTERM_TEMP_VAL] = {ATT_CHAR_INTERMED_TEMPERATURE,
PERM(NTF, ENABLE), PERM(RI, ENABLE), 0},
// Intermediate Measurement Characteristic - Client Characteristic
// Configuration Descriptor
[HTS_IDX_INTERM_TEMP_CFG] = {ATT_DESC_CLIENT_CHAR_CFG,
PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0,
0},
// Measurement Interval Characteristic Declaration
[HTS_IDX_MEAS_INTV_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0,
0},
// Measurement Interval Characteristic Value
[HTS_IDX_MEAS_INTV_VAL] = {ATT_CHAR_MEAS_INTERVAL, PERM(RD, ENABLE),
PERM(RI, ENABLE), HTPT_MEAS_INTV_MAX_LEN},
// Measurement Interval Characteristic - Client Characteristic Configuration
// Descriptor
[HTS_IDX_MEAS_INTV_CFG] = {ATT_DESC_CLIENT_CHAR_CFG,
PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0,
0},
// Measurement Interval Characteristic - Valid Range Descriptor
[HTS_IDX_MEAS_INTV_VAL_RANGE] = {ATT_DESC_VALID_RANGE, PERM(RD, ENABLE),
PERM(RI, ENABLE), 0},
};
static uint16_t htpt_compute_att_table(uint16_t features);
/*
* LOCAL FUNCTION DEFINITIONS
****************************************************************************************
*/
static uint8_t htpt_init(struct prf_task_env *env, uint16_t *start_hdl,
uint16_t app_task, uint8_t sec_lvl,
struct htpt_db_cfg *params) {
// Service content flag
uint16_t cfg_flag;
// DB Creation Status
uint8_t status = ATT_ERR_NO_ERROR;
BLE_PRF_HP_FUNC_ENTER();
cfg_flag = htpt_compute_att_table(params->features);
status = attm_svc_create_db(
start_hdl, ATT_SVC_HEALTH_THERMOM, (uint8_t *)&cfg_flag, HTS_IDX_NB, NULL,
env->task, &htpt_att_db[0],
(sec_lvl & (PERM_MASK_SVC_DIS | PERM_MASK_SVC_AUTH | PERM_MASK_SVC_EKS)) |
PERM(SVC_MI, DISABLE));
if (status == ATT_ERR_NO_ERROR) {
//-------------------- allocate memory required for the profile
//---------------------
struct htpt_env_tag *htpt_env = (struct htpt_env_tag *)ke_malloc(
sizeof(struct htpt_env_tag), KE_MEM_ATT_DB);
// allocate PROXR required environment variable
env->env = (prf_env_t *)htpt_env;
htpt_env->shdl = *start_hdl;
htpt_env->prf_env.app_task =
app_task | (PERM_GET(sec_lvl, SVC_MI) ? PERM(PRF_MI, ENABLE)
: PERM(PRF_MI, DISABLE));
htpt_env->prf_env.prf_task = env->task | PERM(PRF_MI, DISABLE);
// initialize environment variable
env->id = TASK_ID_HTPT;
htpt_task_init(&(env->desc));
// Save features on the environment
htpt_env->features = params->features;
htpt_env->meas_intv = params->meas_intv;
htpt_env->meas_intv_min = params->valid_range_min;
htpt_env->meas_intv_max = params->valid_range_max;
htpt_env->temp_type = params->temp_type;
htpt_env->operation = NULL;
memset(htpt_env->ntf_ind_cfg, 0, sizeof(htpt_env->ntf_ind_cfg));
// Update measurement interval permissions
if (HTPT_IS_FEATURE_SUPPORTED(params->features, HTPT_MEAS_INTV_CHAR_SUP)) {
uint16_t perm = PERM(RD, ENABLE);
// Check if Measurement Interval Char. supports indications
if (HTPT_IS_FEATURE_SUPPORTED(params->features, HTPT_MEAS_INTV_IND_SUP)) {
perm |= PERM(IND, ENABLE);
}
// Check if Measurement Interval Char. is writable
if (HTPT_IS_FEATURE_SUPPORTED(params->features, HTPT_MEAS_INTV_WR_SUP)) {
perm |= PERM(WP, UNAUTH) | PERM(WRITE_REQ, ENABLE);
}
attm_att_set_permission(HTPT_HANDLE(HTS_IDX_MEAS_INTV_VAL), perm, 0);
}
// service is ready, go into an Idle state
ke_state_set(env->task, HTPT_IDLE);
}
BLE_PRF_HP_FUNC_LEAVE();
return (status);
}
static void htpt_destroy(struct prf_task_env *env) {
struct htpt_env_tag *htpt_env = (struct htpt_env_tag *)env->env;
BLE_PRF_HP_FUNC_ENTER();
// free profile environment variables
if (htpt_env->operation != NULL) {
ke_free(htpt_env->operation);
}
env->env = NULL;
ke_free(htpt_env);
BLE_PRF_HP_FUNC_LEAVE();
}
static void htpt_create(struct prf_task_env *env, uint8_t conidx) {
BLE_PRF_HP_FUNC_ENTER();
/* Clear configuration for this connection */
struct htpt_env_tag *htpt_env = (struct htpt_env_tag *)env->env;
htpt_env->ntf_ind_cfg[conidx] = 0;
BLE_PRF_HP_FUNC_LEAVE();
}
static void htpt_cleanup(struct prf_task_env *env, uint8_t conidx,
uint8_t reason) {
BLE_PRF_HP_FUNC_ENTER();
/* Clear configuration for this connection */
struct htpt_env_tag *htpt_env = (struct htpt_env_tag *)env->env;
htpt_env->ntf_ind_cfg[conidx] = 0;
BLE_PRF_HP_FUNC_LEAVE();
}
/**
****************************************************************************************
* @brief Compute a flag allowing to know attributes to add into the database
*
* @return a 16-bit flag whose each bit matches an attribute. If the bit is set
*to 1, the attribute will be added into the database.
****************************************************************************************
*/
static uint16_t htpt_compute_att_table(uint16_t features) {
BLE_PRF_HP_FUNC_ENTER();
// Temperature Measurement Characteristic is mandatory
uint16_t att_table = HTPT_TEMP_MEAS_MASK;
// Check if Temperature Type Char. is supported
if (HTPT_IS_FEATURE_SUPPORTED(features, HTPT_TEMP_TYPE_CHAR_SUP)) {
att_table |= HTPT_TEMP_TYPE_MASK;
}
// Check if Intermediate Temperature Char. is supported
if (HTPT_IS_FEATURE_SUPPORTED(features, HTPT_INTERM_TEMP_CHAR_SUP)) {
att_table |= HTPT_INTM_TEMP_MASK;
}
// Check if Measurement Interval Char. is supported
if (HTPT_IS_FEATURE_SUPPORTED(features, HTPT_MEAS_INTV_CHAR_SUP)) {
att_table |= HTPT_MEAS_INTV_MASK;
// Check if Measurement Interval Char. supports indications
if (HTPT_IS_FEATURE_SUPPORTED(features, HTPT_MEAS_INTV_IND_SUP)) {
att_table |= HTPT_MEAS_INTV_CCC_MASK;
}
// Check if Measurement Interval Char. is writable
if (HTPT_IS_FEATURE_SUPPORTED(features, HTPT_MEAS_INTV_WR_SUP)) {
att_table |= HTPT_MEAS_INTV_VALID_RGE_MASK;
}
}
BLE_PRF_HP_FUNC_LEAVE();
return att_table;
}
/*
* GLOBAL VARIABLE DEFINITIONS
****************************************************************************************
*/
/// HTPT Task interface required by profile manager
const struct prf_task_cbs htpt_itf = {
(prf_init_fnct)htpt_init,
htpt_destroy,
htpt_create,
htpt_cleanup,
};
/*
* FUNCTION DEFINITIONS
****************************************************************************************
*/
uint8_t htpt_get_valid_rge_offset(uint16_t features) {
uint8_t offset = 0;
if (HTPT_IS_FEATURE_SUPPORTED(features, HTPT_MEAS_INTV_WR_SUP)) {
offset += 1;
if (HTPT_IS_FEATURE_SUPPORTED(features, HTPT_MEAS_INTV_IND_SUP)) {
offset += 1;
}
}
return offset;
}
uint8_t htpt_pack_temp_value(uint8_t *packed_temp,
struct htp_temp_meas temp_meas) {
uint8_t cursor = 0;
*(packed_temp + cursor) = temp_meas.flags;
cursor += 1;
co_write32p(packed_temp + cursor, temp_meas.temp);
cursor += 4;
// Time Flag Set
if ((temp_meas.flags & HTP_FLAG_TIME) == HTP_FLAG_TIME) {
cursor += prf_pack_date_time(packed_temp + cursor, &(temp_meas.time_stamp));
}
// Type flag set
if ((temp_meas.flags & HTP_FLAG_TYPE) == HTP_FLAG_TYPE) {
*(packed_temp + cursor) = temp_meas.type;
cursor += 1;
}
// Clear unused packet data
if (cursor < HTPT_TEMP_MEAS_MAX_LEN) {
memset(packed_temp + cursor, 0, (HTPT_TEMP_MEAS_MAX_LEN - cursor));
}
return cursor;
}
void htpt_exe_operation(void) {
BLE_PRF_HP_FUNC_ENTER();
struct htpt_env_tag *htpt_env = PRF_ENV_GET(HTPT, htpt);
ASSERT_ERR(htpt_env->operation != NULL);
bool finished = true;
while (htpt_env->operation->cursor < BLE_CONNECTION_MAX) {
// check if this type of event is enabled
if (((htpt_env->ntf_ind_cfg[htpt_env->operation->cursor] &
htpt_env->operation->op) != 0)
// and event not filtered on current connection
&& (htpt_env->operation->conidx != htpt_env->operation->cursor)) {
// trigger the event
struct gattc_send_evt_cmd *evt =
KE_MSG_ALLOC_DYN(GATTC_SEND_EVT_CMD,
KE_BUILD_ID(TASK_GATTC, htpt_env->operation->cursor),
prf_src_task_get(&htpt_env->prf_env, 0),
gattc_send_evt_cmd, htpt_env->operation->length);
evt->operation = (htpt_env->operation->op != HTPT_CFG_INTERM_MEAS_NTF)
? GATTC_INDICATE
: GATTC_NOTIFY;
evt->length = htpt_env->operation->length;
evt->handle = htpt_env->operation->handle;
memcpy(evt->value, htpt_env->operation->data, evt->length);
ke_msg_send(evt);
finished = false;
htpt_env->operation->cursor++;
break;
}
htpt_env->operation->cursor++;
}
// check if operation is finished
if (finished) {
// do not send response if operation has been locally requested
if (htpt_env->operation->dest_id !=
prf_src_task_get(&htpt_env->prf_env, 0)) {
// send response to requester
struct htpt_meas_intv_upd_rsp *rsp = KE_MSG_ALLOC(
((htpt_env->operation->op == HTPT_CFG_MEAS_INTV_IND)
? HTPT_MEAS_INTV_UPD_RSP
: HTPT_TEMP_SEND_RSP),
htpt_env->operation->dest_id, prf_src_task_get(&htpt_env->prf_env, 0),
htpt_meas_intv_upd_rsp);
rsp->status = GAP_ERR_NO_ERROR;
ke_msg_send(rsp);
}
// free operation
ke_free(htpt_env->operation);
htpt_env->operation = NULL;
// go back to idle state
ke_state_set(prf_src_task_get(&(htpt_env->prf_env), 0), HTPT_IDLE);
}
BLE_PRF_HP_FUNC_LEAVE();
}
uint8_t htpt_update_ntf_ind_cfg(uint8_t conidx, uint8_t cfg, uint16_t valid_val,
uint16_t value) {
struct htpt_env_tag *htpt_env = PRF_ENV_GET(HTPT, htpt);
uint8_t status = GAP_ERR_NO_ERROR;
BLE_PRF_HP_FUNC_ENTER();
if ((value != valid_val) && (value != PRF_CLI_STOP_NTFIND)) {
status = PRF_APP_ERROR;
} else if (value == valid_val) {
htpt_env->ntf_ind_cfg[conidx] |= cfg;
} else {
htpt_env->ntf_ind_cfg[conidx] &= ~cfg;
}
if (status == GAP_ERR_NO_ERROR) {
// inform application that notification/indication configuration has changed
struct htpt_cfg_indntf_ind *ind = KE_MSG_ALLOC(
HTPT_CFG_INDNTF_IND, prf_dst_task_get(&htpt_env->prf_env, conidx),
prf_src_task_get(&htpt_env->prf_env, conidx), htpt_cfg_indntf_ind);
ind->conidx = conidx;
ind->ntf_ind_cfg = htpt_env->ntf_ind_cfg[conidx];
ke_msg_send(ind);
}
BLE_PRF_HP_FUNC_LEAVE();
return (status);
}
const struct prf_task_cbs *htpt_prf_itf_get(void) { return &htpt_itf; }
uint16_t htpt_att_hdl_get(struct htpt_env_tag *htpt_env, uint8_t att_idx) {
uint16_t handle = htpt_env->shdl;
do {
// Mandatory attribute handle
if (att_idx > HTS_IDX_TEMP_MEAS_IND_CFG) {
handle += HTPT_TEMP_MEAS_ATT_NB;
} else {
handle += att_idx;
break;
}
// Temperature Type
if ((HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_TEMP_TYPE_CHAR_SUP)) &&
(att_idx > HTS_IDX_TEMP_TYPE_VAL)) {
handle += HTPT_TEMP_TYPE_ATT_NB;
} else if (!HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_TEMP_TYPE_CHAR_SUP)) {
handle = ATT_INVALID_HANDLE;
break;
} else {
handle += att_idx - HTS_IDX_TEMP_TYPE_CHAR;
break;
}
// Intermediate Temperature Measurement
if ((HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_INTERM_TEMP_CHAR_SUP)) &&
(att_idx > HTS_IDX_INTERM_TEMP_CFG)) {
handle += HTPT_INTERM_MEAS_ATT_NB;
} else if (!HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_INTERM_TEMP_CHAR_SUP)) {
handle = ATT_INVALID_HANDLE;
break;
} else {
handle += att_idx - HTS_IDX_INTERM_TEMP_CHAR;
break;
}
// Measurement Interval
if (!HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_MEAS_INTV_CHAR_SUP) ||
(att_idx >= HTS_IDX_NB)) {
handle = ATT_INVALID_HANDLE;
break;
}
if (att_idx <= HTS_IDX_MEAS_INTV_VAL) {
handle += att_idx - HTS_IDX_MEAS_INTV_CHAR;
break;
} else {
handle += HTPT_MEAS_INTV_ATT_NB;
}
// Measurement Interval Indication
if (att_idx == HTS_IDX_MEAS_INTV_CFG) {
if (!HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_MEAS_INTV_IND_SUP)) {
handle = ATT_INVALID_HANDLE;
break;
}
}
// Measurement Interval Write permission
else if (HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_MEAS_INTV_WR_SUP)) {
handle += HTPT_MEAS_INTV_CCC_ATT_NB;
if (!HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_MEAS_INTV_WR_SUP)) {
handle = ATT_INVALID_HANDLE;
break;
}
}
} while (0);
return handle;
}
uint8_t htpt_att_idx_get(struct htpt_env_tag *htpt_env, uint16_t handle) {
uint16_t handle_ref = htpt_env->shdl;
uint8_t att_idx = ATT_INVALID_IDX;
do {
// not valid hande
if (handle < handle_ref) {
break;
}
// Mandatory attribute handle
handle_ref += HTPT_TEMP_MEAS_ATT_NB;
if (handle < handle_ref) {
att_idx = HTS_IDX_TEMP_TYPE_CHAR - (handle_ref - handle);
break;
}
// Temperature Type
if (HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_TEMP_TYPE_CHAR_SUP)) {
handle_ref += HTPT_TEMP_TYPE_ATT_NB;
if (handle < handle_ref) {
att_idx = HTS_IDX_INTERM_TEMP_CHAR - (handle_ref - handle);
break;
}
}
// Intermediate Temperature Measurement
if (HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_INTERM_TEMP_CHAR_SUP)) {
handle_ref += HTPT_INTERM_MEAS_ATT_NB;
if (handle < handle_ref) {
att_idx = HTS_IDX_MEAS_INTV_CHAR - (handle_ref - handle);
break;
}
}
// Measurement Interval
if (HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_MEAS_INTV_CHAR_SUP)) {
handle_ref += HTPT_MEAS_INTV_ATT_NB;
if (handle < handle_ref) {
att_idx = HTS_IDX_MEAS_INTV_CFG - (handle_ref - handle);
break;
}
if (HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_MEAS_INTV_IND_SUP)) {
if (handle == handle_ref) {
att_idx = HTS_IDX_MEAS_INTV_CFG;
break;
}
handle_ref += HTPT_MEAS_INTV_CCC_ATT_NB;
}
if (HTPT_IS_FEATURE_SUPPORTED(htpt_env->features,
HTPT_MEAS_INTV_WR_SUP)) {
if (handle == handle_ref) {
att_idx = HTS_IDX_MEAS_INTV_VAL_RANGE;
break;
}
}
}
} while (0);
return att_idx;
}
#endif // BLE_HT_THERMOM
/// @} HTPT