Exemplo n.º 1
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATT_WRITE_CMD_IND message.
 * The handler checks if the stream needs to be turned on.
 * @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_write_cmd_ind_handler(ke_msg_id_t const msgid,
                                      struct gattc_write_cmd_ind const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id)
{
    // Update the attribute value
    attmdb_att_update_value(param->handle, param->length, param->offset,
            (uint8_t*)&(param->value[0]));

    switch (STREAMDATAD_IDX(param->handle))
    {
        case STREAMDATAD_IDX_ENABLE_VAL:
			streamdatad_streamonoff();
			atts_write_rsp_send(streamdatad_env.conhdl, param->handle, PRF_ERR_OK);
			break;

        case STREAMDATAD_IDX_STREAMDATAD_D0_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D1_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D2_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D3_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D4_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D5_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D6_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D7_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D8_EN:
        case STREAMDATAD_IDX_STREAMDATAD_D9_EN:
			atts_write_rsp_send(streamdatad_env.conhdl, param->handle, PRF_ERR_OK);
            break;
        
        case STREAMDATAD_IDX_STREAMDATAD_D0_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D1_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D2_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D3_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D4_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D5_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D6_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D7_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D8_VAL:
        case STREAMDATAD_IDX_STREAMDATAD_D9_VAL:
        {
            struct streamdatad_rcv_data_packet_ind * ind = KE_MSG_ALLOC(STREAMDATAD_RCV_DATA_PACKET_IND,
														streamdatad_env.con_info.appid, dest_id,
														streamdatad_rcv_data_packet_ind);

            ind->conhdl     = gapc_get_conhdl(streamdatad_env.con_info.conidx);
            ind->handle     = param->handle;
            ind->seq        = param->value[0]; // is already incrementing (with the dummy data of the test)
            ind->size       = param->length;
            memcpy(&(ind->value[0]), &(param->value[0]), param->length);

            // Forward Received packet data to APP with the sequence number to indicate lost packets
            ke_msg_send(ind);
        }
        break;
        
    }

    return (KE_MSG_CONSUMED);
}
Exemplo n.º 2
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GL2C_CODE_ATT_WR_CMD_IND message.
 * The handler will analyse what has been set and decide alert level
 * @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_write_cmd_ind_handler(ke_msg_id_t const msgid,
                                      struct gattc_write_cmd_ind const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id)
{
    uint8_t char_code = UDSS_ERR_CHAR;
    uint8_t status = PRF_APP_ERROR;

