Bluetooth: GATT: Refactor read API
Add read parameters to read callabck Merge bt_gatt_read_multiple functionality into bt_gatt_read. This makes it easier for application to handle all types of reads as same sematics is kept for them. Instead of destroy callback, call read_func with NULL data to indicated that read has completed. This makes it clear when read is completed and parameters used for it are no longer needed. Thanks to this application doesn't need to abuse user data destroy callback for detecting if read has completed. Since destroy callback is no longer needed it is removed. Also note that bt_gatt_read doesn't take any user data parameter and that destroy callback was acctually called with read parameters. If application would require to pass user data along with parameters it may use CONTAINER_OF macro along with bt_gatt_read_params. Change-Id: I8d6ea136b1e61c1dae73cca868b53c48c45a5492 Signed-off-by: Szymon Janc <ext.szymon.janc@tieto.com>
This commit is contained in:
parent
7e2264a065
commit
843268d78d
5 changed files with 143 additions and 200 deletions
|
@ -407,12 +407,6 @@ void bt_gatt_cancel(struct bt_conn *conn)
|
|||
{
|
||||
}
|
||||
|
||||
int bt_gatt_read_multiple(struct bt_conn *conn, const uint16_t *handles,
|
||||
size_t count, bt_gatt_read_func_t func)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void on_ble_gattc_write_rsp(const struct ble_gattc_write_rsp *ev,
|
||||
void *priv)
|
||||
{
|
||||
|
|
|
@ -771,26 +771,40 @@ struct bt_gatt_discover_params {
|
|||
int bt_gatt_discover(struct bt_conn *conn,
|
||||
struct bt_gatt_discover_params *params);
|
||||
|
||||
struct bt_gatt_read_params;
|
||||
|
||||
/** @brief Read callback function
|
||||
*
|
||||
* @param conn Connection object.
|
||||
* @param err Error code.
|
||||
* @param data Attribute value data.
|
||||
* @param params Read parameters used.
|
||||
* @param data Attribute value data. NULL means read has completed.
|
||||
* @param length Attribute value length.
|
||||
*/
|
||||
typedef uint8_t (*bt_gatt_read_func_t)(struct bt_conn *conn, int err,
|
||||
struct bt_gatt_read_params *params,
|
||||
const void *data, uint16_t length);
|
||||
|
||||
/** @brief GATT Read parameters */
|
||||
struct bt_gatt_read_params {
|
||||
/** Attribute handle */
|
||||
uint16_t handle;
|
||||
/** Attribute data offset */
|
||||
uint16_t offset;
|
||||
/** Read attribute callback */
|
||||
bt_gatt_read_func_t func;
|
||||
/** Read destroy callback */
|
||||
void (*destroy)(void *user_data);
|
||||
/** Handles count.
|
||||
* If equals to 1 single.handle and single.offset are used.
|
||||
* If >1 Read Multiple Characteristic Values is performed and handles
|
||||
* are used.
|
||||
*/
|
||||
size_t handle_count;
|
||||
union {
|
||||
struct {
|
||||
/** Attribute handle */
|
||||
uint16_t handle;
|
||||
/** Attribute data offset */
|
||||
uint16_t offset;
|
||||
} single;
|
||||
/** Handles to read in Read Multiple Characteristic Values */
|
||||
uint16_t *handles;
|
||||
};
|
||||
};
|
||||
|
||||
/** @brief Read Attribute Value by handle
|
||||
|
@ -908,21 +922,6 @@ int bt_gatt_unsubscribe(struct bt_conn *conn,
|
|||
*/
|
||||
void bt_gatt_cancel(struct bt_conn *conn);
|
||||
|
||||
/** @brief Read Multiple Attribute Values by set of handles
|
||||
*
|
||||
* Routine to be used to retrieve set of attributes values determined by set of
|
||||
* handles in one call.
|
||||
*
|
||||
* @param conn Connection object.
|
||||
* @param handles Set of valid handles to attributes.
|
||||
* @param count Number of handles to be read.
|
||||
* @param func User callback routine to get retrieved values.
|
||||
*
|
||||
* @return 0 in case of success or negative value in case of error.
|
||||
*/
|
||||
int bt_gatt_read_multiple(struct bt_conn *conn, const uint16_t *handles,
|
||||
size_t count, bt_gatt_read_func_t func);
|
||||
|
||||
#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */
|
||||
#endif /* CONFIG_BLUETOOTH_CENTRAL || CONFIG_BLUETOOTH_PERIPHERAL */
|
||||
|
||||
|
|
|
@ -855,18 +855,18 @@ static uint16_t parse_include(struct bt_conn *conn, const void *pdu,
|
|||
attr->handle = handle;
|
||||
|
||||
if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) {
|
||||
handle = 0;
|
||||
goto done;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop if could not parse the whole PDU */
|
||||
if (length > 0) {
|
||||
return 0;
|
||||
/* Whole PDU read without error */
|
||||
if (length == 0 && handle) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
done:
|
||||
return handle;
|
||||
params->func(conn, NULL, params);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t parse_characteristic(struct bt_conn *conn, const void *pdu,
|
||||
|
@ -929,18 +929,18 @@ static uint16_t parse_characteristic(struct bt_conn *conn, const void *pdu,
|
|||
attr->handle = handle;
|
||||
|
||||
if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) {
|
||||
handle = 0;
|
||||
goto done;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop if could not parse the whole PDU */
|
||||
if (length > 0) {
|
||||
return 0;
|
||||
/* Whole PDU read without error */
|
||||
if (length == 0 && handle) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
done:
|
||||
return handle;
|
||||
params->func(conn, NULL, params);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void att_read_type_rsp(struct bt_conn *conn, uint8_t err,
|
||||
|
@ -963,7 +963,7 @@ static void att_read_type_rsp(struct bt_conn *conn, uint8_t err,
|
|||
}
|
||||
|
||||
if (!handle) {
|
||||
goto done;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Continue from the last handle */
|
||||
|
@ -1166,12 +1166,12 @@ static void att_read_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
|
|||
BT_DBG("err 0x%02x", err);
|
||||
|
||||
if (err) {
|
||||
params->func(conn, err, NULL, 0);
|
||||
goto done;
|
||||
params->func(conn, err, params, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params->func(conn, 0, pdu, length) == BT_GATT_ITER_STOP) {
|
||||
goto done;
|
||||
if (params->func(conn, 0, params, pdu, length) == BT_GATT_ITER_STOP) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1181,21 +1181,15 @@ static void att_read_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
|
|||
* if the rest of the Characteristic Value is required.
|
||||
*/
|
||||
if (length < (bt_att_get_mtu(conn) - 1)) {
|
||||
goto done;
|
||||
params->func(conn, 0, params, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
params->offset += length;
|
||||
params->single.offset += length;
|
||||
|
||||
/* Continue reading the attribute */
|
||||
if (bt_gatt_read(conn, params) < 0) {
|
||||
params->func(conn, BT_ATT_ERR_UNLIKELY, NULL, 0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
return;
|
||||
done:
|
||||
if (params->destroy) {
|
||||
params->destroy(params);
|
||||
params->func(conn, BT_ATT_ERR_UNLIKELY, params, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1211,25 +1205,67 @@ static int gatt_read_blob(struct bt_conn *conn,
|
|||
}
|
||||
|
||||
req = net_buf_add(buf, sizeof(*req));
|
||||
req->handle = sys_cpu_to_le16(params->handle);
|
||||
req->offset = sys_cpu_to_le16(params->offset);
|
||||
req->handle = sys_cpu_to_le16(params->single.handle);
|
||||
req->offset = sys_cpu_to_le16(params->single.offset);
|
||||
|
||||
BT_DBG("handle 0x%04x offset 0x%04x", params->handle, params->offset);
|
||||
BT_DBG("handle 0x%04x offset 0x%04x", params->single.handle,
|
||||
params->single.offset);
|
||||
|
||||
return gatt_send(conn, buf, att_read_rsp, params, NULL);
|
||||
}
|
||||
|
||||
static void att_read_multiple_rsp(struct bt_conn *conn, uint8_t err,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_read_params *params = user_data;
|
||||
|
||||
BT_DBG("err 0x%02x", err);
|
||||
|
||||
if (err) {
|
||||
params->func(conn, err, params, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
params->func(conn, 0, params, pdu, length);
|
||||
|
||||
/* mark read as complete since read multiple is single response */
|
||||
params->func(conn, 0, params, NULL, 0);
|
||||
}
|
||||
|
||||
static int gatt_read_multiple(struct bt_conn *conn,
|
||||
struct bt_gatt_read_params *params)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
uint8_t i;
|
||||
|
||||
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_REQ,
|
||||
params->handle_count * sizeof(uint16_t));
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < params->handle_count; i++) {
|
||||
net_buf_add_le16(buf, params->handles[i]);
|
||||
}
|
||||
|
||||
return gatt_send(conn, buf, att_read_multiple_rsp, params, NULL);
|
||||
}
|
||||
|
||||
int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
struct bt_att_read_req *req;
|
||||
|
||||
if (!conn || !params || !params->handle || !params->func ||
|
||||
!params->destroy) {
|
||||
if (!conn || !params || !params->handle_count || !params->func) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (params->offset) {
|
||||
if (params->handle_count > 1) {
|
||||
return gatt_read_multiple(conn, params);
|
||||
}
|
||||
|
||||
if (params->single.offset) {
|
||||
return gatt_read_blob(conn, params);
|
||||
}
|
||||
|
||||
|
@ -1239,9 +1275,9 @@ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
|
|||
}
|
||||
|
||||
req = net_buf_add(buf, sizeof(*req));
|
||||
req->handle = sys_cpu_to_le16(params->handle);
|
||||
req->handle = sys_cpu_to_le16(params->single.handle);
|
||||
|
||||
BT_DBG("handle 0x%04x", params->handle);
|
||||
BT_DBG("handle 0x%04x", params->single.handle);
|
||||
|
||||
return gatt_send(conn, buf, att_read_rsp, params, NULL);
|
||||
}
|
||||
|
@ -1576,48 +1612,6 @@ void bt_gatt_cancel(struct bt_conn *conn)
|
|||
bt_att_cancel(conn);
|
||||
}
|
||||
|
||||
static void att_read_multiple_rsp(struct bt_conn *conn, uint8_t err,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
bt_gatt_read_func_t func = user_data;
|
||||
|
||||
BT_DBG("err 0x%02x", err);
|
||||
|
||||
if (err) {
|
||||
func(conn, err, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
func(conn, 0, pdu, length);
|
||||
}
|
||||
|
||||
int bt_gatt_read_multiple(struct bt_conn *conn, const uint16_t *handles,
|
||||
size_t count, bt_gatt_read_func_t func)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
uint8_t i;
|
||||
|
||||
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
if (!handles || count < 2 || !func) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_REQ,
|
||||
count * sizeof(*handles));
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
net_buf_add_le16(buf, handles[i]);
|
||||
}
|
||||
|
||||
return gatt_send(conn, buf, att_read_multiple_rsp, func, NULL);
|
||||
}
|
||||
#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */
|
||||
|
||||
void bt_gatt_disconnected(struct bt_conn *conn)
|
||||
|
|
|
@ -770,23 +770,20 @@ done:
|
|||
|
||||
static struct bt_gatt_read_params read_params;
|
||||
|
||||
static uint8_t read_func(struct bt_conn *conn, int err, const void *data,
|
||||
uint16_t length)
|
||||
static uint8_t read_func(struct bt_conn *conn, int err,
|
||||
struct bt_gatt_read_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
printk("Read complete: err %u length %u\n", err, length);
|
||||
|
||||
if (!data) {
|
||||
memset(params, 0, sizeof(*params));
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void read_destroy(void *user_data)
|
||||
{
|
||||
struct bt_gatt_read_params *params = user_data;
|
||||
|
||||
printk("Read destroy\n");
|
||||
|
||||
memset(params, 0, sizeof(*params));
|
||||
}
|
||||
|
||||
static void cmd_gatt_read(int argc, char *argv[])
|
||||
{
|
||||
int err;
|
||||
|
@ -797,17 +794,17 @@ static void cmd_gatt_read(int argc, char *argv[])
|
|||
}
|
||||
|
||||
read_params.func = read_func;
|
||||
read_params.destroy = read_destroy;
|
||||
|
||||
if (argc < 2) {
|
||||
printk("handle required\n");
|
||||
return;
|
||||
}
|
||||
|
||||
read_params.handle = strtoul(argv[1], NULL, 16);
|
||||
read_params.handle_count = 1;
|
||||
read_params.single.handle = strtoul(argv[1], NULL, 16);
|
||||
|
||||
if (argc > 2) {
|
||||
read_params.offset = strtoul(argv[2], NULL, 16);
|
||||
read_params.single.offset = strtoul(argv[2], NULL, 16);
|
||||
}
|
||||
|
||||
err = bt_gatt_read(default_conn, &read_params);
|
||||
|
@ -828,7 +825,7 @@ void cmd_gatt_mread(int argc, char *argv[])
|
|||
return;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
if (argc < 3) {
|
||||
printk("Attribute handles in hex format to read required\n");
|
||||
return;
|
||||
}
|
||||
|
@ -842,7 +839,11 @@ void cmd_gatt_mread(int argc, char *argv[])
|
|||
h[i] = strtoul(argv[i + 1], NULL, 16);
|
||||
}
|
||||
|
||||
err = bt_gatt_read_multiple(default_conn, h, i, read_func);
|
||||
read_params.func = read_func;
|
||||
read_params.handle_count = i;
|
||||
read_params.handles = h; /* not used in read func */
|
||||
|
||||
err = bt_gatt_read(default_conn, &read_params);
|
||||
if (err) {
|
||||
printk("GATT multiple read request failed (err %d)\n", err);
|
||||
}
|
||||
|
|
|
@ -88,15 +88,6 @@ static void gatt_buf_clear(void)
|
|||
memset(&gatt_buf, 0, sizeof(gatt_buf));
|
||||
}
|
||||
|
||||
static bool gatt_buf_isempty(void)
|
||||
{
|
||||
if (gatt_buf.len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct bt_gatt_attr *gatt_db_add(const struct bt_gatt_attr *pattern)
|
||||
{
|
||||
static struct bt_gatt_attr *attr = gatt_db;
|
||||
|
@ -1199,32 +1190,14 @@ fail_conn:
|
|||
|
||||
static struct bt_gatt_read_params read_params;
|
||||
|
||||
static void read_destroy(void *user_data)
|
||||
static void read_destroy(struct bt_gatt_read_params *params)
|
||||
{
|
||||
struct bt_gatt_read_params *params = user_data;
|
||||
|
||||
memset(params, 0, sizeof(*params));
|
||||
|
||||
if (!gatt_buf_isempty()) {
|
||||
gatt_buf_clear();
|
||||
}
|
||||
gatt_buf_clear();
|
||||
}
|
||||
|
||||
static void read_result(void *user_data)
|
||||
{
|
||||
/* Respond with an error if the buffer was cleared. */
|
||||
if (gatt_buf_isempty()) {
|
||||
tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ,
|
||||
CONTROLLER_INDEX, BTP_STATUS_FAILED);
|
||||
} else {
|
||||
tester_send(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX,
|
||||
gatt_buf.buf, gatt_buf.len);
|
||||
}
|
||||
|
||||
read_destroy(user_data);
|
||||
}
|
||||
|
||||
static uint8_t read_cb(struct bt_conn *conn, int err, const void *data,
|
||||
static uint8_t read_cb(struct bt_conn *conn, int err,
|
||||
struct bt_gatt_read_params *params, const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
struct gatt_read_rp *rp = (void *) gatt_buf.buf;
|
||||
|
@ -1232,17 +1205,20 @@ static uint8_t read_cb(struct bt_conn *conn, int err, const void *data,
|
|||
/* Respond to the Lower Tester with ATT Error received */
|
||||
if (err) {
|
||||
rp->att_response = err;
|
||||
}
|
||||
|
||||
/* read complete */
|
||||
if (!data) {
|
||||
tester_send(BTP_SERVICE_ID_GATT, btp_opcode, CONTROLLER_INDEX,
|
||||
gatt_buf.buf, gatt_buf.len);
|
||||
read_destroy(params);
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear gatt_buf if there is no more space available to cache
|
||||
* read result. This will cause read_result function to send
|
||||
* BTP error status to the Lower Tester.
|
||||
*/
|
||||
if (!gatt_buf_add(data, length)) {
|
||||
gatt_buf_clear();
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
|
||||
CONTROLLER_INDEX, BTP_STATUS_FAILED);
|
||||
read_destroy(params);
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
|
@ -1265,10 +1241,13 @@ static void read(uint8_t *data, uint16_t len)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
read_params.handle = sys_le16_to_cpu(cmd->handle);
|
||||
read_params.offset = 0x0000;
|
||||
read_params.handle_count = 1;
|
||||
read_params.single.handle = sys_le16_to_cpu(cmd->handle);
|
||||
read_params.single.offset = 0x0000;
|
||||
read_params.func = read_cb;
|
||||
read_params.destroy = read_result;
|
||||
|
||||
/* TODO should be handled as user_data via CONTAINER_OF macro */
|
||||
btp_opcode = GATT_READ;
|
||||
|
||||
if (bt_gatt_read(conn, &read_params) < 0) {
|
||||
read_destroy(&read_params);
|
||||
|
@ -1287,20 +1266,6 @@ fail_conn:
|
|||
BTP_STATUS_FAILED);
|
||||
}
|
||||
|
||||
static void read_long_result(void *user_data)
|
||||
{
|
||||
/* Respond with an error if the buffer was cleared. */
|
||||
if (gatt_buf_isempty()) {
|
||||
tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_LONG,
|
||||
CONTROLLER_INDEX, BTP_STATUS_FAILED);
|
||||
} else {
|
||||
tester_send(BTP_SERVICE_ID_GATT, GATT_READ_LONG,
|
||||
CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
|
||||
}
|
||||
|
||||
read_destroy(user_data);
|
||||
}
|
||||
|
||||
static void read_long(uint8_t *data, uint16_t len)
|
||||
{
|
||||
const struct gatt_read_long_cmd *cmd = (void *) data;
|
||||
|
@ -1315,10 +1280,13 @@ static void read_long(uint8_t *data, uint16_t len)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
read_params.handle = sys_le16_to_cpu(cmd->handle);
|
||||
read_params.offset = sys_le16_to_cpu(cmd->offset);
|
||||
read_params.handle_count = 1;
|
||||
read_params.single.handle = sys_le16_to_cpu(cmd->handle);
|
||||
read_params.single.offset = sys_le16_to_cpu(cmd->offset);
|
||||
read_params.func = read_cb;
|
||||
read_params.destroy = read_long_result;
|
||||
|
||||
/* TODO should be handled as user_data via CONTAINER_OF macro */
|
||||
btp_opcode = GATT_READ_LONG;
|
||||
|
||||
if (bt_gatt_read(conn, &read_params) < 0) {
|
||||
read_destroy(&read_params);
|
||||
|
@ -1337,24 +1305,6 @@ fail_conn:
|
|||
BTP_STATUS_FAILED);
|
||||
}
|
||||
|
||||
static uint8_t read_multiple_result(struct bt_conn *conn, int err,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
read_cb(conn, err, data, length);
|
||||
|
||||
if (gatt_buf_isempty()) {
|
||||
tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_MULTIPLE,
|
||||
CONTROLLER_INDEX, BTP_STATUS_FAILED);
|
||||
} else {
|
||||
tester_send(BTP_SERVICE_ID_GATT, GATT_READ_MULTIPLE,
|
||||
CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
|
||||
|
||||
gatt_buf_clear();
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static void read_multiple(uint8_t *data, uint16_t len)
|
||||
{
|
||||
const struct gatt_read_multiple_cmd *cmd = (void *) data;
|
||||
|
@ -1375,10 +1325,15 @@ static void read_multiple(uint8_t *data, uint16_t len)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (bt_gatt_read_multiple(conn, handles, cmd->handles_count,
|
||||
read_multiple_result) < 0) {
|
||||
gatt_buf_clear();
|
||||
read_params.func = read_cb;
|
||||
read_params.handle_count = i;
|
||||
read_params.handles = handles; /* not used in read func */
|
||||
|
||||
/* TODO should be handled as user_data via CONTAINER_OF macro */
|
||||
btp_opcode = GATT_READ_MULTIPLE;
|
||||
|
||||
if (bt_gatt_read(conn, &read_params) < 0) {
|
||||
gatt_buf_clear();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue