Exemple #1
0
/**
 ****************************************************************************************
 * @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 status;
    // Get the address of the environment
    struct tipc_env_tag *tipc_env = PRF_CLIENT_GET_ENV(dest_id, tipc);
    if(state == TIPC_DISCOVERING)
    {
        if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND)||
                (param->status == ATT_ERR_NO_ERROR))
        {
            // Currently Discovering Current Time service characteristics.
            if(tipc_env->last_svc_req  == ATT_SVC_CURRENT_TIME)
            {
                // service start/end handles has been received
                if(tipc_env->last_uuid_req == ATT_SVC_CURRENT_TIME)
                {
                    // check if service handles are not ok
                    if (tipc_env->cts.svc.shdl == ATT_INVALID_HANDLE)
                    {
                        // stop discovery procedure.
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
                    }
                    //too many services found only one such service should exist
                    else if(tipc_env->nb_svc != 1)
                    {
                        // stop discovery procedure.
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                    }
                    // check if service handles are ok
                    else
                    {
                        //discover all CTS characteristics
                        prf_disc_char_all_send(&(tipc_env->con_info), &(tipc_env->cts.svc));
                        tipc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                    }

                }
                else if(tipc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
                {
                    status = prf_check_svc_char_validity(TIPC_CHAR_CTS_MAX, tipc_env->cts.chars,
                            tipc_cts_char);

                    //  check for characteristic properties.
                    if(status == PRF_ERR_OK)
                    {
                        tipc_env->last_uuid_req = ATT_INVALID_HANDLE;
                        tipc_env->last_char_code = TIPC_CHAR_CTS_CURR_TIME;
                        //request all service characteristic description for CTS
                        prf_disc_char_desc_send(&(tipc_env->con_info), &(tipc_env->cts.chars[TIPC_CHAR_CTS_CURR_TIME]));
                    }
                    else
                    {
                        // stop discovery procedure.
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, status);
                    }
                }
                else
                {
                    status = prf_check_svc_char_desc_validity(TIPC_DESC_CTS_MAX,
                            tipc_env->cts.descs,
                            tipc_cts_char_desc,
                            tipc_env->cts.chars);

                    if(status == PRF_ERR_OK)
                    {
                        // reset number of services
                        tipc_env->nb_svc = 0;

                        //start discovering NDCS on peer
                        prf_disc_svc_send(&(tipc_env->con_info), ATT_SVC_NEXT_DST_CHANGE);

                        tipc_env->last_uuid_req = ATT_SVC_NEXT_DST_CHANGE;
                        tipc_env->last_svc_req  = ATT_SVC_NEXT_DST_CHANGE;
                    }
                    else
                    {
                        // stop discovery procedure.
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, status);
                    }
                }
            }

            // Currently Next DST Change service characteristics.
            else if(tipc_env->last_svc_req  == ATT_SVC_NEXT_DST_CHANGE)
            {
                // service start/end handles has been received
                if(tipc_env->last_uuid_req == ATT_SVC_NEXT_DST_CHANGE)
                {
                    if (tipc_env->ndcs.svc.shdl == ATT_INVALID_HANDLE)
                    {
                        // reset number of services
                        tipc_env->nb_svc = 0;

                        //start discovering RTUS on peer
                        prf_disc_svc_send(&(tipc_env->con_info), ATT_SVC_REF_TIME_UPDATE);

                        tipc_env->last_uuid_req = ATT_SVC_REF_TIME_UPDATE;
                        tipc_env->last_svc_req  = ATT_SVC_REF_TIME_UPDATE;
                    }
                    //too many services found only one such service should exist
                    else if(tipc_env->nb_svc != 1)
                    {
                        // stop discovery procedure.
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                    }
                    // check if service handles are ok
                    else
                    {
                        //discover all NDCS characteristics
                        prf_disc_char_all_send(&(tipc_env->con_info), &(tipc_env->ndcs.svc));
                        tipc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                    }
                }
                else if(tipc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
                {
                    status = prf_check_svc_char_validity(TIPC_CHAR_NDCS_MAX, tipc_env->ndcs.chars,
                            tipc_ndcs_char);

                    //  check for characteristic properties.
                    if(status == PRF_ERR_OK)
                    {
                        // reset number of services
                        tipc_env->nb_svc = 0;

                        //start discovering RTUS on peer
                        prf_disc_svc_send(&(tipc_env->con_info), ATT_SVC_REF_TIME_UPDATE);

                        tipc_env->last_uuid_req = ATT_SVC_REF_TIME_UPDATE;
                        tipc_env->last_svc_req  = ATT_SVC_REF_TIME_UPDATE;
                    }
                    else
                    {
                        // stop discovery procedure.
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, status);
                    }
                }
            }

            // Currently Reference Time Update service characteristics.
            else if(tipc_env->last_svc_req  == ATT_SVC_REF_TIME_UPDATE)
            {
                // service start/end handles has been received
                if(tipc_env->last_uuid_req == ATT_SVC_REF_TIME_UPDATE)
                {
                    if (tipc_env->rtus.svc.shdl == ATT_INVALID_HANDLE)
                    {
                        // send app the details about the discovered TIPS DB to save
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_OK);
                    }
                    //too many services found only one such service should exist
                    else if(tipc_env->nb_svc != 1)
                    {
                        // stop discovery procedure.
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                    }
                    // check if service handles are ok
                    else
                    {
                        //discover all RTUS characteristics
                        prf_disc_char_all_send(&(tipc_env->con_info), &(tipc_env->rtus.svc));
                        tipc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                    }
                }
                else if(tipc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
                {
                    status = prf_check_svc_char_validity(TIPC_CHAR_RTUS_MAX, tipc_env->rtus.chars,
                            tipc_rtus_char);

                    //  check for characteristic properties.
                    if(status == PRF_ERR_OK)
                    {
                        // send app the details about the discovered TIPS DB to save
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_OK);
                    }
                    else
                    {
                        // stop discovery procedure.
                        tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, status);
                    }
                }
            }
        }
    }
    else if(state == TIPC_CONNECTED)
    {
        switch(param->req_type)
        {
            case GATTC_WRITE:
            case GATTC_WRITE_NO_RESPONSE:
            {
                struct tipc_wr_char_rsp *wr_cfm = KE_MSG_ALLOC(TIPC_WR_CHAR_RSP,
                        tipc_env->con_info.appid, dest_id, tipc_wr_char_rsp);

                wr_cfm->conhdl    = gapc_get_conhdl(tipc_env->con_info.conidx);
                //it will be a GATT status code
                wr_cfm->status    = param->status;
                // send the message
                ke_msg_send(wr_cfm);
            }
            break;
            case GATTC_READ:
            {
                // an error occurs while reading peer attribute, inform app
                if(param->status != GATT_ERR_NO_ERROR)
                {
                    tipc_error_ind_send(tipc_env, param->status);
                }
            }
            break;
            default: break;
        }
    }
    return (KE_MSG_CONSUMED);
}
Exemple #2
0
/**
 ****************************************************************************************
 * @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);
    // Get the address of the environment
    struct scppc_env_tag *scppc_env = PRF_CLIENT_GET_ENV(dest_id, scppc);
    uint8_t status = PRF_ERR_OK;

    if(state == SCPPC_DISCOVERING)
    {
        if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND) ||
                (param->status == ATT_ERR_NO_ERROR))
        {
            // Service start/end handles has been received
            if(scppc_env->last_uuid_req == ATT_SVC_SCAN_PARAMETERS)
            {
                // check if service handles are not ok
                if(scppc_env->scps.svc.shdl== ATT_INVALID_HANDLE)
                {
                    // stop discovery procedure.
                    scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
                }
                // Too many services found only one such service should exist
                else if(scppc_env->nb_svc > 1)
                {
                    scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                }
                else
                {
                    // Discover all SCPS characteristics
                    prf_disc_char_all_send(&(scppc_env->con_info), &(scppc_env->scps.svc));
                    scppc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                }
            }
            else if(scppc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
            {
                status = prf_check_svc_char_validity(SCPPC_CHAR_MAX, scppc_env->scps.chars,
                        scppc_scps_char);

                if(status == PRF_ERR_OK)
                {
                    //If Scan Refresh Char. is present, discover its descriptor
                    if (scppc_env->scps.chars[SCPPC_CHAR_SCAN_REFRESH].char_hdl != ATT_INVALID_HANDLE)
                    {
                        scppc_env->last_uuid_req = ATT_INVALID_HANDLE;
                        scppc_env->last_char_code = scppc_scps_char_desc[SCPPC_DESC_SCAN_REFRESH_CFG].char_code;

                        // Discover Scan Refresh Char. Descriptor - Mandatory
                        prf_disc_char_desc_send(&(scppc_env->con_info),
                                &(scppc_env->scps.chars[scppc_env->last_char_code]));
                    }
                    else
                    {
                        scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, status);
                    }
                }
                else
                {
                    // Stop discovery procedure.
                    scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, status);
                }
            }
            else
            {
                status = prf_check_svc_char_desc_validity(SCPPC_DESC_MAX,
                        scppc_env->scps.descs,
                        scppc_scps_char_desc,
                        scppc_env->scps.chars);

                scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, status);
            }

            if (status == PRF_ERR_OK)
            {
                // Write Scan Interval Windows value
                struct scppc_scan_intv_wd_wr_req * req = KE_MSG_ALLOC(SCPPC_SCAN_INTV_WD_WR_REQ,
                        dest_id, dest_id,
                        scppc_scan_intv_wd_wr_req);

                req->conhdl = gapc_get_conhdl(scppc_env->con_info.conidx);

                co_write16p(&req->scan_intv_wd.le_scan_intv, scppc_env->scan_intv_wd.le_scan_intv);
                co_write16p(&req->scan_intv_wd.le_scan_window, scppc_env->scan_intv_wd.le_scan_window);

                ke_msg_send(req);
            }
        }
    }
    else if (state == SCPPC_CONNECTED)
    {
        switch(param->req_type)
        {
            case GATTC_WRITE:
            {
                struct scppc_wr_char_rsp *wr_cfm = KE_MSG_ALLOC(SCPPC_WR_CHAR_RSP,
                        scppc_env->con_info.appid, dest_id,
                        scppc_wr_char_rsp);

                wr_cfm->conhdl    = gapc_get_conhdl(scppc_env->con_info.conidx);
                //it will be a GATT status code
                wr_cfm->status    = param->status;

                // send the message
                ke_msg_send(wr_cfm);
            }
            break;

            case GATTC_READ:
            {
                if(param->status != GATT_ERR_NO_ERROR)
                {
                    // an error occurs while reading peer device attribute
                    scppc_error_ind_send(scppc_env, param->status);
                }
            }
            break;
            default: break;
        }
    }
    return (KE_MSG_CONSUMED);
}
Exemple #3
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATT_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 gatt_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gatt_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    // Status
    uint8_t status;
    // Get the address of the environment
    struct htpc_env_tag *htpc_env = PRF_CLIENT_GET_ENV(dest_id, htpc);

    if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND)||
        (param->status == ATT_ERR_NO_ERROR))
    {
        // Service start/end handles has been received
        if(htpc_env->last_uuid_req == ATT_SVC_HEALTH_THERMOM)
        {
            // check if service handles are not ok
            if (htpc_env->hts.svc.shdl == ATT_INVALID_HANDLE)
            {
                // stop discovery procedure.
                htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
            }
            //too many services found only one such service should exist
            else if(htpc_env->nb_svc > 1)
            {
                // stop discovery procedure.
                htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, PRF_ERR_MULTIPLE_SVC);
            }
            // check if service handles are ok
            else
            {
                //discover all HTS characteristics
                htpc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                prf_disc_char_all_send(&(htpc_env->con_info), &(htpc_env->hts.svc));
            }
        }
        else if(htpc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
        {
            status = prf_check_svc_char_validity(HTPC_CHAR_HTS_MAX, htpc_env->hts.chars,
                                                 htpc_hts_char);

            // Check for characteristic properties.
            if(status == PRF_ERR_OK)
            {
                htpc_env->last_uuid_req = ATT_INVALID_HANDLE;
                htpc_env->last_char_code = htpc_hts_char_desc[HTPC_DESC_HTS_TEMP_MEAS_CLI_CFG].char_code;
                //Discover Temperature Measurement Char. Descriptor - Mandatory
                prf_disc_char_desc_send(&(htpc_env->con_info),
                                        &(htpc_env->hts.chars[htpc_env->last_char_code]));
            }
            else
            {
                // Stop discovery procedure.
                htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, status);
            }
        }
        else //Descriptors
        {
            //Get code of next char. having descriptors
            htpc_env->last_char_code = htpc_get_next_desc_char_code(htpc_env, &htpc_hts_char_desc[0]);
            if (htpc_env->last_char_code != HTPC_DESC_HTS_MAX)
            {
                prf_disc_char_desc_send(&(htpc_env->con_info),
                        &(htpc_env->hts.chars[htpc_env->last_char_code]));
            }
            else
            {
                status = prf_check_svc_char_desc_validity(HTPC_DESC_HTS_MAX,
                                                          htpc_env->hts.descs,
                                                          htpc_hts_char_desc,
                                                          htpc_env->hts.chars);

                htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, status);
            }
        }
    }
    else
    {
        htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, param->status);
    }

    return (KE_MSG_CONSUMED);
}
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATT_CMP_EVT message.
 * @param[in] msgid Id of the message received.
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance.
 * @param[in] src_id ID of the sending task instance.
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gatt_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gatt_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    // Get the address of the environment
    struct cscpc_env_tag *cscpc_env = PRF_CLIENT_GET_ENV(dest_id, cscpc);

    if (cscpc_env != NULL)
    {
        // Status
        uint8_t status = PRF_ERR_STOP_DISC_CHAR_MISSING;

        ASSERT_ERR(cscpc_env->operation != NULL);
        ASSERT_ERR(((struct cscpc_cmd *)cscpc_env->operation)->operation == CSCPC_ENABLE_OP_CODE);

        if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND) ||
            (param->status == ATT_ERR_NO_ERROR))
        {
            /* -------------------------------------------------
             * SERVICE DISCOVERY -------------------------------
             * ------------------------------------------------- */
            if (cscpc_env->last_req == ATT_SVC_CYCLING_SPEED_CADENCE)
            {
                if (cscpc_env->nb_svc > 0)
                {
                    // Check if service handles are OK
                    if ((cscpc_env->cscs.svc.shdl != ATT_INVALID_HANDLE) &&
                        (cscpc_env->cscs.svc.ehdl != ATT_INVALID_HANDLE) &&
                        (cscpc_env->cscs.svc.shdl < cscpc_env->cscs.svc.ehdl))
                    {
                        // Status is OK
                        status = PRF_ERR_OK;

                        // Discover all CSCS characteristics
                        cscpc_env->last_req = ATT_DECL_CHARACTERISTIC;
                        prf_disc_char_all_send(&(cscpc_env->con_info), &(cscpc_env->cscs.svc));
                    }
                    // Handles are not corrects, the Cycling Speed and Cadence Service has not been found, stop
                }
                // The Cycling Speed and Cadence Service has not been found, stop discovery
            }

            /* -------------------------------------------------
             * CHARACTERISTICS DISCOVERY -----------------------
             * ------------------------------------------------- */
            else if (cscpc_env->last_req == ATT_DECL_CHARACTERISTIC)
            {
                // Check if mandatory properties have been found and if properties are correct
                status = prf_check_svc_char_validity(CSCP_CSCS_CHAR_MAX, cscpc_env->cscs.chars, cscpc_cscs_char);

                // Check for characteristic properties.
                if (status == PRF_ERR_OK)
                {
                    cscpc_env->last_req = CSCP_CSCS_CSC_MEAS_CHAR;

                    // Find the Client Characteristic Configuration Descriptor for the CSC Measurement characteristic
                    prf_disc_char_desc_send(&(cscpc_env->con_info),
                                            &(cscpc_env->cscs.chars[CSCP_CSCS_CSC_MEAS_CHAR]));
                }
            }

            /* -------------------------------------------------
             * DESCRIPTORS DISCOVERY ---------------------------
             * ------------------------------------------------- */
            else
            {
                // Discovery over ?
                bool disc_over = true;

                if (cscpc_env->last_req == CSCP_CSCS_CSC_MEAS_CHAR)
                {
                    // Check if the SC Control Point Characteristic has been found
                    if (cscpc_env->cscs.chars[CSCP_CSCS_SC_CTNL_PT_CHAR].char_hdl != ATT_INVALID_SEARCH_HANDLE)
                    {
                        // Status is OK
                        status = PRF_ERR_OK;

                        // Find the Client Characteristic Configuration Descriptor for the SC Control Point characteristic
                        cscpc_env->last_req = CSCP_CSCS_SC_CTNL_PT_CHAR;
                        prf_disc_char_desc_send(&(cscpc_env->con_info),
                                                &(cscpc_env->cscs.chars[CSCP_CSCS_SC_CTNL_PT_CHAR]));

                        // Discovery continues
                        disc_over = false;
                    }
                }
                else
                {
                    // Discovery is over
                    ASSERT_ERR(cscpc_env->last_req == CSCP_CSCS_SC_CTNL_PT_CHAR);
                }

                if (disc_over)
                {
                    status = prf_check_svc_char_desc_validity(CSCPC_DESC_MAX,
                                                              cscpc_env->cscs.descs,
                                                              cscpc_cscs_char_desc,
                                                              cscpc_env->cscs.chars);

                    if (status == PRF_ERR_OK)
                    {
                        // Reset number of services
                        cscpc_env->nb_svc = 0;

                        // Register in GATT for notifications/indications
                        prf_register_atthdl2gatt(&cscpc_env->con_info, &cscpc_env->cscs.svc);

                        // Send the content of the service to the HL
                        struct cscpc_cscs_content_ind *ind = KE_MSG_ALLOC(CSCPC_CSCS_CONTENT_IND,
                                                                          cscpc_env->con_info.appid,
                                                                          cscpc_env->con_info.prf_id,
                                                                          cscpc_cscs_content_ind);

                        ind->conhdl = cscpc_env->con_info.conhdl;
                        memcpy(&ind->cscs, &cscpc_env->cscs, sizeof(struct cscpc_cscs_content));

                        // Send the message
                        ke_msg_send(ind);

                        // Stop discovery procedure.
                        cscpc_send_cmp_evt(cscpc_env, CSCPC_ENABLE_OP_CODE, status);
                    }
                }
            }
        }
        else
        {
            status = param->status;
        }

        if (status != PRF_ERR_OK)
        {
            // Stop discovery procedure.
            cscpc_send_cmp_evt(cscpc_env, CSCPC_ENABLE_OP_CODE, status);
        }
    }
    // else ignore the message

    return (KE_MSG_CONSUMED);
}
Exemple #5
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATT_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 gatt_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gatt_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    uint8_t status = 0;
    // Get the address of the environment
    struct qppc_env_tag *qppc_env = PRF_CLIENT_GET_ENV(dest_id, qppc);

    if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND)||
        (param->status == ATT_ERR_NO_ERROR))
    {
        // service start/end handles has been received
        if(qppc_env->last_uuid_req == ATT_DECL_PRIMARY_SERVICE)
        {
            // check if service handles are not ok
            if(qppc_env->qpps.svc.shdl == ATT_INVALID_HANDLE)
            {
                // stop discovery procedure.
                qppc_enable_cfm_send(qppc_env, &qppc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
            }
            //too many services found only one such service should exist
            else if(qppc_env->nb_svc != 1)
            {
                // stop discovery procedure.
                qppc_enable_cfm_send(qppc_env, &qppc_env->con_info, PRF_ERR_MULTIPLE_SVC);
            }
            else
            {
                //discover all QPPS characteristics
                prf_disc_char_all_send(&(qppc_env->con_info), &(qppc_env->qpps.svc));
                qppc_env->nb_char = 0;
                qppc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
            }
        }
        else if(qppc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
        {
            status = prf_check_svc_128_char_validity(2, qppc_env->qpps.chars,
                                                 qppc_qpps_char);

            // Check for characteristic properties.
            if(status == PRF_ERR_OK)
            {
                qppc_env->last_uuid_req = ATT_DESC_CHAR_USER_DESCRIPTION;
                qppc_env->last_char_code = qppc_qpps_char_desc[QPPC_QPPS_RX_CHAR_VALUE_USER_DESP].char_code;

                //Discover Quintic Private profile Configuration Descriptor - Mandatory
                prf_disc_char_desc_send(&(qppc_env->con_info),
                                        &(qppc_env->qpps.chars[qppc_env->last_char_code]));
            }
            else
            {
                // Stop discovery procedure.
                qppc_enable_cfm_send(qppc_env, &qppc_env->con_info, status);
            }
        }
        else if (qppc_env->last_uuid_req == ATT_DESC_CHAR_USER_DESCRIPTION)
        {
            qppc_env->last_uuid_req = ATT_DESC_CLIENT_CHAR_CFG;
            qppc_env->last_char_code = qppc_qpps_char_desc[QPPC_QPPS_FIRST_TX_VALUE_CLI_CFG].char_code;
            //Discover Quintic Private profile User Descriptor
            prf_disc_char_desc_send(&(qppc_env->con_info),
                                    &(qppc_env->qpps.chars[qppc_env->last_char_code]));
        }
        else if (qppc_env->last_uuid_req == ATT_DESC_CLIENT_CHAR_CFG)
        {
            if (qppc_env->last_char_code < qppc_env->nb_char)
            {
                prf_disc_char_desc_send(&(qppc_env->con_info),
                                        &(qppc_env->qpps.chars[++qppc_env->last_char_code]));
            }
            else
            {
                status = prf_check_svc_char_desc_validity(2,
                                                          qppc_env->qpps.descs,
                                                          qppc_qpps_char_desc,
                                                          qppc_env->qpps.chars);
                qppc_enable_cfm_send(qppc_env, &qppc_env->con_info, status);
            }
        }
    }

    return (KE_MSG_CONSUMED);
}
Exemple #6
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATT_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 gatt_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gatt_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    // Get the address of the environment
    struct scppc_env_tag *scppc_env = PRF_CLIENT_GET_ENV(dest_id, scppc);

    uint8_t status = PRF_ERR_OK;

    if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND) ||
        (param->status == ATT_ERR_NO_ERROR))
    {
        // Service start/end handles has been received
        if(scppc_env->last_uuid_req == ATT_SVC_SCAN_PARAMETERS)
        {
            // check if service handles are not ok
            if(scppc_env->scps.svc.shdl== ATT_INVALID_HANDLE)
            {
                // stop discovery procedure.
                scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
            }
            // Too many services found only one such service should exist
            else if(scppc_env->nb_svc > 1)
            {
                scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, PRF_ERR_MULTIPLE_SVC);
            }
            else
            {
                // Discover all SCPS characteristics
                prf_disc_char_all_send(&(scppc_env->con_info), &(scppc_env->scps.svc));
                scppc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
            }
        }
        else if(scppc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
        {
            status = prf_check_svc_char_validity(SCPPC_CHAR_MAX, scppc_env->scps.chars,
                                                 scppc_scps_char);

            if(status == PRF_ERR_OK)
            {
                //If Scan Refresh Char. is present, discover its descriptor
                if (scppc_env->scps.chars[SCPPC_CHAR_SCAN_REFRESH].char_hdl != ATT_INVALID_HANDLE)
                {
                    scppc_env->last_uuid_req = ATT_INVALID_HANDLE;
                    scppc_env->last_char_code = scppc_scps_char_desc[SCPPC_DESC_SCAN_REFRESH_CFG].char_code;

                    // Discover Scan Refresh Char. Descriptor - Mandatory
                    prf_disc_char_desc_send(&(scppc_env->con_info),
                                            &(scppc_env->scps.chars[scppc_env->last_char_code]));
                }
                else
                {
                    scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, status);
                }
            }
            else
            {
                // Stop discovery procedure.
                scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, status);
            }
        }
        else
        {
            status = prf_check_svc_char_desc_validity(SCPPC_DESC_MAX,
                                                      scppc_env->scps.descs,
                                                      scppc_scps_char_desc,
                                                      scppc_env->scps.chars);

            scppc_enable_cfm_send(scppc_env, &scppc_env->con_info, status);
        }

        if (status == PRF_ERR_OK)
        {
            // Write Scan Interval Windows value
            struct scppc_scan_intv_wd_wr_req * req = KE_MSG_ALLOC(SCPPC_SCAN_INTV_WD_WR_REQ,
                                                                  dest_id, dest_id,
                                                                  scppc_scan_intv_wd_wr_req);

            req->conhdl = scppc_env->con_info.conhdl;

            co_write16p(&req->scan_intv_wd.le_scan_intv, scppc_env->scan_intv_wd.le_scan_intv);
            co_write16p(&req->scan_intv_wd.le_scan_window, scppc_env->scan_intv_wd.le_scan_window);

            ke_msg_send(req);
        }
    }

    return (KE_MSG_CONSUMED);
}
Exemple #7
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATTC_CMP_EVT message.
 * @param[in] msgid Id of the message received.
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance.
 * @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)
{
    // Get the address of the environment
    struct anpc_env_tag *anpc_env = PRF_CLIENT_GET_ENV(dest_id, anpc);

    if (anpc_env != NULL)
    {
        switch (param->req_type)
        {
            case (GATTC_DISC_BY_UUID_SVC):
            case (GATTC_DISC_ALL_CHAR):
            case (GATTC_DISC_DESC_CHAR):
            {
                // Status
                uint8_t status = PRF_ERR_STOP_DISC_CHAR_MISSING;

                if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND) ||
                    (param->status == ATT_ERR_NO_ERROR))
                {
                    ASSERT_ERR(((struct anpc_cmd *)anpc_env->operation)->operation == ANPC_ENABLE_OP_CODE);

                    /* -------------------------------------------------
                     * SERVICE DISCOVERY -------------------------------
                     * ------------------------------------------------- */
                    if (anpc_env->last_req == ATT_SVC_ALERT_NTF)
                    {
                        if (anpc_env->nb_svc > 0)
                        {
                            // Check if service handles are OK
                            if ((anpc_env->ans.svc.shdl != ATT_INVALID_HANDLE) &&
                                (anpc_env->ans.svc.ehdl != ATT_INVALID_HANDLE) &&
                                (anpc_env->ans.svc.shdl < anpc_env->ans.svc.ehdl))
                            {
                                // Status is OK
                                status = PRF_ERR_OK;

                                // Discover all ANS characteristics
                                prf_disc_char_all_send(&(anpc_env->con_info), &(anpc_env->ans.svc));
                                anpc_env->last_req = ATT_DECL_CHARACTERISTIC;
                            }
                            // Handles are not corrects, the Phone Alert Status Service has not been found, stop
                        }
                        // The Phone Alert Status Service has not been found, stop discovery
                    }
                    /* -------------------------------------------------
                     * CHARACTERISTICS DISCOVERY -----------------------
                     * ------------------------------------------------- */
                    else if (anpc_env->last_req == ATT_DECL_CHARACTERISTIC)
                    {
                        // Check if mandatory properties have been found and if properties are correct
                        status = prf_check_svc_char_validity(ANPC_CHAR_MAX, anpc_env->ans.chars, anpc_ans_char);

                        // Check for characteristic properties.
                        if (status == PRF_ERR_OK)
                        {
                            anpc_env->last_req = ANPC_CHAR_NEW_ALERT;

                            // Find the Client Characteristic Configuration Descriptor for the New Alert characteristic
                            prf_disc_char_desc_send(&(anpc_env->con_info),
                                                    &(anpc_env->ans.chars[ANPC_CHAR_NEW_ALERT]));
                        }
                    }
                    /* -------------------------------------------------
                     * DESCRIPTORS DISCOVERY ---------------------------
                     * ------------------------------------------------- */
                    else
                    {
                        if (anpc_env->last_req == ANPC_CHAR_NEW_ALERT)
                        {
                            status = PRF_ERR_OK;

                            anpc_env->last_req = ANPC_CHAR_UNREAD_ALERT_STATUS;

                            // Find the Client Characteristic Configuration Descriptor for the Unread Alert Status characteristic
                            prf_disc_char_desc_send(&(anpc_env->con_info),
                                                    &(anpc_env->ans.chars[ANPC_CHAR_UNREAD_ALERT_STATUS]));
                        }
                        else if (anpc_env->last_req == ANPC_CHAR_UNREAD_ALERT_STATUS)
                        {
                            status = prf_check_svc_char_desc_validity(ANPC_DESC_MAX,
                                                                      anpc_env->ans.descs,
                                                                      anpc_ans_char_desc,
                                                                      anpc_env->ans.chars);

                            if (status == PRF_ERR_OK)
                            {
                                // Reset number of services
                                anpc_env->nb_svc = 0;

                                // Register in GATT for notifications/indications
                                prf_register_atthdl2gatt(&anpc_env->con_info, &anpc_env->ans.svc);

                                // Send the content of the service to the HL
                                struct anpc_ans_content_ind *ind = KE_MSG_ALLOC(ANPC_ANS_CONTENT_IND,
                                                                                anpc_env->con_info.appid,
                                                                                anpc_env->con_info.prf_id,
                                                                                anpc_ans_content_ind);

                                ind->conhdl = gapc_get_conhdl(anpc_env->con_info.conidx);
                                memcpy(&ind->ans, &anpc_env->ans, sizeof(struct anpc_ans_content));

                                // Send the message
                                ke_msg_send(ind);

                                // Force the operation value
                                ((struct anpc_enable_cmd *)anpc_env->operation)->operation = ANPC_ENABLE_RD_NEW_ALERT_OP_CODE;
                                // Check Supported New Alert Categories
                                prf_read_char_send(&(anpc_env->con_info), anpc_env->ans.svc.shdl,
                                                   anpc_env->ans.svc.ehdl, anpc_env->ans.chars[ANPC_CHAR_SUP_NEW_ALERT_CAT].val_hdl);
                            }
                        }
                        else
                        {
                            ASSERT_ERR(0);
                        }
                    }
                }
                else
                {
                    status = param->status;
                }

                if (status != PRF_ERR_OK)
                {
                    // Stop discovery procedure.
                    anpc_send_cmp_evt(anpc_env, ANPC_ENABLE_OP_CODE, status);
                }
            } break;

            case (GATTC_WRITE):
            {
                // Retrieve the operation currently performed
                uint8_t operation = ((struct anpc_cmd *)anpc_env->operation)->operation;

                switch (operation)
                {
                    case (ANPC_WRITE_OP_CODE):
                    {
                        uint8_t wr_code = ((struct anpc_write_cmd *)anpc_env->operation)->write_code;

                        if ((wr_code == ANPC_RD_WR_NEW_ALERT_CFG) ||
                            (wr_code == ANPC_RD_WR_UNREAD_ALERT_STATUS_CFG) ||
                            (wr_code == ANPC_WR_ALERT_NTF_CTNL_PT))
                        {
                            anpc_send_cmp_evt(anpc_env, ANPC_WRITE_OP_CODE, param->status);
                        }
                        else
                        {
                            ASSERT_ERR(0);
                        }
                    } break;

                    case (ANPC_ENABLE_WR_NEW_ALERT_OP_CODE):
                    {
                        // Look for the next category to enable
                        if (anpc_found_next_alert_cat(anpc_env, ((struct anpc_enable_cmd *)anpc_env->operation)->new_alert_enable))
                        {
                            // Enable sending of notifications for the found category ID
                            anpc_write_alert_ntf_ctnl_pt(anpc_env, CMD_ID_EN_NEW_IN_ALERT_NTF, anpc_env->last_req - 1);
                        }
                        else
                        {
                            // Force the operation value
                            ((struct anpc_cmd *)anpc_env->operation)->operation = ANPC_ENABLE_WR_NTF_NEW_ALERT_OP_CODE;
                            // Send a "Notify New Alert Immediately" command with the Category ID field set to 0xFF
                            anpc_write_alert_ntf_ctnl_pt(anpc_env, CMD_ID_NTF_NEW_IN_ALERT_IMM, CAT_ID_ALL_SUPPORTED_CAT);
                        }
                    } break;

                    case (ANPC_ENABLE_WR_NTF_NEW_ALERT_OP_CODE):
                    {
                        // Reset the environment
                        anpc_env->last_req = CAT_ID_SPL_ALERT;

                        if (anpc_found_next_alert_cat(anpc_env, ((struct anpc_enable_cmd *)anpc_env->operation)->unread_alert_enable))
                        {
                            // Force the operation value
                            ((struct anpc_cmd *)anpc_env->operation)->operation = ANPC_ENABLE_WR_UNREAD_ALERT_OP_CODE;
                            // Enable sending of notifications for the found category ID
                            anpc_write_alert_ntf_ctnl_pt(anpc_env, CMD_ID_EN_UNREAD_CAT_STATUS_NTF, anpc_env->last_req - 1);
                        }
                        else
                        {
                            anpc_send_cmp_evt(anpc_env, ANPC_ENABLE_OP_CODE, PRF_ERR_OK);
                        }
                    } break;

                    case (ANPC_ENABLE_WR_UNREAD_ALERT_OP_CODE):
                    {
                        // Look for the next category to enable
                        if (anpc_found_next_alert_cat(anpc_env, ((struct anpc_enable_cmd *)anpc_env->operation)->unread_alert_enable))
                        {
                            // Enable sending of notifications for the found category ID
                            anpc_write_alert_ntf_ctnl_pt(anpc_env, CMD_ID_EN_UNREAD_CAT_STATUS_NTF, anpc_env->last_req - 1);
                        }
                        else
                        {
                            // Force the operation value
                            ((struct anpc_cmd *)anpc_env->operation)->operation = ANPC_ENABLE_WR_NTF_UNREAD_ALERT_OP_CODE;
                            // Send a "Notify New Alert Immediately" command with the Category ID field set to 0xFF
                            anpc_write_alert_ntf_ctnl_pt(anpc_env, CMD_ID_NTF_UNREAD_CAT_STATUS_IMM, CAT_ID_ALL_SUPPORTED_CAT);
                        }
                    } break;

                    case (ANPC_ENABLE_WR_NTF_UNREAD_ALERT_OP_CODE):
                    {
                        // The discovery procedure is over
                        anpc_send_cmp_evt(anpc_env, ANPC_ENABLE_OP_CODE, param->status);
                    } break;

                    default:
                    {
                        ASSERT_ERR(0);
                    } break;
                }
            } break;

            case (GATTC_READ):
            {
                switch (((struct anpc_cmd *)anpc_env->operation)->operation)
                {
                    // Read Supported New Alert Category Characteristic value
                    case (ANPC_ENABLE_RD_NEW_ALERT_OP_CODE):
                    {
                        if (param->status == GATT_ERR_NO_ERROR)
                        {
                            // Force the operation value
                            ((struct anpc_cmd *)anpc_env->operation)->operation = ANPC_ENABLE_RD_UNREAD_ALERT_OP_CODE;
                            // Check Supported Unread Alert Category
                            prf_read_char_send(&(anpc_env->con_info),
                                               anpc_env->ans.svc.shdl, anpc_env->ans.svc.ehdl,
                                               anpc_env->ans.chars[ANPC_CHAR_SUP_UNREAD_ALERT_CAT].val_hdl);
                        }
                        else
                        {
                            anpc_send_cmp_evt(anpc_env, ANPC_ENABLE_OP_CODE, param->status);
                        }
                    } break;

                    case (ANPC_ENABLE_RD_UNREAD_ALERT_OP_CODE):
                    {
                        if (param->status == GATT_ERR_NO_ERROR)
                        {
                            // If peer device was unknown, stop the procedure
                            if (((struct anpc_enable_cmd *)anpc_env->operation)->con_type == PRF_CON_DISCOVERY)
                            {
                                anpc_send_cmp_evt(anpc_env, ANPC_ENABLE_OP_CODE, PRF_ERR_OK);
                            }
                            // If peer device was already known (bonded), start Recovery from Connection Loss procedure
                            else
                            {
                                // Reset the environment
                                anpc_env->last_req = CAT_ID_SPL_ALERT;

                                if (anpc_found_next_alert_cat(anpc_env, ((struct anpc_enable_cmd *)anpc_env->operation)->new_alert_enable))
                                {
                                    // Force the operation value
                                    ((struct anpc_cmd *)anpc_env->operation)->operation = ANPC_ENABLE_WR_NEW_ALERT_OP_CODE;
                                    // Enable sending of notifications for the found category ID
                                    anpc_write_alert_ntf_ctnl_pt(anpc_env, CMD_ID_EN_NEW_IN_ALERT_NTF, anpc_env->last_req - 1);
                                }
                                else
                                {
                                    // Reset the environment
                                    anpc_env->last_req = CAT_ID_SPL_ALERT;

                                    if (anpc_found_next_alert_cat(anpc_env, ((struct anpc_enable_cmd *)anpc_env->operation)->unread_alert_enable))
                                    {
                                        // Force the operation value
                                        ((struct anpc_cmd *)anpc_env->operation)->operation = ANPC_ENABLE_WR_UNREAD_ALERT_OP_CODE;
                                        // Enable sending of notifications for the found category ID
                                        anpc_write_alert_ntf_ctnl_pt(anpc_env, CMD_ID_EN_UNREAD_CAT_STATUS_NTF, anpc_env->last_req - 1);
                                    }
                                    else
                                    {
                                        anpc_send_cmp_evt(anpc_env, ANPC_ENABLE_OP_CODE, PRF_ERR_OK);
                                    }
                                }
                            }
                        }
                        else
                        {
                            anpc_send_cmp_evt(anpc_env, ANPC_ENABLE_OP_CODE, param->status);
                        }
                    } break;

                    case (ANPC_READ_OP_CODE):
                    {
                        // Inform the requester that the read procedure is over
                        anpc_send_cmp_evt(anpc_env, ANPC_READ_OP_CODE, param->status);
                    } break;

                    default:
                    {
                        ASSERT_ERR(0);
                    }
                }
            } break;

            case (GATTC_REGISTER):
            case (GATTC_UNREGISTER):
            {
                // Do nothing
            } break;

            default:
            {
                ASSERT_ERR(0);
            } break;
        }
    }
    // else ignore the message

    return (KE_MSG_CONSUMED);
}
Exemple #8
0
/**
 ****************************************************************************************
 * @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);
    // Status
    uint8_t status;
    // Get the address of the environment
    struct htpc_env_tag *htpc_env = PRF_CLIENT_GET_ENV(dest_id, htpc);

    if(state == HTPC_DISCOVERING)
    {
        if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND)||
                (param->status == ATT_ERR_NO_ERROR))
        {
            // Service start/end handles has been received
            if(htpc_env->last_uuid_req == ATT_SVC_HEALTH_THERMOM)
            {
                // check if service handles are not ok
                if (htpc_env->hts.svc.shdl == ATT_INVALID_HANDLE)
                {
                    // stop discovery procedure.
                    htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
                }
                //too many services found only one such service should exist
                else if(htpc_env->nb_svc > 1)
                {
                    // stop discovery procedure.
                    htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                }
                // check if service handles are ok
                else
                {
                    //discover all HTS characteristics
                    htpc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                    prf_disc_char_all_send(&(htpc_env->con_info), &(htpc_env->hts.svc));
                }
            }
            else if(htpc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
            {
                status = prf_check_svc_char_validity(HTPC_CHAR_HTS_MAX, htpc_env->hts.chars,
                        htpc_hts_char);

                // Check for characteristic properties.
                if(status == PRF_ERR_OK)
                {
                    htpc_env->last_uuid_req = ATT_INVALID_HANDLE;
                    htpc_env->last_char_code = htpc_hts_char_desc[HTPC_DESC_HTS_TEMP_MEAS_CLI_CFG].char_code;
                    //Discover Temperature Measurement Char. Descriptor - Mandatory
                    prf_disc_char_desc_send(&(htpc_env->con_info),
                            &(htpc_env->hts.chars[htpc_env->last_char_code]));
                }
                else
                {
                    // Stop discovery procedure.
                    htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, status);
                }
            }
            else //Descriptors
            {
                //Get code of next char. having descriptors
                htpc_env->last_char_code = htpc_get_next_desc_char_code(htpc_env, &htpc_hts_char_desc[0]);
                if (htpc_env->last_char_code != HTPC_DESC_HTS_MAX)
                {
                    prf_disc_char_desc_send(&(htpc_env->con_info),
                            &(htpc_env->hts.chars[htpc_env->last_char_code]));
                }
                else
                {
                    status = prf_check_svc_char_desc_validity(HTPC_DESC_HTS_MAX,
                            htpc_env->hts.descs,
                            htpc_hts_char_desc,
                            htpc_env->hts.chars);

                    htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, status);
                }
            }
        }
        else
        {
            htpc_enable_cfm_send(htpc_env, &htpc_env->con_info, param->status);
        }
    }
    else if(state == HTPC_CONNECTED)
    {
        switch(param->req_type)
        {
            case GATTC_WRITE:
            case GATTC_WRITE_NO_RESPONSE:
            {
                // Get the address of the environment
                struct htpc_wr_char_rsp *wr_cfm = KE_MSG_ALLOC(HTPC_WR_CHAR_RSP,
                        htpc_env->con_info.appid, dest_id,
                        htpc_wr_char_rsp);

                wr_cfm->conhdl    = gapc_get_conhdl(htpc_env->con_info.conidx);
                //it will be a GATT status code
                wr_cfm->status    = param->status;
                // send the message
                ke_msg_send(wr_cfm);
            }
            break;

            case GATTC_READ:
            {
                if(param->status != GATT_ERR_NO_ERROR)
                {
                    prf_client_att_info_rsp((prf_env_struct*) htpc_env, HTPC_RD_CHAR_RSP,
                            param->status, NULL);
                }
            }
            break;
            default: break;
        }
    }

    return (KE_MSG_CONSUMED);
}
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATT_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 gatt_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gatt_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    uint8_t status;
    // Get the address of the environment
    struct tipc_env_tag *tipc_env = PRF_CLIENT_GET_ENV(dest_id, tipc);

    if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND)||
        (param->status == ATT_ERR_NO_ERROR))
    {
        // Currently Discovering Current Time service characteristics.
        if(tipc_env->last_svc_req  == ATT_SVC_CURRENT_TIME)
        {
            // service start/end handles has been received
            if(tipc_env->last_uuid_req == ATT_SVC_CURRENT_TIME)
            {
                // check if service handles are not ok
                if (tipc_env->cts.svc.shdl == ATT_INVALID_HANDLE)
                {
                    // stop discovery procedure.
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
                }
                //too many services found only one such service should exist
                else if(tipc_env->nb_svc != 1)
                {
                    // stop discovery procedure.
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                }
                // check if service handles are ok
                else
                {
                    //discover all CTS characteristics
                    prf_disc_char_all_send(&(tipc_env->con_info), &(tipc_env->cts.svc));
                    tipc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                }

            }
            else if(tipc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
            {
                status = prf_check_svc_char_validity(TIPC_CHAR_CTS_MAX, tipc_env->cts.chars,
                                                     tipc_cts_char);

                //  check for characteristic properties.
                if(status == PRF_ERR_OK)
                {
                    tipc_env->last_uuid_req = ATT_INVALID_HANDLE;
                    tipc_env->last_char_code = TIPC_CHAR_CTS_CURR_TIME;
                    //request all service characteristic description for CTS
                    prf_disc_char_desc_send(&(tipc_env->con_info), &(tipc_env->cts.chars[TIPC_CHAR_CTS_CURR_TIME]));
                }
                else
                {
                    // stop discovery procedure.
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, status);
                }
            }
            else
            {
                status = prf_check_svc_char_desc_validity(TIPC_DESC_CTS_MAX,
                                                          tipc_env->cts.descs,
                                                          tipc_cts_char_desc,
                                                          tipc_env->cts.chars);

                if(status == PRF_ERR_OK)
                {
                    // reset number of services
                    tipc_env->nb_svc = 0;

                    //start discovering NDCS on peer
                    prf_disc_svc_send(&(tipc_env->con_info), ATT_SVC_NEXT_DST_CHANGE);

                    tipc_env->last_uuid_req = ATT_SVC_NEXT_DST_CHANGE;
                    tipc_env->last_svc_req  = ATT_SVC_NEXT_DST_CHANGE;
                }
                else
                {
                    // stop discovery procedure.
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, status);
                }
            }
        }

        // Currently Next DST Change service characteristics.
        else if(tipc_env->last_svc_req  == ATT_SVC_NEXT_DST_CHANGE)
        {
            // service start/end handles has been received
            if(tipc_env->last_uuid_req == ATT_SVC_NEXT_DST_CHANGE)
            {
                if (tipc_env->ndcs.svc.shdl == ATT_INVALID_HANDLE)
                {
                    // reset number of services
                    tipc_env->nb_svc = 0;

                    //start discovering RTUS on peer
                    prf_disc_svc_send(&(tipc_env->con_info), ATT_SVC_REF_TIME_UPDATE);

                    tipc_env->last_uuid_req = ATT_SVC_REF_TIME_UPDATE;
                    tipc_env->last_svc_req  = ATT_SVC_REF_TIME_UPDATE;
                }
                //too many services found only one such service should exist
                else if(tipc_env->nb_svc != 1)
                {
                    // stop discovery procedure.
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                }
                // check if service handles are ok
                else
                {
                    //discover all NDCS characteristics
                    prf_disc_char_all_send(&(tipc_env->con_info), &(tipc_env->ndcs.svc));
                    tipc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                }
            }
            else if(tipc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
            {
                status = prf_check_svc_char_validity(TIPC_CHAR_NDCS_MAX, tipc_env->ndcs.chars,
                                                                         tipc_ndcs_char);

                //  check for characteristic properties.
                if(status == PRF_ERR_OK)
                {
                    // reset number of services
                    tipc_env->nb_svc = 0;

                    //start discovering RTUS on peer
                    prf_disc_svc_send(&(tipc_env->con_info), ATT_SVC_REF_TIME_UPDATE);

                    tipc_env->last_uuid_req = ATT_SVC_REF_TIME_UPDATE;
                    tipc_env->last_svc_req  = ATT_SVC_REF_TIME_UPDATE;
                }
                else
                {
                    // stop discovery procedure.
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, status);
                }
            }
        }

        // Currently Reference Time Update service characteristics.
        else if(tipc_env->last_svc_req  == ATT_SVC_REF_TIME_UPDATE)
        {
            // service start/end handles has been received
            if(tipc_env->last_uuid_req == ATT_SVC_REF_TIME_UPDATE)
            {
                if (tipc_env->rtus.svc.shdl == ATT_INVALID_HANDLE)
                {
                    // send app the details about the discovered TIPS DB to save
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_OK);
                }
                //too many services found only one such service should exist
                else if(tipc_env->nb_svc != 1)
                {
                    // stop discovery procedure.
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                }
                // check if service handles are ok
                else
                {
                    //discover all RTUS characteristics
                    prf_disc_char_all_send(&(tipc_env->con_info), &(tipc_env->rtus.svc));
                    tipc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                }
            }
            else if(tipc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
            {
                status = prf_check_svc_char_validity(TIPC_CHAR_RTUS_MAX, tipc_env->rtus.chars,
                                                     tipc_rtus_char);

                //  check for characteristic properties.
                if(status == PRF_ERR_OK)
                {
                    // send app the details about the discovered TIPS DB to save
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, PRF_ERR_OK);
                }
                else
                {
                    // stop discovery procedure.
                    tipc_enable_cfm_send(tipc_env, &tipc_env->con_info, status);
                }
            }
        }
    }
    return (KE_MSG_CONSUMED);
}
Exemple #10
0
/**
 ****************************************************************************************
 * @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 status;
    // Get the address of the environment
    struct blpc_env_tag *blpc_env = PRF_CLIENT_GET_ENV(dest_id, blpc);

    if(state == BLPC_DISCOVERING)
    {
        if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND)||
                (param->status == ATT_ERR_NO_ERROR))
        {
            // service start/end handles has been received
            if(blpc_env->last_uuid_req == ATT_SVC_BLOOD_PRESSURE)
            {
                // check if service handles are not ok
                if(blpc_env->bps.svc.shdl == ATT_INVALID_HANDLE)
                {
                    // stop discovery procedure.
                    blpc_enable_cfm_send(blpc_env, &blpc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
                }
                //too many services found only one such service should exist
                else if(blpc_env->nb_svc != 1)
                {
                    // stop discovery procedure.
                    blpc_enable_cfm_send(blpc_env, &blpc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                }
                else
                {
                    //discover all BPS characteristics
                    prf_disc_char_all_send(&(blpc_env->con_info), &(blpc_env->bps.svc));
                    blpc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                }
            }
            else if(blpc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
            {
                status = prf_check_svc_char_validity(BLPC_CHAR_MAX, blpc_env->bps.chars,
                        blpc_bps_char);

                // Check for characteristic properties.
                if(status == PRF_ERR_OK)
                {
                    blpc_env->last_uuid_req = ATT_INVALID_HANDLE;
                    blpc_env->last_char_code = blpc_bps_char_desc[BLPC_DESC_BP_MEAS_CLI_CFG].char_code;

                    //Discover Blood Pressure Measurement Char. Descriptor - Mandatory
                    prf_disc_char_desc_send(&(blpc_env->con_info),
                            &(blpc_env->bps.chars[blpc_env->last_char_code]));
                }
                else
                {
                    // Stop discovery procedure.
                    blpc_enable_cfm_send(blpc_env, &blpc_env->con_info, status);
                }
            }
            else
            {
                //If Intermediate Cuff Pressure Char. found, discover its descriptor
                if ((blpc_env->bps.chars[BLPC_CHAR_CP_MEAS].char_hdl != ATT_INVALID_HANDLE) &&
                        (blpc_env->last_char_code == BLPC_CHAR_BP_MEAS))
                {
                    blpc_env->last_char_code = BLPC_CHAR_CP_MEAS;
                    //Discover Intermediate Cuff Pressure Char. Descriptor - Mandatory
                    prf_disc_char_desc_send(&(blpc_env->con_info),
                            &(blpc_env->bps.chars[blpc_env->last_char_code]));
                }
                else
                {
                    status = prf_check_svc_char_desc_validity(BLPC_DESC_MAX,
                            blpc_env->bps.descs,
                            blpc_bps_char_desc,
                            blpc_env->bps.chars);

                    blpc_enable_cfm_send(blpc_env, &blpc_env->con_info, status);
                }
            }
        }
    }
    else if(state == BLPC_CONNECTED)
    {
        switch(param->req_type)
        {
            case GATTC_WRITE:
            case GATTC_WRITE_NO_RESPONSE:
            {
                struct blpc_wr_char_rsp *wr_cfm = KE_MSG_ALLOC(BLPC_WR_CHAR_RSP,
                                                               blpc_env->con_info.appid, dest_id,
                                                               blpc_wr_char_rsp);
                wr_cfm->conhdl    = gapc_get_conhdl(blpc_env->con_info.conidx);
                //it will be a GATT status code
                wr_cfm->status    = param->status;
                // send the message
                ke_msg_send(wr_cfm);
            }
            break;

            case GATTC_READ:
            {
                if(param->status != GATT_ERR_NO_ERROR)
                {
                    prf_client_att_info_rsp((prf_env_struct*) blpc_env, BLPC_RD_CHAR_RSP,
                            param->status, NULL);
                }
            }
            break;
            default: break;
        }
    }

    return (KE_MSG_CONSUMED);
}
Exemple #11
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATT_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 gatt_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gatt_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    uint8_t status;
    // Get the address of the environment
    struct hrpc_env_tag *hrpc_env = PRF_CLIENT_GET_ENV(dest_id, hrpc);

    if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND)||
        (param->status == ATT_ERR_NO_ERROR))
    {
        // service start/end handles has been received
        if(hrpc_env->last_uuid_req == ATT_SVC_HEART_RATE)
        {
            // check if service handles are not ok
            if(hrpc_env->hrs.svc.shdl == ATT_INVALID_HANDLE)
            {
                // stop discovery procedure.
                hrpc_enable_cfm_send(hrpc_env, &hrpc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
            }
            //too many services found only one such service should exist
            else if(hrpc_env->nb_svc != 1)
            {
                // stop discovery procedure.
                hrpc_enable_cfm_send(hrpc_env, &hrpc_env->con_info, PRF_ERR_MULTIPLE_SVC);
            }
            else
            {
                //discover all HRS characteristics
                prf_disc_char_all_send(&(hrpc_env->con_info), &(hrpc_env->hrs.svc));
                hrpc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
            }
        }
        else if(hrpc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
        {
            status = prf_check_svc_char_validity(HRPC_CHAR_MAX, hrpc_env->hrs.chars,
                                                 hrpc_hrs_char);

            // Check for characteristic properties.
            if(status == PRF_ERR_OK)
            {
                hrpc_env->last_uuid_req = ATT_INVALID_HANDLE;
                hrpc_env->last_char_code = hrpc_hrs_char_desc[HRPC_DESC_HR_MEAS_CLI_CFG].char_code;

                //Discover Heart Rate Measurement Char. Descriptor - Mandatory
                prf_disc_char_desc_send(&(hrpc_env->con_info),
                                        &(hrpc_env->hrs.chars[hrpc_env->last_char_code]));
            }
            else
            {
                // Stop discovery procedure.
                hrpc_enable_cfm_send(hrpc_env, &hrpc_env->con_info, status);
            }
        }
        else //Descriptors
        {
            status = prf_check_svc_char_desc_validity(HRPC_DESC_MAX,
                                                      hrpc_env->hrs.descs,
                                                      hrpc_hrs_char_desc,
                                                      hrpc_env->hrs.chars);

            hrpc_enable_cfm_send(hrpc_env, &hrpc_env->con_info, status);
        }
    }
    return (KE_MSG_CONSUMED);
}
Exemple #12
0
/**
 ****************************************************************************************
 * @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 status;
    // Get the address of the environment
    struct glpc_env_tag *glpc_env = PRF_CLIENT_GET_ENV(dest_id, glpc);

    if(state == GLPC_DISCOVERING)
    {
        if ((param->status == ATT_ERR_ATTRIBUTE_NOT_FOUND)||
                (param->status == ATT_ERR_NO_ERROR))
        {
            // service start/end handles has been received
            if(glpc_env->last_uuid_req == ATT_SVC_GLUCOSE)
            {
                // check if service handles are not ok
                if(glpc_env->gls.svc.shdl == ATT_INVALID_HANDLE)
                {
                    // stop discovery procedure.
                    glpc_enable_cfm_send(glpc_env, &glpc_env->con_info, PRF_ERR_STOP_DISC_CHAR_MISSING);
                }
                //too many services found only one such service should exist
                else if(glpc_env->nb_svc != 1)
                {
                    // stop discovery procedure.
                    glpc_enable_cfm_send(glpc_env, &glpc_env->con_info, PRF_ERR_MULTIPLE_SVC);
                }
                else
                {
                    //discover all GLS characteristics
                    prf_disc_char_all_send(&(glpc_env->con_info), &(glpc_env->gls.svc));
                    glpc_env->last_uuid_req = ATT_DECL_CHARACTERISTIC;
                }
            }
            else if(glpc_env->last_uuid_req == ATT_DECL_CHARACTERISTIC)
            {
                status = prf_check_svc_char_validity(GLPC_CHAR_MAX, glpc_env->gls.chars,
                        glpc_gls_char);

                // Check for characteristic properties.
                if(status == PRF_ERR_OK)
                {
                    glpc_env->last_uuid_req = ATT_INVALID_HANDLE;
                    glpc_env->last_char_code = glpc_gls_char_desc[GLPC_DESC_MEAS_CLI_CFG].char_code;

                    //Discover Glucose Measurement Char. Descriptor - Mandatory
                    prf_disc_char_desc_send(&(glpc_env->con_info),
                            &(glpc_env->gls.chars[glpc_env->last_char_code]));
                }
                else
                {
                    // Stop discovery procedure.
                    glpc_enable_cfm_send(glpc_env, &glpc_env->con_info, status);
                }
            }
            else
            {
                //If Glucose Measurement Context Char. found, discover its descriptor
                if ((glpc_env->gls.chars[GLPC_CHAR_MEAS_CTX].char_hdl != ATT_INVALID_HANDLE) &&
                        (glpc_env->last_char_code == GLPC_CHAR_MEAS))
                {
                    glpc_env->last_char_code = GLPC_CHAR_MEAS_CTX;
                    //Discover Intermediate Cuff Pressure Char. Descriptor - Mandatory
                    prf_disc_char_desc_send(&(glpc_env->con_info),
                            &(glpc_env->gls.chars[glpc_env->last_char_code]));
                }
                // discover Record access control point descriptor
                else if (glpc_env->last_char_code != GLPC_CHAR_RACP)
                {
                    glpc_env->last_char_code = GLPC_CHAR_RACP;
                    //Discover Intermediate Cuff Pressure Char. Descriptor - Mandatory
                    prf_disc_char_desc_send(&(glpc_env->con_info),
                            &(glpc_env->gls.chars[glpc_env->last_char_code]));
                }
                else
                {
                    status = prf_check_svc_char_desc_validity(GLPC_DESC_MAX,
                            glpc_env->gls.descs,
                            glpc_gls_char_desc,
                            glpc_env->gls.chars);

                    glpc_enable_cfm_send(glpc_env, &glpc_env->con_info, status);
                }
            }
        }
    }
    else if (state == GLPC_CONNECTED)
    {
        switch(param->req_type)
        {
            case GATTC_WRITE:
            case GATTC_WRITE_NO_RESPONSE:
            {
                // RACP write done.
                if(glpc_env->last_uuid_req == ATT_CHAR_REC_ACCESS_CTRL_PT)
                {
                    // an error occurs while writing RACP command
                    if(param->status != PRF_ERR_OK)
                    {
                        struct glpc_racp_rsp * rsp = KE_MSG_ALLOC(GLPC_RACP_RSP,
                                glpc_env->con_info.appid, dest_id,
                                glpc_racp_rsp);

                        // retrieve connection handle
                        rsp->conhdl = gapc_get_conhdl(glpc_env->con_info.conidx);
                        // set error status
                        rsp->status = param->status;

                        // stop timer.
                        ke_timer_clear(GLPC_RACP_REQ_TIMEOUT, dest_id);

                        ke_msg_send(rsp);
                        glpc_env->last_uuid_req = 0;
                    }
                }
                else
                {
                    bool finished = false;

                    // Restore if measurement context notification should be register in from
                    // unused variable
                    bool meas_ctx_en = glpc_env->last_char_code;

                    // Registration succeed
                    if(param->status == PRF_ERR_OK)
                    {
                        // Glucose measurement  notification registration done
                        if(glpc_env->last_uuid_req == GLPC_DESC_MEAS_CLI_CFG)
                        {
                            // register to RACP indications
                            prf_gatt_write_ntf_ind(&(glpc_env->con_info),
                                    glpc_env->gls.descs[GLPC_DESC_RACP_CLI_CFG].desc_hdl,
                                    PRF_CLI_START_IND);
                            glpc_env->last_uuid_req = GLPC_DESC_RACP_CLI_CFG;
                        }
                        // Record access control point indication registration done
                        // Register to Glucose Measurement Context notifications if requested.
                        else if((glpc_env->last_uuid_req == GLPC_DESC_RACP_CLI_CFG)
                                && meas_ctx_en)
                        {
                            // register to Glucose Measurement Context notifications
                            prf_gatt_write_ntf_ind(&(glpc_env->con_info),
                                    glpc_env->gls.descs[GLPC_DESC_MEAS_CTX_CLI_CFG].desc_hdl,
                                    PRF_CLI_START_NTF);
                            glpc_env->last_uuid_req = GLPC_DESC_MEAS_CTX_CLI_CFG;
                        }
                        // All registration done
                        else
                        {
                            // indication/notification registration finished
                            finished = true;
                        }
                    }

                    // send status if registration done or if an error occurs.
                    if((param->status != PRF_ERR_OK) || (finished))
                    {
                        struct glpc_register_cfm * cfm = KE_MSG_ALLOC(GLPC_REGISTER_CFM,
                                glpc_env->con_info.appid, dest_id,
                                glpc_register_cfm);
                        // set error status
                        cfm->conhdl = gapc_get_conhdl(glpc_env->con_info.conidx);
                        cfm->status = param->status;

                        ke_msg_send(cfm);
                    }
                }
            }
            break;

            case GATTC_READ:
            {
                if(param->status != GATT_ERR_NO_ERROR)
                {
                    struct glpc_read_features_rsp * rsp = KE_MSG_ALLOC(GLPC_READ_FEATURES_RSP,
                            glpc_env->con_info.appid, dest_id,
                            glpc_read_features_rsp);

                    // set connection handle
                    rsp->conhdl = gapc_get_conhdl(glpc_env->con_info.conidx);
                    // set error status
                    rsp->status = param->status;

                    ke_msg_send(rsp);
                }
            }
            break;
            default: break;
        }
    }

    return (KE_MSG_CONSUMED);
}