    if (KE_IDX_GET(src_id) == udss_env.con_info.conidx)
    {
//        uint8_t att_idx = GLPS_IDX(param->handle);
        status = PRF_ERR_OK;

        if (param->handle == udss_env.shdl + UDS_IDX_USER_HEIGHT_VAL)
        {
            char_code = UDSS_USER_HEIGHT_CHAR;
            //Set User Height to specified value
            attmdb_att_set_value(udss_env.shdl + UDS_IDX_USER_HEIGHT_VAL,
                             sizeof(uint8_t), (uint8_t *)&param->value[0]);
        }
        else if (param->handle == udss_env.shdl + UDS_IDX_USER_AGE_VAL)
        {
            char_code = UDSS_USER_AGE_CHAR;
            //Set User Age to specified value
            attmdb_att_set_value(udss_env.shdl + UDS_IDX_USER_AGE_VAL,
                                 sizeof(uint8_t), (uint8_t *)&param->value[0]);
        }
        else if (param->handle == udss_env.shdl + UDS_IDX_USER_DATE_OF_BIRTH_VAL)
        {
            char_code = UDSS_USER_DATE_OF_BIRTH_CHAR;
            //Set User Date of Birth to specified value
            attmdb_att_set_value(udss_env.shdl + UDS_IDX_USER_DATE_OF_BIRTH_VAL,
                                 sizeof(struct date), (uint8_t *)&param->value[0]);
        }
        else if (param->handle == udss_env.shdl + UDS_IDX_USER_DB_CHANGE_INCR_VAL)
        {
            char_code = UDSS_USER_DB_CHANGE_INCR_CHAR;
            //Set User DB Change Increment to specified value
            attmdb_att_set_value(udss_env.shdl + UDS_IDX_USER_DB_CHANGE_INCR_VAL,
                                 sizeof(uint8_t), (uint8_t *)&param->value[0]);
        }
        else if (param->handle == udss_env.shdl + UDS_IDX_USER_CTRL_POINT_VAL)
        {
            uint8_t reqstatus;
            uint8_t* value;
            uint16_t length;
            struct uds_ucp_req ucp_req;

            // Update the attribute value (note several write could be required since
            // attribute length > (ATT_MTU-3)
            attmdb_att_update_value(param->handle, param->length, param->offset,
                    (uint8_t*)&(param->value[0]));

            // retrieve full data.
            attmdb_att_get_value(param->handle, &length, &value);

            // unpack user control point value
            reqstatus = udss_unpack_ucp_req(value, length, &ucp_req);

            // check unpacked status
            switch(reqstatus)
            {
                case PRF_APP_ERROR:
                {
                    /* Do nothing, ignore request since it's not complete and maybe
                     * requires several peer write to be performed.
                     */
                }
                break;
                case PRF_ERR_OK:
                {
                    // check wich request shall be send to api task
                    switch(ucp_req.op_code)
                    {
                        case UDS_REQ_REG_NEW_USER:
                        case UDS_REQ_CONSENT:
                        case UDS_REQ_DEL_USER_DATA:
                        {
                            //forward request operation to application
                            struct udss_ucp_req_ind * req = KE_MSG_ALLOC(UDSS_UCP_REQ_IND,
                                    udss_env.con_info.appid, TASK_UDSS,
                                    udss_ucp_req_ind);

                            // UCP on going.
//                            UDSS_SET(UCP_ON_GOING);

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

                            ke_msg_send(req);
                        }
                        break;
//                        case UDS_REQ_ABORT_OP:
//                        {
//                            // nothing to abort, send an error message.
//                            struct uds_ucp_rsp ucp_rsp;

//                            ucp_rsp.op_code = UDS_REQ_RSP_CODE;
//                            ucp_rsp.operand.rsp.op_code_req =
//                                    ucp_req.op_code;
//                            ucp_rsp.operand.rsp.status = UDS_RSP_ABORT_UNSUCCESSFUL;

//                            // send user control response indication
//                            udss_send_ucp_rsp(&(ucp_rsp),
//                                    TASK_UDSS);
//                        }
//                        break;
                        default:
                        {
                            // nothing to do since it's handled during unpack
                        }
                        break;
                    }
                }
                break;
                default:
                {
                    /* There is an error in user control request, inform peer
                     * device that request is incorrect. */
                    struct uds_ucp_rsp ucp_rsp;

                    ucp_rsp.op_code = UDS_REQ_RSP_CODE;
                    ucp_rsp.req_op_code = ucp_req.op_code;
                    ucp_rsp.rsp_val = reqstatus;

                    // send user control response indication
                    udss_send_ucp_rsp(&(ucp_rsp),
                            TASK_UDSS);
                }
                break;
            }
            
        }
        // not expected request
        else
        {
            status = ATT_ERR_WRITE_NOT_PERMITTED;
        }
    }
    
    if(param->response)
    {
        // Send Write Response
        atts_write_rsp_send(udss_env.con_info.conidx, param->handle, status);
    }

    return (KE_MSG_CONSUMED);
}
Exemplo n.º 3
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GL2C_CODE_ATT_WR_CMD_IND message.
 * The handler compares the new values with current ones and notifies them if they changed.
 * @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_write_cmd_ind_handler(ke_msg_id_t const msgid,
                                      struct gattc_write_cmd_ind const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id)
{
    // Update the attribute value
    attmdb_att_update_value(param->handle, param->length, param->offset,
            (uint8_t*)&(param->value[0]));

