bleSDK_expansion_board/ble/prf/prf_ptss.c

784 lines
24 KiB
C

/**
****************************************************************************************
*
* @file prf_ptss.c
*
* @brief Profile Testing Service - Server Role Implementation.
*
* < If want to modify it, recommend to copy the file to 'user porject'/src >
****************************************************************************************
*/
#if (PRF_PTSS)
/*
* INCLUDE FILES
****************************************************************************************
*/
#include "prf.h"
#include "prf_ptss.h"
#if (DBG_PTSS)
#include "dbg.h"
#define DEBUG(format, ...) debug("<PTSS>" format "\r\n", ##__VA_ARGS__)
#else
#define DEBUG(format, ...)
#define debugHex(dat, len)
#endif
/*
* DEFINITIONS
****************************************************************************************
*/
/// Version String for Read
#define PTS_VERS_STR ("Ver:1.25")
#define PTS_VERS_STR_LEN (sizeof(PTS_VERS_STR) - 1)
/// Macro for Client Config value operation
#define PTS_CLI_CFG_GET(conidx) \
((ptss_env.cli_cfg >> (conidx*2)) & (PRF_CLI_START_NTF | PRF_CLI_START_IND))
#define PTS_CLI_CFG_CLR(conidx) \
ptss_env.cli_cfg &= ~((PRF_CLI_START_NTF | PRF_CLI_START_IND) << (conidx*2))
#define PTS_CLI_CFG_SET(conidx, conf) \
ptss_env.cli_cfg = (ptss_env.cli_cfg & ~((PRF_CLI_START_NTF | PRF_CLI_START_IND) << (conidx*2))) | ((conf) << (conidx*2))
/**
****************************************************************************************
* @section ENVIRONMENT DEFINITION
****************************************************************************************
*/
/// Server Environment Variable
typedef struct ptss_env_tag
{
// Service Start Handle
uint16_t start_hdl;
// CCC of peer devices(bits).
uint16_t cli_cfg;
} ptss_env_t;
/// Global Variable Declarations
ptss_env_t ptss_env;
/// Global state of bonded, changed in gapc
uint8_t pts_bond;
/// Buffer of received data
static uint16_t recv_len = PTS_DATA_MAX_LEN;
static uint8_t recv_buf[PTS_DATA_MAX_LEN];
/// Buffer of read data
static uint16_t read_len = PTS_DATA_MAX_LEN;
static uint8_t read_buf[PTS_DATA_MAX_LEN];
/// Buffer of desc data
static uint16_t desc_len = PTS_DESC_MAX_LEN;
static uint8_t desc_buf[PTS_DESC_MAX_LEN];
/**
****************************************************************************************
* @section ATTRIBUTES DEFINITION
****************************************************************************************
*/
/// Service Attributes Indexes
enum pts_att_idx
{
// Service Declaration, *MUST* Start at 0
PTS_IDX_SVC,
// Attribute No.1: NTF/IND/RD
PTS_IDX_ATT1_CHAR,
PTS_IDX_ATT1_VAL,
PTS_IDX_ATT1_DESC,
// Attribute No.2: WR/WC/WS
PTS_IDX_ATT2_CHAR,
PTS_IDX_ATT2_VAL,
// Attribute No.3: RD
PTS_IDX_ATT3_CHAR,
PTS_IDX_ATT3_VAL,
// Max Index, *NOTE* Minus 1(Svc Decl) is .nb_att
PTS_IDX_NB,
};
/////////////////////////////////////////////////////////////////////////////
/// *** 0: Test for Normal ***
/////////////////////////////////////////////////////////////////////////////
#define PTS0_SVC_UUID ATT_UUID16(0xFF00)
#define PTS0_CHAR_ATT1 ATT_UUID16(0xFF01)
#define PTS0_CHAR_ATT2 ATT_UUID16(0xFF02)
#define PTS0_CHAR_ATT3 ATT_UUID16(0xFF03)
const att_decl_t pts0_atts[] =
{
// No.1 Characteristic Declaration and Value and CCC Descriptor
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT1_CHAR ),
ATT_ELMT( PTS_IDX_ATT1_VAL, PTS0_CHAR_ATT1, PROP_NTF|PROP_IND, 0 ),
ATT_ELMT_DESC_CLI_CHAR_CFG( PTS_IDX_ATT1_DESC ),
// No.2 Characteristic Declaration and Value
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT2_CHAR ),
ATT_ELMT( PTS_IDX_ATT2_VAL, PTS0_CHAR_ATT2, PROP_WC|PROP_WR|PROP_WS, PTS_DATA_MAX_LEN ),
// No.3 Characteristic Declaration and Value
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT3_CHAR ),
ATT_ELMT( PTS_IDX_ATT3_VAL, PTS0_CHAR_ATT3, PROP_RD, 0 ),
};
const struct svc_decl pts0_svc_db =
{
.uuid = PTS0_SVC_UUID,
.info = SVC_UUID(16),
.atts = pts0_atts,
.nb_att = PTS_IDX_NB - 1,
};
/////////////////////////////////////////////////////////////////////////////
/// *** 1: Test for ERROR ***
/////////////////////////////////////////////////////////////////////////////
#define PTS1_SVC_UUID ATT_UUID16(0xFF20)
#define PTS1_CHAR_ATT1 ATT_UUID16(0xFF21)
#define PTS1_CHAR_ATT2 ATT_UUID16(0xFF22)
#define PTS1_CHAR_ATT3 ATT_UUID16(0xFF23)
const att_decl_t pts1_atts[] =
{
// No.1 Characteristic Declaration and Value and User Descriptor
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT1_CHAR ),
ATT_ELMT( PTS_IDX_ATT1_VAL, PTS1_CHAR_ATT1, PROP_WC|PROP_WR|PROP_RD, PTS_DATA_MAX_LEN ),
ATT_ELMT( PTS_IDX_ATT1_DESC, ATT_DESC_CHAR_USER_DESCRIPTION, PROP_RD, 0 ),
// No.2 Characteristic Declaration and Value
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT2_CHAR ),
ATT_ELMT( PTS_IDX_ATT2_VAL, PTS1_CHAR_ATT2, PROP_WC|PROP_WR|PROP_RD, PTS_DATA_MAX_LEN ),
// No.3 Characteristic Declaration and Value
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT3_CHAR ),
ATT_ELMT( PTS_IDX_ATT3_VAL, PTS1_CHAR_ATT3, PROP_WC|PROP_WR|PROP_RD, PTS_DATA_MAX_LEN ),
};
const struct svc_decl pts1_svc_db =
{
.uuid = PTS1_SVC_UUID,
.info = SVC_UUID(16),
.atts = pts1_atts,
.nb_att = PTS_IDX_NB - 1,
};
/////////////////////////////////////////////////////////////////////////////
/// *** 2: Test for UUID128 ***
/////////////////////////////////////////////////////////////////////////////
#define PTS_ATT_UUID128(uuid) { 0x16, 0x0A, 0x10, 0x40, 0xD1, 0x9F, 0x4C, 0x6C, \
0xB4, 0x55, 0xE3, 0xF7, (uuid) & 0xFF, (uuid >> 8) & 0xFF, 0x00, 0x00}
const uint8_t PTS2_SVC_UUID[] = PTS_ATT_UUID128(0xFF00);
const uint8_t PTS2_CHAR_ATT1[] = PTS_ATT_UUID128(0xFF01);
const uint8_t PTS2_CHAR_ATT2[] = PTS_ATT_UUID128(0xFF02);
const uint8_t PTS2_CHAR_ATT3[] = PTS_ATT_UUID128(0xFF03);
const att_decl_t pts2_atts[] =
{
// No.1 Characteristic Declaration and Value and User Descriptor
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT1_CHAR ),
ATT_ELMT128( PTS_IDX_ATT1_VAL, PTS2_CHAR_ATT1, PROP_RD, 0 ),
ATT_ELMT( PTS_IDX_ATT1_DESC, ATT_DESC_CHAR_USER_DESCRIPTION, PROP_RD|PROP_WR, PTS_DESC_MAX_LEN ),
// No.2 Characteristic Declaration and Value
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT2_CHAR ),
ATT_ELMT128( PTS_IDX_ATT2_VAL, PTS2_CHAR_ATT2, PROP_WC|PROP_WR|PROP_RD, PTS_DATA_MAX_LEN ),
// No.3 Characteristic Declaration and Value
ATT_ELMT_DECL_CHAR( PTS_IDX_ATT3_CHAR ),
ATT_ELMT128( PTS_IDX_ATT3_VAL, PTS2_CHAR_ATT3, PROP_RD|PROP_WR, PTS_DATA_MAX_LEN ),
};
const struct svc_decl pts_svc_db2 =
{
.uuid128 = PTS2_SVC_UUID,
.info = SVC_UUID(128),
.atts = pts2_atts,
.nb_att = PTS_IDX_NB - 1,
};
/*
* FUNCTION DECLARATIONS
****************************************************************************************
*/
/**
****************************************************************************************
* @section SVC FUNCTIONS
****************************************************************************************
*/
/// Retrieve attribute handle from index (@see pts_att_idx)
static uint16_t ptss_get_att_handle(uint8_t att_idx)
{
ASSERT_ERR(att_idx < PTS_ATT_NB); //svr_idx=0
return (att_idx + ptss_env.start_hdl);
}
/// Retrieve attribute index form handle or ATT_INVALID_IDX if nothing found
static uint8_t ptss_get_att_idx(uint8_t *svr_idx, uint16_t handle)
{
uint8_t att_idx = ATT_INVALID_IDX;
if ((handle >= ptss_env.start_hdl) && (handle < ptss_env.start_hdl + PTS_IDX_NB))
{
att_idx = handle - ptss_env.start_hdl;
*svr_idx = 0;
}
else if ((handle >= ptss_env.start_hdl + PTS_IDX_NB) && (handle < ptss_env.start_hdl + PTS_IDX_NB*2))
{
att_idx = handle - (ptss_env.start_hdl + PTS_IDX_NB);
*svr_idx = 1;
}
else if ((handle >= ptss_env.start_hdl + PTS_IDX_NB*2) && (handle < ptss_env.start_hdl + PTS_IDX_NB*3))
{
att_idx = handle - (ptss_env.start_hdl + PTS_IDX_NB*2);
*svr_idx = 2;
}
return (att_idx);
}
/// Handles reception of the attribute info request message.
static void ptss_att_info_cfm(uint8_t conidx, uint8_t svr_idx, uint8_t att_idx, uint16_t handle)
{
uint16_t length = 0;
uint8_t status = LE_SUCCESS;
if (att_idx != ATT_INVALID_IDX)
{
if (svr_idx == 1) // PTS1 Error
{
// Long Write Error
if (att_idx == PTS_IDX_ATT1_VAL)
{
if ((pts_bond & GAP_AUTH_BOND) == 0)
status = ATT_ERR_INSUFF_AUTHOR;
else
length = PTS_DATA_MAX_LEN;
}
else if (att_idx == PTS_IDX_ATT2_VAL)
{
if ((pts_bond & GAP_AUTH_MITM) == 0)
status = ATT_ERR_INSUFF_AUTHEN;
else
length = PTS_DATA_MAX_LEN;
}
else if (att_idx == PTS_IDX_ATT3_VAL)
{
//if (!pts_bond)
status = ATT_ERR_INSUFF_ENC_KEY_SIZE;
//else
// cfm->length = PTS_DATA_MAX_LEN;
}
else
{
status = ATT_ERR_WRITE_NOT_PERMITTED;
}
}
else if (svr_idx == 2) // PTS2 Normal
{
if ((att_idx == PTS_IDX_ATT2_VAL) || (att_idx == PTS_IDX_ATT3_VAL))
{
length = PTS_DATA_MAX_LEN;
}
else if (att_idx == PTS_IDX_ATT1_DESC)
{
length = PTS_DESC_MAX_LEN;
}
else
{
status = ATT_ERR_WRITE_NOT_PERMITTED;
}
}
else // PTS0 Normal
{
if (att_idx == PTS_IDX_ATT2_VAL)
{
length = PTS_DATA_MAX_LEN;
}
else if (att_idx == PTS_IDX_ATT1_DESC)
{
length = sizeof(uint16_t); // CCC attribute
}
else
{
status = ATT_ERR_WRITE_NOT_PERMITTED;
}
}
}
else
{
status = PRF_ERR_APP_ERROR;
}
DEBUG(" info_cfm(svr:%d,hdl:0x%x,sta:0x%x,len:%d)", svr_idx, param->handle, status, length);
// Send info response
gatt_info_cfm(conidx, status, handle, length);
}
/// Confirm ATTS_WRITE_REQ
static void ptss_att_write_cfm(uint8_t conidx, uint8_t svr_idx, uint8_t att_idx, uint16_t handle, const struct atts_write_ind *ind)
{
uint8_t status = LE_SUCCESS;
if (att_idx != ATT_INVALID_IDX)
{
if (svr_idx == 1) // PTS1 Error
{
// Write Error
if (att_idx == PTS_IDX_ATT1_VAL)
{
if ((pts_bond & GAP_AUTH_BOND) == 0)
status = ATT_ERR_INSUFF_AUTHOR;
}
else if (att_idx == PTS_IDX_ATT2_VAL)
{
if ((pts_bond & GAP_AUTH_MITM) == 0)
status = ATT_ERR_INSUFF_AUTHEN;
}
else if (att_idx == PTS_IDX_ATT3_VAL)
{
//if (!pts_bond)
status = ATT_ERR_INSUFF_ENC_KEY_SIZE;
}
else
{
status = ATT_ERR_WRITE_NOT_PERMITTED;
}
}
else if (svr_idx == 2) // PTS2 Normal
{
if ((att_idx == PTS_IDX_ATT2_VAL) || (att_idx == PTS_IDX_ATT3_VAL))
{
// received data to callback
ptss_cb_recv(conidx, ind->length, ind->value);
// Save data to read-back
if (att_idx == PTS_IDX_ATT2_VAL)
{
recv_len = ind->length;
memcpy(recv_buf, ind->value, ind->length);
}
else
{
read_len = ind->length;
memcpy(read_buf, ind->value, ind->length);
}
}
else if (att_idx == PTS_IDX_ATT1_DESC)
{
// Save desc to read-back
desc_len = ind->length;
memcpy(desc_buf, ind->value, desc_len);
}
else
{
status = ATT_ERR_WRITE_NOT_PERMITTED;
}
}
else // PTS0 Normal
{
if (att_idx == PTS_IDX_ATT2_VAL)
{
// received data to callback
ptss_cb_recv(conidx, ind->length, ind->value);
}
else if (att_idx == PTS_IDX_ATT1_DESC)
{
// update configuration if value for stop or notification enable
uint8_t cli_cfg = ind->value[0] & (PRF_CLI_START_NTF | PRF_CLI_START_IND);
PTS_CLI_CFG_SET(conidx, cli_cfg);
// client conf to callback
ptss_cb_ccc(conidx, cli_cfg);
}
else
{
status = PRF_ERR_APP_ERROR;
}
}
}
else
{
status = PRF_ERR_APP_ERROR;
}
DEBUG(" --write_cfm(svr:%d,hdl:0x%x,sta:0x%x)", svr_idx, handle, status);
// Send write conform
gatt_write_cfm(conidx, status, handle);
}
/// Confirm ATTS_READ_REQ
static void ptss_att_read_cfm(uint8_t conidx, uint8_t svr_idx, uint8_t att_idx, uint16_t handle)
{
uint16_t length = 0;
uint8_t status = LE_SUCCESS;
if (att_idx != ATT_INVALID_IDX)
{
if (svr_idx == 1) // PTS1 Error
{
// Read Error
if (att_idx == PTS_IDX_ATT1_VAL)
{
if ((pts_bond & GAP_AUTH_BOND) == 0)
status = ATT_ERR_INSUFF_AUTHOR;
else
length = PTS_DATA_MAX_LEN;
}
else if (att_idx == PTS_IDX_ATT2_VAL)
{
if ((pts_bond & GAP_AUTH_MITM) == 0)
status = ATT_ERR_INSUFF_AUTHEN;
else
length = PTS_DATA_MAX_LEN;
}
else if (att_idx == PTS_IDX_ATT3_VAL)
{
//if (!pts_bond)
status = ATT_ERR_INSUFF_ENC_KEY_SIZE;
//else
// length = PTS_DATA_MAX_LEN;
}
else if (att_idx == PTS_IDX_ATT1_DESC)
{
length = 2 * gatt_get_mtu(conidx) - 2;
}
else
{
status = ATT_ERR_READ_NOT_PERMITTED;
}
}
else if (svr_idx == 2) // PTS2 Normal
{
if (att_idx == PTS_IDX_ATT1_VAL)
{
length = gatt_get_mtu(conidx);
if (length < 512) length--;
}
else if (att_idx == PTS_IDX_ATT1_DESC)
{
length = desc_len;
}
else if (att_idx == PTS_IDX_ATT3_VAL)
{
length = read_len;
}
else if (att_idx == PTS_IDX_ATT2_VAL)
{
length = recv_len;
}
else
{
status = ATT_ERR_READ_NOT_PERMITTED;
}
}
else // PTS0 Normal
{
if (att_idx == PTS_IDX_ATT1_DESC)
{
length = sizeof(uint16_t);
}
else if (att_idx == PTS_IDX_ATT3_VAL)
{
//if (ptss_env.rdv_cb)
// length = ptss_env.rdv_cb(conidx, NULL);
length = gatt_get_mtu(conidx);
if (length < 512) length--;
}
else
{
status = ATT_ERR_READ_NOT_PERMITTED;
}
}
}
else
{
status = PRF_ERR_APP_ERROR;
}
DEBUG(" read_cfm(svr:%d,hdl:0x%x,sta:0x%x,len:%d)", svr_idx, param->handle, status, length);
// Send read response
if (status == LE_SUCCESS)
{
if (svr_idx == 2)
{
// PTS2 read-back
if (att_idx == PTS_IDX_ATT2_VAL)
{
gatt_read_cfm(conidx, status, handle, recv_len, recv_buf);
return;
}
else if (att_idx == PTS_IDX_ATT3_VAL)
{
gatt_read_cfm(conidx, status, handle, read_len, read_buf);
return;
}
else if (att_idx == PTS_IDX_ATT1_DESC)
{
gatt_read_cfm(conidx, status, handle, desc_len, desc_buf);
return;
}
}
else
{
// PTS0 fill data
if (att_idx == PTS_IDX_ATT1_DESC)
{
uint16_t cli_cfg = PTS_CLI_CFG_GET(conidx);
gatt_read_cfm(conidx, status, handle, length, (uint8_t *)&cli_cfg);
return;
}
else if (att_idx == PTS_IDX_ATT3_VAL)
{
ptss_cb_read(conidx, att_idx, handle);
return;
}
}
}
gatt_read_cfm(conidx, status, handle, length, NULL);
}
/// Handles reception of the atts request from peer device
static void ptss_svc_func(uint8_t conidx, uint8_t opcode, uint16_t handle, const void *param)
{
uint8_t svr_idx, att_idx;
att_idx = ptss_get_att_idx(&svr_idx, handle);
DEBUG("svc_func(cid:%d,op:0x%x,hdl:0x%x,svr:%d,att:%d)", conidx, opcode, handle, svr_idx, att_idx);
switch (opcode)
{
case ATTS_READ_REQ:
{
ptss_att_read_cfm(conidx, svr_idx, att_idx, handle);
} break;
case ATTS_WRITE_REQ:
{
const struct atts_write_ind *ind = param;
DEBUG(" write_req(hdl:0x%x,att:%d,wr:0x%x,len:%d)", handle, att_idx, ind->wrcode, ind->length);
ptss_att_write_cfm(conidx, svr_idx, att_idx, handle, ind);
} break;
case ATTS_INFO_REQ:
{
ptss_att_info_cfm(conidx, svr_idx, att_idx, handle);
} break;
case ATTS_CMP_EVT:
{
const struct atts_cmp_evt *evt = param;
DEBUG(" cmp_evt(op:0x%x,sta:0x%x,seq:%d)", evt->operation, evt->status, evt->seq_num);
// add 'if' to avoid warning #117-D: "evt" never referenced
if (evt->operation == GATT_NOTIFY)
{
// Notify result
}
} break;
default:
{
// nothing to do
} break;
}
}
/**
****************************************************************************************
* @section API FUNCTIONS
****************************************************************************************
*/
/**
****************************************************************************************
* @brief Add Service Profile in the DB
* Customize via pre-define @see PTSS_START_HDL
*
* @return Result status, LE_SUCCESS or Error Reason
****************************************************************************************
*/
uint8_t ptss_svc_init(void)
{
uint8_t status = LE_SUCCESS;
uint16_t next_hdl;
// Init Environment
ptss_env.start_hdl = PTS_START_HDL;
ptss_env.cli_cfg = 0;
#if (PTSS_START_HDL)
*start_hdl = PTSS_START_HDL; // at special handle
#endif
// Create Service in database
status = attmdb_svc_create(&ptss_env.start_hdl, NULL, &pts0_svc_db, ptss_svc_func);
DEBUG("svc_init0(sta:0x%X,shdl:%d)", status, ptss_env.start_hdl);
if (status != LE_SUCCESS)
{
return status;
}
next_hdl = ptss_env.start_hdl + PTS_IDX_NB;
status = attmdb_svc_create(&next_hdl, NULL, &pts1_svc_db, ptss_svc_func);
DEBUG("svc_init1(sta:0x%X,shdl:%d)", status, next_hdl);
if (status != LE_SUCCESS)
{
return status;
}
next_hdl += PTS_IDX_NB;
status = attmdb_svc_create(&next_hdl, NULL, &pts_svc_db2, ptss_svc_func);
if (status != LE_SUCCESS)
{
return status;
}
return status;
}
/**
****************************************************************************************
* @brief Enable setting client configuration characteristics
*
* @param[in] conidx Connection index
* @param[in] cli_cfg Client configuration @see prf_cli_conf
*
* @return Status of the operation @see prf_err
****************************************************************************************
*/
void ptss_set_ccc(uint8_t conidx, uint8_t cli_cfg)
{
//if (gapc_get_conhdl(conidx) != GAP_INVALID_CONHDL)
{
// update configuration
PTS_CLI_CFG_SET(conidx, cli_cfg);
}
}
/**
****************************************************************************************
* @brief Transmit data to peer device via NTF or IND
*
* @param[in] conidx peer destination connection index
* @param[in] handle Handle of NTF/IND
* @param[in] len Length of data
* @param[in] data pointer of buffer
*
* @return Status of the operation @see prf_err
****************************************************************************************
*/
uint8_t ptss_evt_send(uint8_t conidx, uint16_t handle, uint16_t len, const uint8_t* data)
{
uint8_t status = PRF_ERR_REQ_DISALLOWED;
if (len > 0)
{
uint8_t cli_cfg = PTS_CLI_CFG_GET(conidx);
if (cli_cfg != PRF_CLI_STOP_NTFIND)
{
uint8_t operation = (cli_cfg & PRF_CLI_START_NTF) ? GATT_NOTIFY : GATT_INDICATE;
if (handle == 0) ptss_get_att_handle(PTS_IDX_ATT1_VAL);
gatt_ntf_send(conidx, handle, len, data);
DEBUG("Send(hdl:0x%x,op:0x%x,len:%d)", handle, operation, len);
status = LE_SUCCESS;
}
else
{
status = PRF_ERR_NTF_DISABLED;
}
}
return status;
}
/**
****************************************************************************************
* @brief Callback on received data from peer device via WC or WQ (__weak func)
*
* @param[in] conidx peer device connection index
* @param[in] len Length of data
* @param[in] data pointer of buffer
****************************************************************************************
*/
__weak void ptss_cb_recv(uint8_t conidx, uint16_t len, const uint8_t *data)
{
DEBUG("Recv(cid:%d,len:%d)", conidx, len);
debugHex(data, len);
}
/**
****************************************************************************************
* @brief Callback to response 'READ' from peer device (__weak func)
*
* @param[in] conidx peer device connection index
* @param[in] attidx SESS attribute index, converted with 'handle'
* @param[in] handle SESS attribute handle to send read cfm
*
* @return Length of value been READ
****************************************************************************************
*/
__weak void ptss_cb_read(uint8_t conidx, uint8_t attidx, uint16_t handle)
{
uint16_t length = PTS_VERS_STR_LEN;
const uint8_t *p_data = (const uint8_t *)PTS_VERS_STR;
DEBUG(" read_cfm(att:%d, len:%d)", attidx, length);
gatt_read_cfm(conidx, LE_SUCCESS, handle, length, p_data);
}
/**
****************************************************************************************
* @brief Callback on enabled client config from peer device via WQ (__weak func)
*
* @param[in] conidx Connection index
* @param[in] cli_cfg Client configuration @see prf_cli_conf
****************************************************************************************
*/
__weak void ptss_cb_ccc(uint8_t conidx, uint8_t cli_cfg)
{
DEBUG("Enable(cid:%d,cfg:%d)", conidx, cli_cfg);
}
#endif //PRF_PTSS