/** **************************************************************************************** * @addtogroup HTPCTASK * @{ **************************************************************************************** */ /* * INCLUDE FILES **************************************************************************************** */ #include "rwip_config.h" #if (BLE_HT_COLLECTOR) #include "attm.h" #include "co_utils.h" #include "gap.h" #include "gapc.h" #include "gapc_task.h" #include "htpc.h" #include "htpc_task.h" #include "ke_mem.h" /// State machine used to retrieve Health Thermometer service characteristics /// information const struct prf_char_def htpc_hts_char[HTPC_CHAR_HTS_MAX] = { /// Temperature Measurement [HTPC_CHAR_HTS_TEMP_MEAS] = {ATT_CHAR_TEMPERATURE_MEAS, ATT_MANDATORY, ATT_CHAR_PROP_IND}, /// Temperature Type [HTPC_CHAR_HTS_TEMP_TYPE] = {ATT_CHAR_TEMPERATURE_TYPE, ATT_OPTIONAL, ATT_CHAR_PROP_RD}, /// Intermediate Temperature [HTPC_CHAR_HTS_INTM_TEMP] = {ATT_CHAR_INTERMED_TEMPERATURE, ATT_OPTIONAL, ATT_CHAR_PROP_NTF}, /// Measurement Interval [HTPC_CHAR_HTS_MEAS_INTV] = {ATT_CHAR_MEAS_INTERVAL, ATT_OPTIONAL, ATT_CHAR_PROP_RD}, }; /// State machine used to retrieve Health Thermometer service characteristics /// description information const struct prf_char_desc_def htpc_hts_char_desc[HTPC_DESC_HTS_MAX] = { /// Temperature Measurement Client Config [HTPC_DESC_HTS_TEMP_MEAS_CLI_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, ATT_MANDATORY, HTPC_CHAR_HTS_TEMP_MEAS}, /// Intermediate Temperature Client Config [HTPC_DESC_HTS_INTM_MEAS_CLI_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, ATT_OPTIONAL, HTPC_CHAR_HTS_INTM_TEMP}, /// Measurement Interval Client Config [HTPC_DESC_HTS_MEAS_INTV_CLI_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, ATT_OPTIONAL, HTPC_CHAR_HTS_MEAS_INTV}, /// Measurement Interval valid range [HTPC_DESC_HTS_MEAS_INTV_VAL_RGE] = {ATT_DESC_VALID_RANGE, ATT_OPTIONAL, HTPC_CHAR_HTS_MEAS_INTV}, }; /* * FUNCTION DEFINITIONS **************************************************************************************** */ /** **************************************************************************************** * @brief Handles reception of the @ref HTPC_ENABLE_REQ message. * The handler enables the Health Thermometer Profile Collector Role. * @param[in] msgid Id of the message received (probably unused). * @param[in] param Pointer to the parameters of the message. * @param[in] dest_id ID of the receiving task instance (probably unused). * @param[in] src_id ID of the sending task instance. * @return If the message was consumed or not. **************************************************************************************** */ __STATIC int htpc_enable_req_handler(ke_msg_id_t const msgid, struct htpc_enable_req const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { int msg_status = KE_MSG_CONSUMED; uint8_t status = GAP_ERR_NO_ERROR; uint8_t state = ke_state_get(dest_id); uint8_t conidx = KE_IDX_GET(dest_id); // Health Thermometer Profile Collector Role Task Environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); if ((state == HTPC_IDLE) && (htpc_env->env[conidx] == NULL)) { // allocate environment variable for task instance htpc_env->env[conidx] = (struct htpc_cnx_env *)ke_malloc( sizeof(struct htpc_cnx_env), KE_MEM_ATT_DB); memset(htpc_env->env[conidx], 0, sizeof(struct htpc_cnx_env)); // Config connection, start discovering if (param->con_type == PRF_CON_DISCOVERY) { // start discovering HTS on peer prf_disc_svc_send(&(htpc_env->prf_env), conidx, ATT_SVC_HEALTH_THERMOM); // store context of request and go into busy state htpc_env->env[conidx]->operation = ke_param2msg(param); msg_status = KE_MSG_NO_FREE; ke_state_set(dest_id, HTPC_BUSY); } // normal connection, get saved att details else { htpc_env->env[conidx]->hts = param->hts; // send APP confirmation that can start normal connection to TH htpc_enable_rsp_send(htpc_env, conidx, GAP_ERR_NO_ERROR); } } else { status = PRF_ERR_REQ_DISALLOWED; } // send an error if request fails if (status != GAP_ERR_NO_ERROR) { htpc_enable_rsp_send(htpc_env, conidx, status); } return (msg_status); } /** **************************************************************************************** * @brief Handles reception of the @ref GATTC_SDP_SVC_IND_HANDLER message. * The handler stores the found service details for service discovery. * @param[in] msgid Id of the message received (probably unused). * @param[in] param Pointer to the parameters of the message. * @param[in] dest_id ID of the receiving task instance (probably unused). * @param[in] src_id ID of the sending task instance. * @return If the message was consumed or not. **************************************************************************************** */ __STATIC int gattc_sdp_svc_ind_handler(ke_msg_id_t const msgid, struct gattc_sdp_svc_ind const *ind, ke_task_id_t const dest_id, ke_task_id_t const src_id) { uint8_t state = ke_state_get(dest_id); if (state == HTPC_BUSY) { uint8_t conidx = KE_IDX_GET(dest_id); // Get the address of the environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); ASSERT_INFO(htpc_env != NULL, dest_id, src_id); ASSERT_INFO(htpc_env->env[conidx] != NULL, dest_id, src_id); if (htpc_env->env[conidx]->nb_svc == 0) { // Retrieve IAS characteristic prf_extract_svc_info(ind, HTPC_CHAR_HTS_MAX, &htpc_hts_char[0], htpc_env->env[conidx]->hts.chars, HTPC_DESC_HTS_MAX, &htpc_hts_char_desc[0], &(htpc_env->env[conidx]->hts.descs[0])); // Even if we get multiple responses we only store 1 range htpc_env->env[conidx]->hts.svc.shdl = ind->start_hdl; htpc_env->env[conidx]->hts.svc.ehdl = ind->end_hdl; } htpc_env->env[conidx]->nb_svc++; } return (KE_MSG_CONSUMED); } /** **************************************************************************************** * @brief Handles reception of the @ref HTPC_WR_MEAS_INTV_REQ message. * Check if the handle exists in profile(already discovered) and send request, *otherwise error to APP. * @param[in] msgid Id of the message received (probably unused). * @param[in] param Pointer to the parameters of the message. * @param[in] dest_id ID of the receiving task instance (probably unused). * @param[in] src_id ID of the sending task instance. * @return If the message was consumed or not. **************************************************************************************** */ __STATIC int htpc_wr_meas_intv_req_handler( ke_msg_id_t const msgid, struct htpc_wr_meas_intv_req const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { int msg_status = KE_MSG_CONSUMED; // Status uint8_t status = GAP_ERR_NO_ERROR; uint8_t state = ke_state_get(dest_id); uint8_t conidx = KE_IDX_GET(dest_id); // Device Information Service Client Role Task Environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); ASSERT_INFO(htpc_env != NULL, dest_id, src_id); if ((state == HTPC_IDLE) && (htpc_env->env[conidx] != NULL)) { uint16_t val_hdl = ATT_INVALID_HANDLE; if ((htpc_env->env[conidx]->hts.chars[HTPC_CHAR_HTS_MEAS_INTV].prop & ATT_CHAR_PROP_WR) != ATT_CHAR_PROP_WR) { status = PRF_ERR_NOT_WRITABLE; } else { val_hdl = htpc_env->env[conidx]->hts.chars[HTPC_CHAR_HTS_MEAS_INTV].val_hdl; if (val_hdl != ATT_INVALID_HANDLE) { // Send GATT Write Request prf_gatt_write(&(htpc_env->prf_env), conidx, val_hdl, (uint8_t *)¶m->intv, sizeof(uint16_t), GATTC_WRITE); // store context of request and go into busy state htpc_env->env[conidx]->operation = ke_param2msg(param); msg_status = KE_MSG_NO_FREE; ke_state_set(dest_id, HTPC_BUSY); } else { // invalid handle requested status = PRF_ERR_INEXISTENT_HDL; } } } else if (state != HTPC_FREE) { // request cannot be performed status = PRF_ERR_REQ_DISALLOWED; } // send error response if request fails if (status != GAP_ERR_NO_ERROR) { struct htpc_wr_meas_intv_rsp *rsp = KE_MSG_ALLOC( HTPC_WR_MEAS_INTV_RSP, src_id, dest_id, htpc_wr_meas_intv_rsp); rsp->status = status; ke_msg_send(rsp); } return (msg_status); } /** **************************************************************************************** * @brief Handles reception of the @ref HTPC_HEALTH_TEMP_NTF_CFG_REQ message. * It allows configuration of the peer ind/ntf/stop characteristic for a *specified characteristic. Will return an error code if that cfg char does not *exist. * @param[in] msgid Id of the message received (probably unused). * @param[in] param Pointer to the parameters of the message. * @param[in] dest_id ID of the receiving task instance (probably unused). * @param[in] src_id ID of the sending task instance. * @return If the message was consumed or not. **************************************************************************************** */ __STATIC int htpc_health_temp_ntf_cfg_req_handler( ke_msg_id_t const msgid, struct htpc_health_temp_ntf_cfg_req const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { // Client Characteristic Configuration Descriptor Handle uint16_t cfg_hdl = 0x0000; // Status uint8_t status = GAP_ERR_NO_ERROR; int msg_status = KE_MSG_CONSUMED; // Get the address of the environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); uint8_t conidx = KE_IDX_GET(dest_id); switch (param->char_code) { case HTPC_CHAR_HTS_TEMP_MEAS: // can only IND cfg_hdl = htpc_env->env[conidx] ->hts.descs[HTPC_DESC_HTS_TEMP_MEAS_CLI_CFG] .desc_hdl; if (!((param->cfg_val == PRF_CLI_STOP_NTFIND) || (param->cfg_val == PRF_CLI_START_IND))) { status = PRF_ERR_INVALID_PARAM; } break; case HTPC_CHAR_HTS_MEAS_INTV: // can only IND cfg_hdl = htpc_env->env[conidx] ->hts.descs[HTPC_DESC_HTS_MEAS_INTV_CLI_CFG] .desc_hdl; if (!((param->cfg_val == PRF_CLI_STOP_NTFIND) || (param->cfg_val == PRF_CLI_START_IND))) { status = PRF_ERR_INVALID_PARAM; } break; case HTPC_CHAR_HTS_INTM_TEMP: // can only NTF cfg_hdl = htpc_env->env[conidx] ->hts.descs[HTPC_DESC_HTS_INTM_MEAS_CLI_CFG] .desc_hdl; if (!((param->cfg_val == PRF_CLI_STOP_NTFIND) || (param->cfg_val == PRF_CLI_START_NTF))) { status = PRF_ERR_INVALID_PARAM; } break; default: // Let app know that one of the request params is incorrect status = PRF_ERR_INVALID_PARAM; break; } // Check if the handle value exists if ((cfg_hdl == ATT_INVALID_HANDLE)) { status = PRF_ERR_INEXISTENT_HDL; } // no error detected if (status == GAP_ERR_NO_ERROR) { // Send GATT Write Request prf_gatt_write_ntf_ind(&htpc_env->prf_env, conidx, cfg_hdl, param->cfg_val); // store context of request and go into busy state htpc_env->env[conidx]->operation = ke_param2msg(param); ke_state_set(dest_id, HTPC_BUSY); msg_status = KE_MSG_NO_FREE; } // an error occurs send back response message else { struct htpc_health_temp_ntf_cfg_rsp *rsp = KE_MSG_ALLOC(HTPC_HEALTH_TEMP_NTF_CFG_RSP, src_id, dest_id, htpc_health_temp_ntf_cfg_rsp); rsp->status = status; ke_msg_send(rsp); } return (msg_status); } /** **************************************************************************************** * @brief Handles reception of the @ref HTPC_RD_REQ message. * Check if the handle exists in profile(already discovered) and send request, *otherwise error to APP. * @param[in] msgid Id of the message received (probably unused). * @param[in] param Pointer to the parameters of the message. * @param[in] dest_id ID of the receiving task instance (probably unused). * @param[in] src_id ID of the sending task instance. * @return If the message was consumed or not. **************************************************************************************** */ __STATIC int htpc_rd_char_req_handler(ke_msg_id_t const msgid, struct htpc_rd_char_req const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { int msg_status = KE_MSG_CONSUMED; // Status uint8_t status = GAP_ERR_NO_ERROR; uint8_t state = ke_state_get(dest_id); uint8_t conidx = KE_IDX_GET(dest_id); // Device Information Service Client Role Task Environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); ASSERT_INFO(htpc_env != NULL, dest_id, src_id); if ((state == HTPC_IDLE) && (htpc_env->env[conidx] != NULL)) { uint16_t search_hdl = ATT_INVALID_HDL; switch (param->char_code) { /// Read HTS Temp. Type case HTPC_RD_TEMP_TYPE: { search_hdl = htpc_env->env[conidx]->hts.chars[HTPC_CHAR_HTS_TEMP_TYPE].val_hdl; } break; /// Read HTS Measurement Interval case HTPC_RD_MEAS_INTV: { search_hdl = htpc_env->env[conidx]->hts.chars[HTPC_CHAR_HTS_MEAS_INTV].val_hdl; } break; /// Read HTS Temperature Measurement Client Cfg. Desc case HTPC_RD_TEMP_MEAS_CLI_CFG: { search_hdl = htpc_env->env[conidx] ->hts.descs[HTPC_DESC_HTS_TEMP_MEAS_CLI_CFG] .desc_hdl; } break; /// Read HTS Intermediate Temperature Client Cfg. Desc case HTPC_RD_INTM_TEMP_CLI_CFG: { search_hdl = htpc_env->env[conidx] ->hts.descs[HTPC_DESC_HTS_INTM_MEAS_CLI_CFG] .desc_hdl; } break; /// Read HTS Measurement Interval Client Cfg. Desc case HTPC_RD_MEAS_INTV_CLI_CFG: { search_hdl = htpc_env->env[conidx] ->hts.descs[HTPC_DESC_HTS_MEAS_INTV_CLI_CFG] .desc_hdl; } break; /// Read HTS Measurement Interval Client Cfg. Desc case HTPC_RD_MEAS_INTV_VAL_RGE: { search_hdl = htpc_env->env[conidx] ->hts.descs[HTPC_DESC_HTS_MEAS_INTV_VAL_RGE] .desc_hdl; } break; default: { status = GAP_ERR_INVALID_PARAM; } break; } // Check if handle is viable if (search_hdl != ATT_INVALID_HDL) { // perform read request // htpc_env->env[conidx]->last_char_code = param->char_code; prf_read_char_send(&(htpc_env->prf_env), conidx, htpc_env->env[conidx]->hts.svc.shdl, htpc_env->env[conidx]->hts.svc.ehdl, search_hdl); // store context of request and go into busy state htpc_env->env[conidx]->operation = ke_param2msg(param); msg_status = KE_MSG_NO_FREE; ke_state_set(dest_id, HTPC_BUSY); } else if (status == GAP_ERR_NO_ERROR) { // invalid handle requested status = PRF_ERR_INEXISTENT_HDL; } } else if (state != HTPC_FREE) { // request cannot be performed status = PRF_ERR_REQ_DISALLOWED; } // send error response if request fails if (status != GAP_ERR_NO_ERROR) { prf_client_att_info_rsp(&(htpc_env->prf_env), conidx, HTPC_RD_CHAR_RSP, status, NULL); } return (msg_status); } /** **************************************************************************************** * @brief Handles reception of the @ref GATTC_READ_IND message. * Generic event received after every simple read command sent to peer server. * @param[in] msgid Id of the message received (probably unused). * @param[in] param Pointer to the parameters of the message. * @param[in] dest_id ID of the receiving task instance (probably unused). * @param[in] src_id ID of the sending task instance. * @return If the message was consumed or not. **************************************************************************************** */ __STATIC int gattc_read_ind_handler(ke_msg_id_t const msgid, struct gattc_read_ind const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { // Get the address of the environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); uint8_t conidx = KE_IDX_GET(dest_id); // send attribute information prf_client_att_info_rsp(&(htpc_env->prf_env), conidx, HTPC_RD_CHAR_RSP, GAP_ERR_NO_ERROR, param); return (KE_MSG_CONSUMED); } /** **************************************************************************************** * @brief Handles reception of the @ref GATTC_CMP_EVT message. * This generic event is received for different requests, so need to keep track. * @param[in] msgid Id of the message received (probably unused). * @param[in] param Pointer to the parameters of the message. * @param[in] dest_id ID of the receiving task instance (probably unused). * @param[in] src_id ID of the sending task instance. * @return If the message was consumed or not. **************************************************************************************** */ __STATIC int gattc_cmp_evt_handler(ke_msg_id_t const msgid, struct gattc_cmp_evt const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { uint8_t state = ke_state_get(dest_id); uint8_t conidx = KE_IDX_GET(dest_id); // Get the address of the environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); if (state == HTPC_BUSY) { switch (htpc_env->env[conidx]->operation->id) { case HTPC_ENABLE_REQ: { uint8_t status; if (htpc_env->env[conidx]->nb_svc == 1) { status = prf_check_svc_char_validity( HTPC_CHAR_HTS_MAX, htpc_env->env[conidx]->hts.chars, htpc_hts_char); htpc_env->env[conidx]->nb_svc = 0; } // Too many services found only one such service should exist else if (htpc_env->env[conidx]->nb_svc > 1) { status = PRF_ERR_MULTIPLE_SVC; } else { status = PRF_ERR_STOP_DISC_CHAR_MISSING; } htpc_enable_rsp_send(htpc_env, conidx, status); } break; case HTPC_HEALTH_TEMP_NTF_CFG_REQ: { // send response of the notification configuration request struct htpc_health_temp_ntf_cfg_rsp *rsp = KE_MSG_ALLOC(HTPC_HEALTH_TEMP_NTF_CFG_RSP, prf_dst_task_get(&(htpc_env->prf_env), conidx), dest_id, htpc_health_temp_ntf_cfg_rsp); // set error status rsp->status = param->status; ke_msg_send(rsp); } break; case HTPC_RD_CHAR_REQ: { if (param->status != GAP_ERR_NO_ERROR) { // send attribute information prf_client_att_info_rsp(&(htpc_env->prf_env), conidx, HTPC_RD_CHAR_RSP, param->status, NULL); } } break; case HTPC_WR_MEAS_INTV_REQ: { struct htpc_wr_meas_intv_rsp *rsp = KE_MSG_ALLOC( HTPC_WR_MEAS_INTV_RSP, prf_dst_task_get(&(htpc_env->prf_env), conidx), dest_id, htpc_wr_meas_intv_rsp); rsp->status = param->status; ke_msg_send(rsp); } break; default: break; } // free operation if ((htpc_env->env[conidx] != NULL) && (htpc_env->env[conidx]->operation != NULL)) { ke_free(htpc_env->env[conidx]->operation); htpc_env->env[conidx]->operation = NULL; } // set state to IDLE ke_state_set(dest_id, HTPC_IDLE); } return (KE_MSG_CONSUMED); } /** **************************************************************************************** * @brief Handles reception of the @ref GATTC_EVENT_IND message. * @param[in] msgid Id of the message received (probably unused). * @param[in] param Pointer to the parameters of the message. * @param[in] dest_id ID of the receiving task instance (probably unused). * @param[in] src_id ID of the sending task instance. * @return If the message was consumed or not. **************************************************************************************** */ __STATIC int gattc_event_ind_handler(ke_msg_id_t const msgid, struct gattc_event_ind const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { // Get the address of the environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); uint8_t conidx = KE_IDX_GET(dest_id); // Temperature Measurement Char. if (param->handle == htpc_env->env[conidx]->hts.chars[HTPC_CHAR_HTS_TEMP_MEAS].val_hdl) { // confirm that indication has been correctly received struct gattc_event_cfm *cfm = KE_MSG_ALLOC(GATTC_EVENT_CFM, src_id, dest_id, gattc_event_cfm); cfm->handle = param->handle; ke_msg_send(cfm); htpc_unpack_temp( htpc_env, (uint8_t *)&(param->value), param->length, ((param->type == GATTC_NOTIFY) ? HTP_TEMP_INTERM : HTP_TEMP_STABLE), conidx); } // Intermediate Temperature Measurement Char. else if (param->handle == htpc_env->env[conidx]->hts.chars[HTPC_CHAR_HTS_INTM_TEMP].val_hdl) { htpc_unpack_temp( htpc_env, (uint8_t *)&(param->value), param->length, ((param->type == GATTC_NOTIFY) ? HTP_TEMP_INTERM : HTP_TEMP_STABLE), conidx); } // Measurement Interval Char. else if (param->handle == htpc_env->env[conidx]->hts.chars[HTPC_CHAR_HTS_MEAS_INTV].val_hdl) { // confirm that indication has been correctly received struct gattc_event_cfm *cfm = KE_MSG_ALLOC(GATTC_EVENT_CFM, src_id, dest_id, gattc_event_cfm); cfm->handle = param->handle; ke_msg_send(cfm); struct htpc_meas_intv_ind *ind = KE_MSG_ALLOC( HTPC_MEAS_INTV_IND, prf_dst_task_get(&htpc_env->prf_env, conidx), prf_src_task_get(&htpc_env->prf_env, conidx), htpc_meas_intv_ind); memcpy(&ind->intv, ¶m->value[0], sizeof(uint16_t)); ke_msg_send(ind); } return (KE_MSG_CONSUMED); } /* * GLOBAL VARIABLE DEFINITIONS **************************************************************************************** */ /* Default State handlers definition. */ KE_MSG_HANDLER_TAB(htpc){ {HTPC_ENABLE_REQ, (ke_msg_func_t)htpc_enable_req_handler}, {GATTC_SDP_SVC_IND, (ke_msg_func_t)gattc_sdp_svc_ind_handler}, {HTPC_HEALTH_TEMP_NTF_CFG_REQ, (ke_msg_func_t)htpc_health_temp_ntf_cfg_req_handler}, {HTPC_WR_MEAS_INTV_REQ, (ke_msg_func_t)htpc_wr_meas_intv_req_handler}, {HTPC_RD_CHAR_REQ, (ke_msg_func_t)htpc_rd_char_req_handler}, {GATTC_READ_IND, (ke_msg_func_t)gattc_read_ind_handler}, {GATTC_CMP_EVT, (ke_msg_func_t)gattc_cmp_evt_handler}, {GATTC_EVENT_IND, (ke_msg_func_t)gattc_event_ind_handler}, {GATTC_EVENT_REQ_IND, (ke_msg_func_t)gattc_event_ind_handler}, }; void htpc_task_init(struct ke_task_desc *task_desc) { // Get the address of the environment struct htpc_env_tag *htpc_env = PRF_ENV_GET(HTPC, htpc); task_desc->msg_handler_tab = htpc_msg_handler_tab; task_desc->msg_cnt = ARRAY_LEN(htpc_msg_handler_tab); task_desc->state = htpc_env->state; task_desc->idx_max = HTPC_IDX_MAX; } #endif // BLE_HT_COLLECTOR /// @} HTPCTASK