    switch (ACCEL_IDX(param->handle))
    {
        case ACCEL_IDX_ENABLE_VAL:
        {
            uint16_t len;
            uint8_t* accel_en;
            uint8_t* axis_en;
            uint8_t* range;

            if(*(uint8_t*)&(param->value[0]) > 1)
                accel_threshold = *(uint8_t*)&(param->value[0]) - 2;
//            set_accel_thr();
                        
            attmdb_att_get_value(ACCEL_HANDLE(ACCEL_IDX_ENABLE_VAL), &(len), &(accel_en));

            // Indicate to the application the state of the profile
            if (accel_en)
            {
                // Allocate the start indication message
                struct accel_start_ind *ind = KE_MSG_ALLOC(ACCEL_START_IND,
                                                           accel_env.con_info.appid, dest_id,
                                                           accel_start_ind);

                // Fill in the parameter structure
                for (int i = 0; i < ACCEL_MAX; i++)
                {
                    attmdb_att_get_value(ACCEL_DIR_VAL_HANDLE(i), &(len), &(axis_en));

                    ind->accel_en[i] = *axis_en;
                }


                attmdb_att_get_value(ACCEL_HANDLE(ACCEL_IDX_RANGE_VAL), &(len), &(range));

                ind->range = *range;

                // Send the message
                ke_msg_send(ind);
            }
            else
            {
                // Send the stop indication
                ke_msg_send_basic(ACCEL_STOP_IND, accel_env.con_info.appid, TASK_ACCEL);
            }
        }
        break;

        case ACCEL_IDX_ACCEL_DISPLAY1_VAL:
        case ACCEL_IDX_ACCEL_DISPLAY2_VAL:            
            if(*(uint8_t*)&(param->value[0]) == 0xFF)
            {
                if(param->length > 1 && *(uint8_t*)&(param->value[1]) > 1)
					accel_con_interval = *(uint8_t*)&(param->value[1]);
                if(param->length > 2 && *(uint8_t*)&(param->value[2]) > 1)
					accel_mode = *(uint8_t*)&(param->value[2])-2;
				if(param->length > 3 && *(uint8_t*)&(param->value[3]) > 1)
					accel_latency = *(uint8_t*)&(param->value[3])-2;
				if(param->length > 4 && *(uint8_t*)&(param->value[4]) > 1)
					accel_window = *(uint8_t*)&(param->value[4]);
            }
        {
            uint8_t line;
            uint16_t len;
            uint8_t* display;

            struct accel_write_line_ind *ind = KE_MSG_ALLOC(ACCEL_WRITE_LINE_IND,
                                                            accel_env.con_info.appid, TASK_ACCEL,
                                                            accel_write_line_ind);
            line = (param->handle == ACCEL_HANDLE(ACCEL_IDX_ACCEL_DISPLAY1_VAL))?0:1;


            attmdb_att_get_value(param->handle, &(len), &(display));

            // Fill in the parameter structure
            memcpy(ind->text, display, len);
            ind->line = line;

            // Send the message
            ke_msg_send(ind);
        }
            atts_write_rsp_send(accel_env.con_info.conidx, param->handle, PRF_ERR_OK);
            break;        
        case ACCEL_IDX_ACCEL_X_VAL:
        case ACCEL_IDX_ACCEL_X_EN:
					if(*(uint8_t*)&(param->value[0]) > 1)
					accel_adv_interval1 = *(uint8_t*)&(param->value[0]);
						atts_write_rsp_send(accel_env.con_info.conidx, param->handle, PRF_ERR_OK);
            break;
        case ACCEL_IDX_ACCEL_Y_VAL:
        case ACCEL_IDX_ACCEL_Y_EN:
					if(*(uint8_t*)&(param->value[0]) > 1)
					accel_adv_interval2 = *(uint8_t*)&(param->value[0]);
					if(*(uint8_t*)&(param->value[0]) == 255)
					accel_adv_interval2 = 0;
						atts_write_rsp_send(accel_env.con_info.conidx, param->handle, PRF_ERR_OK);
            break;
        case ACCEL_IDX_ACCEL_Z_VAL:
        case ACCEL_IDX_ACCEL_Z_EN:
					if(*(uint8_t*)&(param->value[0]) > 1)
					accel_adv_interval3 = *(uint8_t*)&(param->value[0]);
					if(*(uint8_t*)&(param->value[0]) == 255)
					accel_adv_interval3 = 0;
						atts_write_rsp_send(accel_env.con_info.conidx, param->handle, PRF_ERR_OK);
            break;
        
        default:
            atts_write_rsp_send(accel_env.con_info.conidx, param->handle, PRF_ERR_OK);
            break;
            
    }

