Ejemplo n.º 1
0
Archivo: wptc.c Proyecto: HerianHe/NFC
void wptc_enable_cfm_send(struct wptc_env_tag *wptc_env, struct prf_con_info *con_info, uint8_t status)
{
    //format response to app
    struct wptc_enable_cfm * cfm = KE_MSG_ALLOC(WPTC_ENABLE_CFM,
                                                 con_info->appid, con_info->prf_id,
                                                 wptc_enable_cfm);

    cfm->conhdl = gapc_get_conhdl(con_info->conidx);
    cfm->status = status;

    if (status == PRF_ERR_OK)
    {
        cfm->wpts = wptc_env->wpts;

        // Register WPT Client task in gatt for indication/notifications
        prf_register_atthdl2gatt(&wptc_env->con_info, &wptc_env->wpts.svc);


        // Go to connected state
        ke_state_set(con_info->prf_id, WPTC_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(wptc_envs, con_info->prf_id, WPTC);
    }

    ke_msg_send(cfm);
}
Ejemplo n.º 2
0
void glpc_enable_cfm_send(struct glpc_env_tag *glpc_env, struct prf_con_info *con_info, uint8_t status)
{
    // Send to APP the details of the discovered attributes on GLPS
    struct glpc_enable_cfm * rsp = KE_MSG_ALLOC(GLPC_ENABLE_CFM,
                                                con_info->appid, con_info->prf_id,
                                                glpc_enable_cfm);

    rsp->conhdl = gapc_get_conhdl(con_info->conidx);
    rsp->status = status;

    if (status == PRF_ERR_OK)
    {
        rsp->gls = glpc_env->gls;

        // Register GLPC task in gatt for indication/notifications
        prf_register_atthdl2gatt(&glpc_env->con_info, &glpc_env->gls.svc);

        // Go to connected state
        ke_state_set(con_info->prf_id, GLPC_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(glpc_envs, con_info->prf_id, GLPC);
    }

    ke_msg_send(rsp);
}
Ejemplo n.º 3
0
void basc_enable_cfm_send(struct basc_env_tag *basc_env, struct prf_con_info *con_info, uint8_t status)
{
    // Counter
    uint8_t svc_inst;

    // Send APP the details of the discovered attributes on BASC
    struct basc_enable_cfm * rsp = KE_MSG_ALLOC(BASC_ENABLE_CFM,
                                                con_info->appid, con_info->prf_id,
                                                basc_enable_cfm);

    rsp->conhdl = con_info->conhdl;
    rsp->status = status;

    if (status == PRF_ERR_OK)
    {
        rsp->bas_nb = basc_env->bas_nb;

        for (svc_inst = 0; svc_inst < basc_env->bas_nb; svc_inst++)
        {
            rsp->bas[svc_inst] = basc_env->bas[svc_inst];

            // Register BASC task in gatt for indication/notifications
            prf_register_atthdl2gatt(&basc_env->con_info, &basc_env->bas[svc_inst].svc);
        }

        // Go to connected state
        ke_state_set(con_info->prf_id, BASC_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(basc_envs, con_info->prf_id, BASC);
    }

    ke_msg_send(rsp);
}
Ejemplo n.º 4
0
void tipc_enable_cfm_send(struct tipc_env_tag *tipc_env, struct prf_con_info *con_info, uint8_t status)
{
    // Send to APP the details of the discovered attributes on TIPS
    struct tipc_enable_cfm * rsp = KE_MSG_ALLOC(TIPC_ENABLE_CFM,
                                                con_info->appid, con_info->prf_id,
                                                tipc_enable_cfm);

    rsp->conhdl = con_info->conhdl;
    rsp->status = status;

    if (status == PRF_ERR_OK)
    {
        rsp->cts    = tipc_env->cts;
        rsp->ndcs   = tipc_env->ndcs;
        rsp->rtus   = tipc_env->rtus;

        //register TIPC task in gatt for indication/notifications
        prf_register_atthdl2gatt(&tipc_env->con_info, &tipc_env->cts.svc);

        // Go to connected state
        ke_state_set(tipc_env->con_info.prf_id, TIPC_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(tipc_envs, con_info->prf_id, TIPC);
    }

    ke_msg_send(rsp);
}
Ejemplo n.º 5
0
void qppc_enable_cfm_send(struct qppc_env_tag *qppc_env, struct prf_con_info *con_info, uint8_t status)
{
    //send APP the details of the discovered attributes on QPPC
    struct qppc_enable_cfm * rsp = KE_MSG_ALLOC(QPPC_ENABLE_CFM,
                                   con_info->appid, con_info->prf_id,
                                   qppc_enable_cfm);

    rsp->conhdl = con_info->conhdl;
    rsp->status = status;

    if (status == PRF_ERR_OK)
    {
        rsp->qpps   = qppc_env->qpps;
        rsp->nb_ntf_char = qppc_env->nb_char - 1; // exclude one RX characteristic

        //register HRPC task in gatt for indication/notifications
        prf_register_atthdl2gatt(&qppc_env->con_info, &qppc_env->qpps.svc);

        // Go to connected state
        ke_state_set(qppc_env->con_info.prf_id, QPPC_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(qppc_env, con_info->prf_id, QPPC);
    }

    ke_msg_send(rsp);
}
Ejemplo n.º 6
0
void scppc_enable_cfm_send(struct scppc_env_tag *scppc_env, struct prf_con_info *con_info, uint8_t status)
{
    //send APP the details of the discovered attributes on SCPPC
    struct scppc_enable_cfm * rsp = KE_MSG_ALLOC(SCPPC_ENABLE_CFM,
                                                 con_info->appid, con_info->prf_id,
                                                 scppc_enable_cfm);

    rsp->conhdl = gapc_get_conhdl(con_info->conidx);
    rsp->status = status;

    if (status == PRF_ERR_OK)
    {
        rsp->scps = scppc_env->scps;

        // Go to connected state
        ke_state_set(con_info->prf_id, SCPPC_CONNECTED);

        // If Scan Refresh Char. has been discovered
        if (scppc_env->scps.chars[SCPPC_CHAR_SCAN_REFRESH].char_hdl != ATT_INVALID_HANDLE)
        {
            // Register SCPPC task in gatt for notifications
            prf_register_atthdl2gatt(con_info, &scppc_env->scps.svc);
        }
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(scppc_envs, con_info->prf_id, SCPPC);
    }

    ke_msg_send(rsp);
}
Ejemplo n.º 7
0
Archivo: blpc.c Proyecto: imGit/DA14580
void blpc_enable_cfm_send(struct blpc_env_tag *blpc_env, struct prf_con_info *con_info, uint8_t status)
{
    // Send to APP the details of the discovered attributes on BLPS
    struct blpc_enable_cfm * rsp = KE_MSG_ALLOC(BLPC_ENABLE_CFM,
                                                con_info->appid, con_info->prf_id,
                                                blpc_enable_cfm);

    rsp->conhdl = gapc_get_conhdl(con_info->conidx);
    rsp->status = status;

    if (status == PRF_ERR_OK)
    {
        rsp->bps = blpc_env->bps;

        prf_register_atthdl2gatt(&blpc_env->con_info, &blpc_env->bps.svc);

        // Go to connected state
        ke_state_set(blpc_env->con_info.prf_id, BLPC_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(blpc_envs, con_info->prf_id, BLPC);
    }

    ke_msg_send(rsp);
}
Ejemplo n.º 8
0
void streamdatah_enable_cfm_send(struct streamdatah_env_tag *streamdatah_env, struct prf_con_info *con_info, uint8_t status)
{
    //format response to app
    struct streamdatah_enable_cfm * cfm = KE_MSG_ALLOC(STREAMDATAH_ENABLE_CFM,
                                                 con_info->appid, con_info->prf_id,
                                                 streamdatah_enable_cfm);

    cfm->conhdl = gapc_get_conhdl(con_info->conidx);
    cfm->status = status;

    if (status == PRF_ERR_OK)
    {
        cfm->streamdatah    = streamdatah_env->streamdatah;

		// Register STREAMDATAH task in gatt for indication/notifications
		prf_register_atthdl2gatt(&streamdatah_env->con_info, &streamdatah_env->streamdatah.svc);

        // Go to connected state
        ke_state_set(con_info->prf_id, STREAMDATAH_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(streamdatah_envs, con_info->prf_id, STREAMDATAH);
    }

    ke_msg_send(cfm);
}
Ejemplo n.º 9
0
void htpc_enable_cfm_send(struct htpc_env_tag *htpc_env, struct prf_con_info *con_info, uint8_t status)
{
    // Inform the APP about the status of the enabling of the Health Thermometer Profile Collector role 5awk
    struct htpc_enable_cfm *cfm = KE_MSG_ALLOC(HTPC_ENABLE_CFM,
                                               con_info->appid, con_info->prf_id,
                                               htpc_enable_cfm);

    // Connection Handle
    cfm->conhdl = gapc_get_conhdl(con_info->conidx);
    // Status
    cfm->status = status;

    // If status is PRF_ERR_OK, hts is non NULL
    if (status == PRF_ERR_OK)
    {
        // Attributes discovered in the peer device database
        cfm->hts = htpc_env->hts;

        // Register the profile task in GATT in order to receive notifications/indications
        prf_register_atthdl2gatt(con_info, &htpc_env->hts.svc);

        // Go to connected state
        ke_state_set(con_info->prf_id, HTPC_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(htpc_envs, con_info->prf_id, HTPC);
    }

    // Send the confirmation to the application
    ke_msg_send(cfm);
}
Ejemplo n.º 10
0
void hogpbh_enable_cfm_send(struct hogpbh_env_tag *hogpbh_env, struct prf_con_info *con_info, uint8_t status)
{
    // Counter
    uint8_t i;

    // Send APP the details of the discovered attributes on HOGPBH
    struct hogpbh_enable_cfm * rsp = KE_MSG_ALLOC(HOGPBH_ENABLE_CFM,
                                                  con_info->appid, con_info->prf_id,
                                                  hogpbh_enable_cfm);

    rsp->conhdl = gapc_get_conhdl(con_info->conidx);
    rsp->status = status;

    if (status == PRF_ERR_OK)
    {
        rsp->hids_nb = hogpbh_env->hids_nb;

        for (i = 0; i < hogpbh_env->hids_nb; i++)
        {
            rsp->hids[i] = hogpbh_env->hids[i];

            // Register HOGPBH task in gatt for indication/notifications
            prf_register_atthdl2gatt(&hogpbh_env->con_info, &hogpbh_env->hids[i].svc);
        }

        // Go to connected state
        ke_state_set(con_info->prf_id, HOGPBH_CONNECTED);
    }
    else
    {
        PRF_CLIENT_ENABLE_ERROR(hogpbh_envs, con_info->prf_id, HOGPBH);
    }

    ke_msg_send(rsp);
}
Ejemplo n.º 11
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref CSCPC_ENABLE_CMD 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 cscpc_enable_cmd_handler(ke_msg_id_t const msgid,
                                    struct cscpc_enable_cmd *param,
                                    ke_task_id_t const dest_id,
                                    ke_task_id_t const src_id)
{
    // Status
    uint8_t status     = PRF_ERR_OK;
    // Message status
    uint8_t msg_status = KE_MSG_CONSUMED;
    // Cycling Speed and Cadence Profile Collector Role Task Environment
    struct cscpc_env_tag *cscpc_env;
    // Connection Information
    struct prf_con_info con_info;

    // Check if the provided conhdl is valid
    if (gap_get_rec_idx(param->conhdl) != GAP_INVALID_CONIDX)
    {
        // Fill the Connection Information structure
        con_info.conhdl = param->conhdl;
        con_info.prf_id = dest_id;
        con_info.appid  = src_id;

        // Add an environment for the provided connection
        status = PRF_CLIENT_ENABLE(con_info, param, cscpc);
    }
    else
    {
        status = PRF_ERR_REQ_DISALLOWED;
    }

    if (status == PRF_ERR_OK)
    {
        // Get the newly created environment
        cscpc_env = PRF_CLIENT_GET_ENV(dest_id, cscpc);

        // Keep the connection info
        memcpy(&cscpc_env->con_info, &con_info, sizeof(struct prf_con_info));

        // Start discovering
        if (param->con_type == PRF_CON_DISCOVERY)
        {
            // Configure the environment for a discovery procedure
            cscpc_env->last_req  = ATT_SVC_CYCLING_SPEED_CADENCE;
            // Force the operation value
            param->operation     = CSCPC_ENABLE_OP_CODE;

            prf_disc_svc_send(&(cscpc_env->con_info), ATT_SVC_CYCLING_SPEED_CADENCE);

            // Keep the operation
            cscpc_env->operation = (void *)param;
            // Do not free the message
            msg_status = KE_MSG_NO_FREE;

            // Go to BUSY state
            ke_state_set(dest_id, CSCPC_BUSY);
        }
        // Bond information are provided
        else
        {
            // Keep the provided database content
            memcpy(&cscpc_env->cscs, &param->cscs, sizeof(struct cscpc_cscs_content));

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

            // Send a complete event status to the application
            cscpc_send_cmp_evt(cscpc_env, CSCPC_ENABLE_OP_CODE, PRF_ERR_OK);
        }
    }
    else if (status == PRF_ERR_FEATURE_NOT_SUPPORTED)
    {
        // The message will be forwarded towards the good task instance
        msg_status = KE_MSG_NO_FREE;
    }
    else
    {
        // The request is disallowed (profile already enabled for this connection, or not enough memory, ...)
        cscpc_send_no_conn_cmp_evt(dest_id, src_id, param->conhdl, CSCPC_ENABLE_OP_CODE);
    }

    return (int)msg_status;
}
Ejemplo n.º 12
0
/**
 ****************************************************************************************
 * @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);
}
Ejemplo n.º 13
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref ANPC_ENABLE_CMD 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 anpc_enable_cmd_handler(ke_msg_id_t const msgid,
                                   struct anpc_enable_cmd *param,
                                   ke_task_id_t const dest_id,
                                   ke_task_id_t const src_id)
{
    // Status
    uint8_t status     = PRF_ERR_OK;
    // Message status
    uint8_t msg_status = KE_MSG_CONSUMED;
    // Alert Notification Profile Client Role Task Environment
    struct anpc_env_tag *anpc_env;
    // Connection Information
    struct prf_con_info con_info;

    // Check if the provided connection handle is valid
    if (gapc_get_conidx(param->conhdl) != GAP_INVALID_CONIDX)
    {
        // Fill the Connection Information structure
        con_info.conidx = gapc_get_conidx(param->conhdl);
        con_info.prf_id = dest_id;
        con_info.appid  = src_id;

        // Add an environment for the provided connection
        status = PRF_CLIENT_ENABLE(con_info, param, anpc);
    }
    else
    {
        status = PRF_ERR_REQ_DISALLOWED;
    }

    if (status == PRF_ERR_OK)
    {
        // Get the newly created environment
        anpc_env = PRF_CLIENT_GET_ENV(dest_id, anpc);

        // Keep the connection info
        memcpy(&anpc_env->con_info, &con_info, sizeof(struct prf_con_info));

        // Start discovering
        if (param->con_type == PRF_CON_DISCOVERY)
        {
            // Configure the environment for a discovery procedure
            anpc_env->last_req  = ATT_SVC_ALERT_NTF;
            // Force the operation value
            param->operation    = ANPC_ENABLE_OP_CODE;

            prf_disc_svc_send(&(anpc_env->con_info), ATT_SVC_ALERT_NTF);
        }
        // Bond information are provided
        else
        {
            // Keep the provided database content
            memcpy(&anpc_env->ans, &param->ans, sizeof(struct anpc_ans_content));

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

            // Force the operation value
            param->operation = ANPC_ENABLE_RD_NEW_ALERT_OP_CODE;

            // Check Supported New 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_NEW_ALERT_CAT].val_hdl);
        }

        // Keep the operation
        anpc_env->operation = (void *)param;
        // Do not free the message
        msg_status = KE_MSG_NO_FREE;

        // Go to BUSY state
        ke_state_set(dest_id, ANPC_BUSY);
    }
    else if (status == PRF_ERR_FEATURE_NOT_SUPPORTED)
    {
        // The message will be forwarded towards the good task instance
        msg_status = KE_MSG_NO_FREE;
    }
    else
    {
        // The request is disallowed (profile already enabled for this connection, or not enough memory, ...)
        anpc_send_no_conn_cmp_evt(dest_id, src_id, param->conhdl, ANPC_ENABLE_OP_CODE);
    }

    return (int)msg_status;
}
Ejemplo n.º 14
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);
}