/** **************************************************************************************** * @addtogroup HRPS * @{ **************************************************************************************** */ /* * INCLUDE FILES **************************************************************************************** */ #include "rwip_config.h" #if (BLE_HR_SENSOR) #include "attm.h" #include "gap.h" #include "gapc_task.h" #include "gattc_task.h" #include "hrps.h" #include "hrps_task.h" #include "prf_utils.h" #include "co_utils.h" #include "ke_mem.h" /* * HTPT PROFILE ATTRIBUTES **************************************************************************************** */ /// Full HRS Database Description - Used to add attributes into the database const struct attm_desc hrps_att_db[HRS_IDX_NB] = { // Heart Rate Service Declaration [HRS_IDX_SVC] = {ATT_DECL_PRIMARY_SERVICE, PERM(RD, ENABLE), 0, 0}, // Heart Rate Measurement Characteristic Declaration [HRS_IDX_HR_MEAS_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0}, // Heart Rate Measurement Characteristic Value [HRS_IDX_HR_MEAS_VAL] = {ATT_CHAR_HEART_RATE_MEAS, PERM(NTF, ENABLE), PERM(RI, ENABLE), HRPS_HT_MEAS_MAX_LEN}, // Heart Rate Measurement Characteristic - Client Characteristic // Configuration Descriptor [HRS_IDX_HR_MEAS_NTF_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0, 0}, // Body Sensor Location Characteristic Declaration [HRS_IDX_BODY_SENSOR_LOC_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0}, // Body Sensor Location Characteristic Value [HRS_IDX_BODY_SENSOR_LOC_VAL] = {ATT_CHAR_BODY_SENSOR_LOCATION, PERM(RD, ENABLE), PERM(RI, ENABLE), sizeof(uint8_t)}, // Heart Rate Control Point Characteristic Declaration [HRS_IDX_HR_CTNL_PT_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0}, // Heart Rate Control Point Characteristic Value [HRS_IDX_HR_CTNL_PT_VAL] = {ATT_CHAR_HEART_RATE_CNTL_POINT, PERM(WRITE_REQ, ENABLE), PERM(RI, ENABLE), sizeof(uint8_t)}, }; /** **************************************************************************************** * @brief Initialization of the HRPS module. * This function performs all the initializations of the Profile module. * - Creation of database (if it's a service) * - Allocation of profile required memory * - Initialization of task descriptor to register application * - Task State array * - Number of tasks * - Default task handler * * @param[out] env Collector or Service allocated environment data. * @param[in|out] start_hdl Service start handle (0 - dynamically allocated), *only applies for services. * @param[in] app_task Application task number. * @param[in] sec_lvl Security level (AUTH, EKS and MI field of @see enum *attm_value_perm_mask) * @param[in] param Configuration parameters of profile collector or *service (32 bits aligned) * * @return status code to know if profile initialization succeed or not. **************************************************************************************** */ static uint8_t hrps_init(struct prf_task_env *env, uint16_t *start_hdl, uint16_t app_task, uint8_t sec_lvl, struct hrps_db_cfg *params) { //------------------ create the attribute database for the profile //------------------- Service Configuration Flag uint8_t cfg_flag = HRPS_MANDATORY_MASK; // Database Creation Status uint8_t status = ATT_ERR_NO_ERROR; // Set Configuration Flag Value if (HRPS_IS_SUPPORTED(params->features, HRPS_BODY_SENSOR_LOC_CHAR_SUP)) { cfg_flag |= HRPS_BODY_SENSOR_LOC_MASK; } if (HRPS_IS_SUPPORTED(params->features, HRPS_ENGY_EXP_FEAT_SUP)) { cfg_flag |= HRPS_HR_CTNL_PT_MASK; } // Add Service Into Database status = attm_svc_create_db( start_hdl, ATT_SVC_HEART_RATE, (uint8_t *)&cfg_flag, HRS_IDX_NB, NULL, env->task, &hrps_att_db[0], (sec_lvl & (PERM_MASK_SVC_DIS | PERM_MASK_SVC_AUTH | PERM_MASK_SVC_EKS)) | PERM(SVC_MI, DISABLE)); //-------------------- allocate memory required for the profile //--------------------- if (status == ATT_ERR_NO_ERROR) { // Allocate HRPS required environment variable struct hrps_env_tag *hrps_env = (struct hrps_env_tag *)ke_malloc( sizeof(struct hrps_env_tag), KE_MEM_ATT_DB); // Initialize HRPS environment env->env = (prf_env_t *)hrps_env; hrps_env->shdl = *start_hdl; hrps_env->features = params->features; hrps_env->sensor_location = params->body_sensor_loc; hrps_env->operation = NULL; hrps_env->prf_env.app_task = app_task | (PERM_GET(sec_lvl, SVC_MI) ? PERM(PRF_MI, ENABLE) : PERM(PRF_MI, DISABLE)); // Mono Instantiated task hrps_env->prf_env.prf_task = env->task | PERM(PRF_MI, DISABLE); // initialize environment variable env->id = TASK_ID_HRPS; hrps_task_init(&(env->desc)); /* Put HRS in Idle state */ ke_state_set(env->task, HRPS_IDLE); } return (status); } /** **************************************************************************************** * @brief Destruction of the HRPS module - due to a reset for instance. * This function clean-up allocated memory (attribute database is destroyed by *another procedure) * * @param[in|out] env Collector or Service allocated environment data. **************************************************************************************** */ static void hrps_destroy(struct prf_task_env *env) { struct hrps_env_tag *hrps_env = (struct hrps_env_tag *)env->env; // free profile environment variables if (hrps_env->operation != NULL) { ke_free(hrps_env->operation); } env->env = NULL; ke_free(hrps_env); } /** **************************************************************************************** * @brief Handles Connection creation * * @param[in|out] env Collector or Service allocated environment data. * @param[in] conidx Connection index **************************************************************************************** */ static void hrps_create(struct prf_task_env *env, uint8_t conidx) { /* Nothing to do */ } /** **************************************************************************************** * @brief Handles Disconnection * * @param[in|out] env Collector or Service allocated environment data. * @param[in] conidx Connection index * @param[in] reason Detach reason **************************************************************************************** */ static void hrps_cleanup(struct prf_task_env *env, uint8_t conidx, uint8_t reason) { /* Nothing to do */ } /* * GLOBAL VARIABLE DEFINITIONS **************************************************************************************** */ /// HRPS Task interface required by profile manager const struct prf_task_cbs hrps_itf = { (prf_init_fnct)hrps_init, hrps_destroy, hrps_create, hrps_cleanup, }; /* * EXPORTED FUNCTIONS DEFINITIONS **************************************************************************************** */ const struct prf_task_cbs *hrps_prf_itf_get(void) { return &hrps_itf; } /* * EXPORTED FUNCTIONS DEFINITIONS **************************************************************************************** */ void hrps_meas_send_rsp_send(uint8_t status) { // Get the address of the environment struct hrps_env_tag *hrps_env = PRF_ENV_GET(HRPS, hrps); // Send CFM to APP that value has been sent or not struct hrps_meas_send_rsp *cfm = KE_MSG_ALLOC(HRPS_MEAS_SEND_RSP, prf_dst_task_get(&hrps_env->prf_env, 0), prf_src_task_get(&hrps_env->prf_env, 0), hrps_meas_send_rsp); cfm->status = status; ke_msg_send(cfm); } uint8_t hrps_pack_meas_value(uint8_t *packed_hr, const struct hrs_hr_meas *pmeas_val) { uint8_t cursor = 0; uint8_t i = 0; // Heart Rate measurement flags *(packed_hr) = pmeas_val->flags; if ((pmeas_val->flags & HRS_FLAG_HR_16BITS_VALUE) == HRS_FLAG_HR_16BITS_VALUE) { // Heart Rate Measurement Value 16 bits co_write16p(packed_hr + 1, pmeas_val->heart_rate); cursor += 3; } else { // Heart Rate Measurement Value 8 bits *(packed_hr + 1) = pmeas_val->heart_rate; cursor += 2; } if ((pmeas_val->flags & HRS_FLAG_ENERGY_EXPENDED_PRESENT) == HRS_FLAG_ENERGY_EXPENDED_PRESENT) { // Energy Expended present co_write16p(packed_hr + cursor, pmeas_val->energy_expended); cursor += 2; } if ((pmeas_val->flags & HRS_FLAG_RR_INTERVAL_PRESENT) == HRS_FLAG_RR_INTERVAL_PRESENT) { for (i = 0; (i < (pmeas_val->nb_rr_interval)) && (i < (HRS_MAX_RR_INTERVAL)); i++) { // RR-Intervals co_write16p(packed_hr + cursor, pmeas_val->rr_intervals[i]); cursor += 2; } } // Clear unused packet data if (cursor < HRPS_HT_MEAS_MAX_LEN) { memset(packed_hr + cursor, 0, HRPS_HT_MEAS_MAX_LEN - cursor); } return cursor; } void hrps_exe_operation(void) { struct hrps_env_tag *hrps_env = PRF_ENV_GET(HRPS, hrps); ASSERT_ERR(hrps_env->operation != NULL); bool finished = true; while (hrps_env->operation->cursor < BLE_CONNECTION_MAX) { // Check if notifications are enabled if ((hrps_env->hr_meas_ntf[hrps_env->operation->cursor] & ~HRP_PRF_CFG_PERFORMED_OK) == PRF_CLI_START_NTF) { // Allocate the GATT notification message struct gattc_send_evt_cmd *meas_val = KE_MSG_ALLOC_DYN(GATTC_SEND_EVT_CMD, KE_BUILD_ID(TASK_GATTC, hrps_env->operation->cursor), prf_src_task_get(&(hrps_env->prf_env), 0), gattc_send_evt_cmd, hrps_env->operation->length); // Fill in the parameter structure meas_val->operation = GATTC_NOTIFY; meas_val->handle = hrps_env->shdl + HRS_IDX_HR_MEAS_VAL; meas_val->length = hrps_env->operation->length; memcpy(meas_val->value, hrps_env->operation->data, hrps_env->operation->length); // Send the event ke_msg_send(meas_val); finished = false; hrps_env->operation->cursor++; break; } hrps_env->operation->cursor++; } // check if operation is finished if (finished) { // Send confirmation hrps_meas_send_rsp_send(GAP_ERR_NO_ERROR); // free operation ke_free(hrps_env->operation); hrps_env->operation = NULL; // go back to idle state ke_state_set(prf_src_task_get(&(hrps_env->prf_env), 0), HRPS_IDLE); } } #endif /* BLE_HR_SENSOR */ /// @} HRPS