    return (KE_MSG_CONSUMED);
}
Exemplo n.º 4
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GL2C_CODE_ATT_WR_CMD_IND message.
 * The handler will
 * @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_write_cmd_ind_handler(ke_msg_id_t const msgid,
                                       struct gattc_write_cmd_ind const *param,
                                       ke_task_id_t const dest_id,
                                       ke_task_id_t const src_id)
{
    uint16_t value = 0x0000;
    uint8_t char_code = HPS_ERR_CHAR;
    uint8_t status = PRF_APP_ERROR;

    if (KE_IDX_GET(src_id) == hpss_env.con_info.conidx)
    {
        if (param->handle == hpss_env.hps_shdl + HPS_IDX_URI_VAL)
        {
            char_code = HPS_URI_CHAR;
        }
        else if (param->handle == hpss_env.hps_shdl + HPS_IDX_HEADER_VAL)
        {
            char_code = HPS_HEADER_CHAR;
        }
        else if (param->handle == hpss_env.hps_shdl + HPS_IDX_BODY_VAL)
        {
            char_code = HPS_BODY_CHAR;
        }
        else if (param->handle == hpss_env.hps_shdl + HPS_IDX_CNTL_PT_VAL)
        {
            char_code = HPS_CNTL_PT_CHAR;
        }
        //this case is processed seperately
        else if (param->handle == hpss_env.hps_shdl + HPS_IDX_STATUS_CODE_CFG)
        {
            //Extract value before check
            memcpy(&value, &(param->value), sizeof(uint16_t));

            if ((value == PRF_CLI_STOP_NTFIND) || (value == PRF_CLI_START_NTF))
            {
                status = PRF_ERR_OK;
                if (value == PRF_CLI_STOP_NTFIND)
                {
                    hpss_env.features &= ~HPSS_STATUS_CODE_NTF_CFG;
                }
                else //PRF_CLI_START_NTF
                {
                    hpss_env.features |= HPSS_STATUS_CODE_NTF_CFG;
                }
            }
            else
            {
                status = PRF_APP_ERROR;
            }

            if (status == PRF_ERR_OK)
            {
                //Update the attribute value
                attmdb_att_set_value(param->handle, sizeof(uint16_t), (uint8_t *)&value);
                if(param->last)
                {
                    //Inform APP of configuration change
                    struct hpss_cfg_indntf_ind * ind = KE_MSG_ALLOC(HPSS_CFG_INDNTF_IND,
                                                       hpss_env.con_info.appid, TASK_HPSS,
                                                       hpss_cfg_indntf_ind);

                    ind->conhdl = gapc_get_conhdl(hpss_env.con_info.conidx);
                    memcpy(&ind->cfg_val, &value, sizeof(uint16_t));

                    ke_msg_send(ind);
                }
            }
            //If HPS, send write response
            if (param->response != 0x00)
            {
                // Send Write Response
                atts_write_rsp_send(hpss_env.con_info.conidx, param->handle, status);
            }
            return  (KE_MSG_CONSUMED);
        }
        if (char_code != HPS_ERR_CHAR)
        {
            //Save value in DB
            attmdb_att_set_value(param->handle, sizeof(uint8_t), (uint8_t *)&param->value[0]);

            status = PRF_ERR_OK;

            switch(char_code)
            {
            case  HPS_URI_CHAR:
                if ((param->length + param->offset) <= HPS_URI_MAX_LEN)
                {
                    // First part of the uri value
                    if (param->offset == 0)
                    {
                        // Set value in the database
                        attmdb_att_set_value(param->handle, param->length, (uint8_t *)&param->value[0]);
                    }
                    else
                    {
                        // Complete the value stored in the database
                        attmdb_att_update_value(param->handle, param->length, param->offset,
                                                (uint8_t *)&param->value[0]);
                    }
                    if(param->last)
                    {
                        uint16_t len = 0;
                        uint8_t *data = NULL;
                        // Get complete uri value and length
                        attmdb_att_get_value(param->handle, &len, &data);
                        // Inform APP. Allocate the URI value change indication
                        struct hpss_uri_ind *ind = KE_MSG_ALLOC(HPSS_URI_IND,
                                                                hpss_env.con_info.appid, TASK_HPSS,
                                                                hpss_uri_ind);


                        // Fill in the parameter structure
                        ind->conhdl = gapc_get_conhdl(hpss_env.con_info.conidx);
                        ind->char_code = HPS_URI_CHAR;
                        ind->len = len;

                        memcpy(&ind->uri, data, len);

                        ke_msg_send(ind);
                    }
                }
                break;
            case  HPS_HEADER_CHAR:
                if ((param->length + param->offset) <= HPS_HEADER_MAX_LEN)
                {
                    // First part of the uri value
                    if (param->offset == 0)
                    {
                        // Set value in the database
                        attmdb_att_set_value(param->handle, param->length, (uint8_t *)&param->value[0]);
                    }
                    else
                    {
                        // Complete the value stored in the database
                        attmdb_att_update_value(param->handle, param->length, param->offset,
                                                (uint8_t *)&param->value[0]);
                    }
                    if(param->last)
                    {
                        uint16_t len = 0;
                        uint8_t *data = NULL;
                        // Get complete uri value and length
                        attmdb_att_get_value(param->handle, &len, &data);
                        // Inform APP. Allocate the URI value change indication
                        struct hpss_header_ind *ind = KE_MSG_ALLOC(HPSS_HEADER_IND,
                                                      hpss_env.con_info.appid, TASK_HPSS,
                                                      hpss_header_ind);


                        // Fill in the parameter structure
                        ind->conhdl = gapc_get_conhdl(hpss_env.con_info.conidx);
                        ind->char_code = HPS_HEADER_CHAR;
                        ind->len = len;

                        memcpy(&ind->header, data, len);

                        ke_msg_send(ind);
                    }
                }
                break;
            case  HPS_BODY_CHAR:
                if ((param->length + param->offset) <= HPS_BODY_MAX_LEN)
                {
                    // First part of the uri value
                    if (param->offset == 0)
                    {
                        // Set value in the database
                        attmdb_att_set_value(param->handle, param->length, (uint8_t *)&param->value[0]);
                    }
                    else
                    {
                        // Complete the value stored in the database
                        attmdb_att_update_value(param->handle, param->length, param->offset,
                                                (uint8_t *)&param->value[0]);
                    }
                    if(param->last)
                    {
                        uint16_t len = 0;
                        uint8_t *data = NULL;
                        // Get complete uri value and length
                        attmdb_att_get_value(param->handle, &len, &data);
                        // Inform APP. Allocate the URI value change indication
                        struct hpss_body_ind *ind = KE_MSG_ALLOC(HPSS_BODY_IND,
                                                    hpss_env.con_info.appid, TASK_HPSS,
                                                    hpss_body_ind);


                        // Fill in the parameter structure
                        ind->conhdl = gapc_get_conhdl(hpss_env.con_info.conidx);
                        ind->char_code = HPS_BODY_CHAR;
                        ind->len = len;

                        memcpy(&ind->body, data, len);

                        ke_msg_send(ind);
                    }
                }
                break;
            case  HPS_CNTL_PT_CHAR:
                if ((param->length + param->offset) <= HPS_CNTL_PT_MAX_LEN)
                {
                    // First part of the uri value
                    if (param->offset == 0)
                    {
                        // Set value in the database
                        attmdb_att_set_value(param->handle, param->length, (uint8_t *)&param->value[0]);
                    }
                    else
                    {
                        // Complete the value stored in the database
                        attmdb_att_update_value(param->handle, param->length, param->offset,
                                                (uint8_t *)&param->value[0]);
                    }
                    if(param->last)
                    {
                        uint16_t len = 0;
                        uint8_t *data = NULL;
                        // Get complete uri value and length
                        attmdb_att_get_value(param->handle, &len, &data);
                        // Inform APP. Allocate the URI value change indication
                        struct hpss_cntl_pt_ind *ind = KE_MSG_ALLOC(HPSS_CNTL_PT_IND,
                                                       hpss_env.con_info.appid, TASK_HPSS,
                                                       hpss_cntl_pt_ind);


                        // Fill in the parameter structure
                        ind->conhdl = gapc_get_conhdl(hpss_env.con_info.conidx);
                        ind->char_code = HPS_CNTL_PT_CHAR;
                        ind->len = len;

                        memcpy(&ind->cntl_pt, data, len);

                        ke_msg_send(ind);
                    }
                }
                break;
            default:
                break;
            }
            //If HPS, send write response
            if (param->response != 0x00)
            {
                // Send Write Response
                atts_write_rsp_send(hpss_env.con_info.conidx, param->handle, status);
            }
        }
    }
    return (KE_MSG_CONSUMED);
}
Exemplo n.º 5
0
/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GL2C_CODE_ATT_WR_CMD_IND message.
 * The handler compares the new values with current ones and notifies them if they changed.
 * @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_write_cmd_ind_handler(ke_msg_id_t const msgid,
                                      struct gattc_write_cmd_ind const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id)
{
    uint8_t status = ATT_ERR_INSUFF_AUTHOR;

    // check connection
    if (KE_IDX_GET(src_id) == glps_env.con_info.conidx)
    {
        uint8_t att_idx = GLPS_IDX(param->handle);
        status = PRF_ERR_OK;

        // check if it's a client configuration char
        if(glps_att_db[att_idx].uuid == ATT_DESC_CLIENT_CHAR_CFG)
        {
            uint16_t cli_cfg;
            uint8_t evt_mask = GLPS_IND_NTF_EVT(att_idx);

            // get client configuration
            cli_cfg = co_read16(&(param->value));

            // stop indication/notification
            if(cli_cfg == PRF_CLI_STOP_NTFIND)
            {
                glps_env.evt_cfg &= ~evt_mask;
            }
            // start indication/notification (check that char value accept it)
            else if((((glps_att_db[att_idx-1].perm & PERM(IND, ENABLE)) != 0)
                && cli_cfg == PRF_CLI_START_IND)
               ||(((glps_att_db[att_idx-1].perm & PERM(NTF, ENABLE)) != 0)
                       && cli_cfg == PRF_CLI_START_NTF))
            {
                glps_env.evt_cfg |= evt_mask;
            }
            // improper value
            else
            {
                status = GLP_ERR_IMPROPER_CLI_CHAR_CFG;
            }

            if (status == PRF_ERR_OK)
            {
                //Inform APP of configuration change
                struct glps_cfg_indntf_ind * ind = KE_MSG_ALLOC(GLPS_CFG_INDNTF_IND,
                        glps_env.con_info.appid, TASK_GLPS,
                        glps_cfg_indntf_ind);

                //Update the attribute value
                attmdb_att_set_value(param->handle, sizeof(uint16_t), (uint8_t *)&cli_cfg);
                ind->conhdl = gapc_get_conhdl(glps_env.con_info.conidx);
                ind->evt_cfg = glps_env.evt_cfg;

                ke_msg_send(ind);
            }
        }

        else if (glps_att_db[att_idx].uuid == ATT_CHAR_REC_ACCESS_CTRL_PT)
        {
            uint8_t reqstatus;
            uint8_t* value;
            uint16_t length;
            struct glp_racp_req racp_req;

            if((glps_env.evt_cfg & GLPS_RACP_IND_CFG) == 0)
            {
                // do nothing since indication not enabled for this characteristic
                status = GLP_ERR_IMPROPER_CLI_CHAR_CFG;
            }
            // If a request is on going
            else if(GLPS_IS(RACP_ON_GOING))
            {
                // if it's an abort command, execute it.
                if((param->offset == 0) && (param->value[0] == GLP_REQ_ABORT_OP))
                {
                    //forward abort operation to application
                    struct glps_racp_req_ind * req = KE_MSG_ALLOC(GLPS_RACP_REQ_IND,
                            glps_env.con_info.appid, TASK_GLPS,
                            glps_racp_req_ind);

                    req->conhdl = gapc_get_conhdl(glps_env.con_info.conidx);
                    req->racp_req.op_code = GLP_REQ_ABORT_OP;
                    req->racp_req.filter.operator = 0;

                    ke_msg_send(req);
                }
                else
                {
                    // do nothing since a procedure already in progress
                    status = GLP_ERR_PROC_ALREADY_IN_PROGRESS;
                }
            }
            else
            {
                // Update the attribute value (note several write could be required since
                // attribute length > (ATT_MTU-3)
                attmdb_att_update_value(param->handle, param->length, param->offset,
                        (uint8_t*)&(param->value[0]));

                // retrieve full data.
                attmdb_att_get_value(param->handle, &length, &value);

                // unpack record access control point value
                reqstatus = glps_unpack_racp_req(value, length, &racp_req);

                // check unpacked status
                switch(reqstatus)
                {
                    case PRF_APP_ERROR:
                    {
                        /* Do nothing, ignore request since it's not complete and maybe
                         * requires several peer write to be performed.
                         */
                    }
                    break;
                    case PRF_ERR_OK:
                    {
                        // check wich request shall be send to api task
                        switch(racp_req.op_code)
                        {
                            case GLP_REQ_REP_STRD_RECS:
                            case GLP_REQ_DEL_STRD_RECS:
                            case GLP_REQ_REP_NUM_OF_STRD_RECS:
                            {
                                //forward request operation to application
                                struct glps_racp_req_ind * req = KE_MSG_ALLOC(GLPS_RACP_REQ_IND,
                                        glps_env.con_info.appid, TASK_GLPS,
                                        glps_racp_req_ind);

                                // RACP on going.
                                GLPS_SET(RACP_ON_GOING);

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

                                ke_msg_send(req);
                            }
                            break;
                            case GLP_REQ_ABORT_OP:
                            {
                                // nothing to abort, send an error message.
                                struct glp_racp_rsp racp_rsp;

                                racp_rsp.op_code = GLP_REQ_RSP_CODE;
                                racp_rsp.operand.rsp.op_code_req =
                                        racp_req.op_code;
                                racp_rsp.operand.rsp.status = GLP_RSP_ABORT_UNSUCCESSFUL;

                                // send record access control response indication
                                glps_send_racp_rsp(&(racp_rsp),
                                        TASK_GLPS);
                            }
                            break;
                            default:
                            {
                                // nothing to do since it's handled during unpack
                            }
                            break;
                        }
                    }
                    break;
                    default:
                    {
                        /* There is an error in record access control request, inform peer
                         * device that request is incorrect. */
                        struct glp_racp_rsp racp_rsp;

                        racp_rsp.op_code = GLP_REQ_RSP_CODE;
                        racp_rsp.operand.rsp.op_code_req = racp_req.op_code;
                        racp_rsp.operand.rsp.status = reqstatus;

                        // send record access control response indication
                        glps_send_racp_rsp(&(racp_rsp),
                                TASK_GLPS);
                    }
                    break;
                }
            }
        }
        // not expected request
        else
        {
            status = ATT_ERR_WRITE_NOT_PERMITTED;
        }
    }

    if(param->response)
    {
        //Send write response
        atts_write_rsp_send(glps_env.con_info.conidx, param->handle, status);
    }

    return (KE_MSG_CONSUMED);
}