Bluetooth: GATT: Add support for new PDUs
This adds support for ATT_MULTIPLE_HANDLE_VALUE_NTF, ATT_READ_MULTIPLE_VARIABLE_REQ and ATT_READ_MULTIPLE_VARIABLE_RSP. Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
f4192bda26
commit
e0cbdf3b87
5 changed files with 406 additions and 3 deletions
|
@ -876,7 +876,7 @@ int bt_gatt_notify_cb(struct bt_conn *conn,
|
|||
*
|
||||
* @param conn Connection object.
|
||||
* @param num_params Number of notification parameters.
|
||||
* @param params Notification parameters.
|
||||
* @param params Array of notification parameters.
|
||||
*
|
||||
* @return 0 in case of success or negative value in case of error.
|
||||
*/
|
||||
|
|
|
@ -102,6 +102,16 @@ config BT_GATT_CACHING
|
|||
characteristics which can be used by clients to detect if anything has
|
||||
changed on the GATT database.
|
||||
|
||||
if BT_GATT_CACHING
|
||||
|
||||
config BT_GATT_NOTIFY_MULTIPLE
|
||||
bool "GATT Notify Multiple Characteristic Values support"
|
||||
depends on BT_GATT_CACHING
|
||||
default y
|
||||
help
|
||||
This option enables support for the GATT Notify Multiple
|
||||
Characteristic Values procedure.
|
||||
|
||||
config BT_GATT_ENFORCE_CHANGE_UNAWARE
|
||||
bool "GATT Enforce change-unaware state"
|
||||
depends on BT_GATT_CACHING
|
||||
|
@ -114,6 +124,8 @@ config BT_GATT_ENFORCE_CHANGE_UNAWARE
|
|||
In case the service cannot deal with sudden errors (-EAGAIN) then it
|
||||
shall not use this option.
|
||||
|
||||
endif # BT_GATT_CACHING
|
||||
|
||||
config BT_GATT_CLIENT
|
||||
bool "GATT client support"
|
||||
help
|
||||
|
|
|
@ -1239,6 +1239,94 @@ static u8_t att_read_mult_req(struct bt_att_chan *chan, struct net_buf *buf)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_EATT)
|
||||
static u8_t read_vl_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||
{
|
||||
struct read_data *data = user_data;
|
||||
struct bt_att_chan *chan = data->chan;
|
||||
struct bt_conn *conn = chan->chan.chan.conn;
|
||||
struct bt_att_read_mult_vl_rsp *rsp;
|
||||
int read;
|
||||
|
||||
BT_DBG("handle 0x%04x", attr->handle);
|
||||
|
||||
data->rsp = net_buf_add(data->buf, sizeof(*data->rsp));
|
||||
|
||||
/*
|
||||
* If any attribute is founded in handle range it means that error
|
||||
* should be changed from pre-set: invalid handle error to no error.
|
||||
*/
|
||||
data->err = 0x00;
|
||||
|
||||
/* Check attribute permissions */
|
||||
data->err = bt_gatt_check_perm(conn, attr, BT_GATT_PERM_READ_MASK);
|
||||
if (data->err) {
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
/* The Length Value Tuple List may be truncated within the first two
|
||||
* octets of a tuple due to the size limits of the current ATT_MTU.
|
||||
*/
|
||||
if (chan->chan.tx.mtu - data->buf->len < 2) {
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
rsp = net_buf_add(data->buf, sizeof(*rsp));
|
||||
|
||||
read = att_chan_read(chan, attr, data->buf, data->offset, NULL, NULL);
|
||||
if (read < 0) {
|
||||
data->err = err_to_att(read);
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
rsp->len = read;
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static u8_t att_read_mult_vl_req(struct bt_att_chan *chan, struct net_buf *buf)
|
||||
{
|
||||
struct bt_conn *conn = chan->chan.chan.conn;
|
||||
struct read_data data;
|
||||
u16_t handle;
|
||||
|
||||
(void)memset(&data, 0, sizeof(data));
|
||||
|
||||
data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_VL_RSP, 0);
|
||||
if (!data.buf) {
|
||||
return BT_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
|
||||
data.chan = chan;
|
||||
|
||||
while (buf->len >= sizeof(u16_t)) {
|
||||
handle = net_buf_pull_le16(buf);
|
||||
|
||||
BT_DBG("handle 0x%04x ", handle);
|
||||
|
||||
/* If handle is not valid then return invalid handle error.
|
||||
* If handle is found error will be cleared by read_cb.
|
||||
*/
|
||||
data.err = BT_ATT_ERR_INVALID_HANDLE;
|
||||
|
||||
bt_gatt_foreach_attr(handle, handle, read_vl_cb, &data);
|
||||
|
||||
/* Stop reading in case of error */
|
||||
if (data.err) {
|
||||
net_buf_unref(data.buf);
|
||||
/* Respond here since handle is set */
|
||||
send_err_rsp(chan, BT_ATT_OP_READ_MULT_VL_REQ, handle,
|
||||
data.err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
(void)bt_att_chan_send(chan, data.buf, chan_rsp_sent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_EATT */
|
||||
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
||||
|
||||
struct read_group_data {
|
||||
|
@ -1894,6 +1982,16 @@ static u8_t att_handle_read_mult_rsp(struct bt_att_chan *chan,
|
|||
|
||||
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_EATT)
|
||||
static u8_t att_handle_read_mult_vl_rsp(struct bt_att_chan *chan,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
BT_DBG("");
|
||||
|
||||
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
||||
}
|
||||
#endif /* CONFIG_BT_EATT */
|
||||
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
||||
|
||||
static u8_t att_handle_read_group_rsp(struct bt_att_chan *chan,
|
||||
|
@ -1960,6 +2058,15 @@ static u8_t att_indicate(struct bt_att_chan *chan, struct net_buf *buf)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8_t att_notify_mult(struct bt_att_chan *chan, struct net_buf *buf)
|
||||
{
|
||||
BT_DBG("chan %p", chan);
|
||||
|
||||
bt_gatt_mult_notification(chan->att->conn, buf->data, buf->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_GATT_CLIENT */
|
||||
|
||||
static u8_t att_confirm(struct bt_att_chan *chan, struct net_buf *buf)
|
||||
|
@ -2004,6 +2111,12 @@ static const struct att_handler {
|
|||
BT_ATT_READ_MULT_MIN_LEN_REQ,
|
||||
ATT_REQUEST,
|
||||
att_read_mult_req },
|
||||
#if defined(CONFIG_BT_EATT)
|
||||
{ BT_ATT_OP_READ_MULT_VL_REQ,
|
||||
BT_ATT_READ_MULT_MIN_LEN_REQ,
|
||||
ATT_REQUEST,
|
||||
att_read_mult_vl_req },
|
||||
#endif /* CONFIG_BT_EATT */
|
||||
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
||||
{ BT_ATT_OP_READ_GROUP_REQ,
|
||||
sizeof(struct bt_att_read_group_req),
|
||||
|
@ -2070,6 +2183,12 @@ static const struct att_handler {
|
|||
sizeof(struct bt_att_read_mult_rsp),
|
||||
ATT_RESPONSE,
|
||||
att_handle_read_mult_rsp },
|
||||
#if defined(CONFIG_BT_EATT)
|
||||
{ BT_ATT_OP_READ_MULT_VL_RSP,
|
||||
sizeof(struct bt_att_read_mult_vl_rsp),
|
||||
ATT_RESPONSE,
|
||||
att_handle_read_mult_vl_rsp },
|
||||
#endif /* CONFIG_BT_EATT */
|
||||
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
||||
{ BT_ATT_OP_READ_GROUP_RSP,
|
||||
sizeof(struct bt_att_read_group_rsp),
|
||||
|
@ -2095,6 +2214,10 @@ static const struct att_handler {
|
|||
sizeof(struct bt_att_indicate),
|
||||
ATT_INDICATION,
|
||||
att_indicate },
|
||||
{ BT_ATT_OP_NOTIFY_MULT,
|
||||
sizeof(struct bt_att_notify_mult),
|
||||
ATT_NOTIFICATION,
|
||||
att_notify_mult },
|
||||
#endif /* CONFIG_BT_GATT_CLIENT */
|
||||
};
|
||||
|
||||
|
|
|
@ -404,12 +404,14 @@ enum {
|
|||
|
||||
#define CF_BIT_ROBUST_CACHING 0
|
||||
#define CF_BIT_EATT 1
|
||||
#define CF_BIT_LAST CF_BIT_EATT
|
||||
#define CF_BIT_NOTIFY_MULTI 2
|
||||
#define CF_BIT_LAST CF_BIT_NOTIFY_MULTI
|
||||
|
||||
#define CF_BYTE_LAST (CF_BIT_LAST % 8)
|
||||
|
||||
#define CF_ROBUST_CACHING(_cfg) (_cfg->data[0] & BIT(CF_BIT_ROBUST_CACHING))
|
||||
#define CF_EATT(_cfg) (_cfg->data[0] & BIT(CF_BIT_EATT))
|
||||
#define CF_NOTIFY_MULTI(_cfg) (_cfg->data[0] & BIT(CF_BIT_NOTIFY_MULTI))
|
||||
|
||||
struct gatt_cf_cfg {
|
||||
u8_t id;
|
||||
|
@ -490,7 +492,7 @@ static bool cf_set_value(struct gatt_cf_cfg *cfg, const u8_t *value, u16_t len)
|
|||
|
||||
/* Set the bits for each octect */
|
||||
for (i = 0U; i < len && i < last_byte; i++) {
|
||||
cfg->data[i] |= value[i] & ((1 << last_bit) - 1);
|
||||
cfg->data[i] |= value[i] & (BIT(last_bit + 1) - 1);
|
||||
BT_DBG("byte %u: data 0x%02x value 0x%02x", i, cfg->data[i],
|
||||
value[i]);
|
||||
}
|
||||
|
@ -1621,6 +1623,113 @@ struct notify_data {
|
|||
};
|
||||
};
|
||||
|
||||
#if defined(CONFIG_BT_GATT_NOTIFY_MULTIPLE)
|
||||
|
||||
struct nfy_mult_data {
|
||||
bt_gatt_complete_func_t func;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
#define nfy_mult_user_data(buf) \
|
||||
((struct nfy_mult_data *)net_buf_user_data(buf))
|
||||
#define nfy_mult_data_match(buf, _func, _user_data) \
|
||||
((nfy_mult_user_data(buf)->func == _func) && \
|
||||
(nfy_mult_user_data(buf)->user_data == _user_data))
|
||||
|
||||
static struct net_buf *nfy_mult[CONFIG_BT_MAX_CONN];
|
||||
|
||||
static int gatt_notify_mult_send(struct bt_conn *conn, struct net_buf **buf)
|
||||
{
|
||||
struct nfy_mult_data *data = nfy_mult_user_data(*buf);
|
||||
int ret;
|
||||
|
||||
ret = bt_att_send(conn, *buf, data->func, data->user_data);
|
||||
if (ret < 0) {
|
||||
net_buf_unref(*buf);
|
||||
}
|
||||
|
||||
*buf = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void notify_mult_process(struct k_work *work)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Send to any connection with an allocated buffer */
|
||||
for (i = 0; i < ARRAY_SIZE(nfy_mult); i++) {
|
||||
struct net_buf **buf = &nfy_mult[i];
|
||||
|
||||
if (*buf) {
|
||||
struct bt_conn *conn = bt_conn_lookup_index(i);
|
||||
|
||||
gatt_notify_mult_send(conn, buf);
|
||||
bt_conn_unref(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(nfy_mult_work, notify_mult_process);
|
||||
|
||||
static bool gatt_cf_notify_multi(struct bt_conn *conn)
|
||||
{
|
||||
struct gatt_cf_cfg *cfg;
|
||||
|
||||
cfg = find_cf_cfg(conn);
|
||||
if (!cfg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CF_NOTIFY_MULTI(cfg);
|
||||
}
|
||||
|
||||
static int gatt_notify_mult(struct bt_conn *conn, u16_t handle,
|
||||
struct bt_gatt_notify_params *params)
|
||||
{
|
||||
struct net_buf **buf = &nfy_mult[bt_conn_index(conn)];
|
||||
struct bt_att_notify_mult *nfy;
|
||||
|
||||
/* Check if we can fit more data into it, in case it doesn't fit send
|
||||
* the existing buffer and proceed to create a new one
|
||||
*/
|
||||
if (*buf && ((net_buf_tailroom(*buf) < sizeof(*nfy) + params->len) ||
|
||||
!nfy_mult_data_match(*buf, params->func, params->user_data))) {
|
||||
int ret;
|
||||
|
||||
ret = gatt_notify_mult_send(conn, buf);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*buf) {
|
||||
*buf = bt_att_create_pdu(conn, BT_ATT_OP_NOTIFY_MULT,
|
||||
sizeof(*nfy) + params->len);
|
||||
if (!*buf) {
|
||||
BT_WARN("No buffer available to send notification");
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Set user_data so it can be restored when sending */
|
||||
nfy_mult_user_data(*buf)->func = params->func;
|
||||
nfy_mult_user_data(*buf)->user_data = params->user_data;
|
||||
}
|
||||
|
||||
BT_DBG("handle 0x%04x len %u", handle, params->len);
|
||||
|
||||
nfy = net_buf_add(*buf, sizeof(*nfy));
|
||||
nfy->handle = sys_cpu_to_le16(handle);
|
||||
nfy->len = sys_cpu_to_le16(params->len);
|
||||
|
||||
net_buf_add(*buf, params->len);
|
||||
memcpy(nfy->value, params->data, params->len);
|
||||
|
||||
k_work_submit(&nfy_mult_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
|
||||
|
||||
static int gatt_notify(struct bt_conn *conn, u16_t handle,
|
||||
struct bt_gatt_notify_params *params)
|
||||
{
|
||||
|
@ -1638,6 +1747,12 @@ static int gatt_notify(struct bt_conn *conn, u16_t handle,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BT_GATT_NOTIFY_MULTIPLE)
|
||||
if (gatt_cf_notify_multi(conn)) {
|
||||
return gatt_notify_mult(conn, handle, params);
|
||||
}
|
||||
#endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
|
||||
|
||||
buf = bt_att_create_pdu(conn, BT_ATT_OP_NOTIFY,
|
||||
sizeof(*nfy) + params->len);
|
||||
if (!buf) {
|
||||
|
@ -1903,6 +2018,27 @@ int bt_gatt_notify_cb(struct bt_conn *conn,
|
|||
return data.err;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_GATT_NOTIFY_MULTIPLE)
|
||||
int bt_gatt_notify_multiple(struct bt_conn *conn, u16_t num_params,
|
||||
struct bt_gatt_notify_params *params)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
__ASSERT(params, "invalid parameters\n");
|
||||
__ASSERT(num_params, "invalid parameters\n");
|
||||
__ASSERT(params->attr, "invalid parameters\n");
|
||||
|
||||
for (i = 0; i < num_params; i++) {
|
||||
ret = bt_gatt_notify_cb(conn, ¶ms[i]);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
|
||||
|
||||
int bt_gatt_indicate(struct bt_conn *conn,
|
||||
struct bt_gatt_indicate_params *params)
|
||||
{
|
||||
|
@ -2343,6 +2479,55 @@ void bt_gatt_notification(struct bt_conn *conn, u16_t handle,
|
|||
}
|
||||
}
|
||||
|
||||
void bt_gatt_mult_notification(struct bt_conn *conn, const void *data,
|
||||
u16_t length)
|
||||
{
|
||||
struct bt_gatt_subscribe_params *params, *tmp;
|
||||
const struct bt_att_notify_mult *nfy;
|
||||
struct net_buf_simple buf;
|
||||
struct gatt_sub *sub;
|
||||
|
||||
BT_DBG("length %u", length);
|
||||
|
||||
sub = gatt_sub_find(conn);
|
||||
if (!sub) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* This is fine since there no write operation to the buffer. */
|
||||
net_buf_simple_init_with_data(&buf, (void *)data, length);
|
||||
|
||||
while (buf.len > sizeof(*nfy)) {
|
||||
u16_t handle;
|
||||
u16_t len;
|
||||
|
||||
nfy = net_buf_simple_pull_mem(&buf, sizeof(*nfy));
|
||||
handle = sys_cpu_to_le16(nfy->handle);
|
||||
len = sys_cpu_to_le16(nfy->len);
|
||||
|
||||
BT_DBG("handle 0x%02x len %u", handle, len);
|
||||
|
||||
if (len > buf.len) {
|
||||
BT_ERR("Invalid data len %u > %u", len, length);
|
||||
return;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sub->list, params, tmp,
|
||||
node) {
|
||||
if (handle != params->value_handle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (params->notify(conn, params, nfy->value, len) ==
|
||||
BT_GATT_ITER_STOP) {
|
||||
bt_gatt_unsubscribe(conn, params);
|
||||
}
|
||||
}
|
||||
|
||||
net_buf_simple_pull_mem(&buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void gatt_sub_update(struct bt_conn *conn, struct gatt_sub *sub)
|
||||
{
|
||||
if (sub->peer.type == BT_ADDR_LE_PUBLIC) {
|
||||
|
@ -3333,12 +3518,83 @@ static int gatt_read_mult(struct bt_conn *conn,
|
|||
|
||||
return gatt_send(conn, buf, gatt_read_mult_rsp, params, NULL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_EATT)
|
||||
static void gatt_read_mult_vl_rsp(struct bt_conn *conn, u8_t err,
|
||||
const void *pdu, u16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_read_params *params = user_data;
|
||||
const struct bt_att_read_mult_vl_rsp *rsp;
|
||||
struct net_buf_simple buf;
|
||||
|
||||
BT_DBG("err 0x%02x", err);
|
||||
|
||||
if (err || !length) {
|
||||
if (err == BT_ATT_ERR_NOT_SUPPORTED) {
|
||||
gatt_read_mult(conn, params);
|
||||
}
|
||||
params->func(conn, err, params, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
net_buf_simple_init_with_data(&buf, (void *)pdu, length);
|
||||
|
||||
while (buf.len >= sizeof(*rsp)) {
|
||||
u16_t len;
|
||||
|
||||
rsp = net_buf_simple_pull_mem(&buf, sizeof(*rsp));
|
||||
len = sys_le16_to_cpu(rsp->len);
|
||||
|
||||
/* If a Length Value Tuple is truncated, then the amount of
|
||||
* Attribute Value will be less than the value of the Value
|
||||
* Length field.
|
||||
*/
|
||||
if (len > buf.len) {
|
||||
len = buf.len;
|
||||
}
|
||||
|
||||
params->func(conn, 0, params, rsp->value, len);
|
||||
|
||||
net_buf_simple_pull_mem(&buf, len);
|
||||
}
|
||||
|
||||
/* mark read as complete since read multiple is single response */
|
||||
params->func(conn, 0, params, NULL, 0);
|
||||
}
|
||||
|
||||
static int gatt_read_mult_vl(struct bt_conn *conn,
|
||||
struct bt_gatt_read_params *params)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
u8_t i;
|
||||
|
||||
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_VL_REQ,
|
||||
params->handle_count * sizeof(u16_t));
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0U; i < params->handle_count; i++) {
|
||||
net_buf_add_le16(buf, params->handles[i]);
|
||||
}
|
||||
|
||||
return gatt_send(conn, buf, gatt_read_mult_vl_rsp, params, NULL);
|
||||
}
|
||||
#endif /* CONFIG_BT_EATT */
|
||||
|
||||
#else
|
||||
static int gatt_read_mult(struct bt_conn *conn,
|
||||
struct bt_gatt_read_params *params)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int gatt_read_mult_vl(struct bt_conn *conn,
|
||||
struct bt_gatt_read_params *params)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
||||
|
||||
int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
|
||||
|
@ -3358,7 +3614,11 @@ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
|
|||
}
|
||||
|
||||
if (params->handle_count > 1) {
|
||||
#if defined(CONFIG_BT_EATT)
|
||||
return gatt_read_mult_vl(conn, params);
|
||||
#else
|
||||
return gatt_read_mult(conn, params);
|
||||
#endif /* CONFIG_BT_EATT */
|
||||
}
|
||||
|
||||
if (params->single.offset) {
|
||||
|
|
|
@ -36,11 +36,19 @@ int bt_gatt_clear(u8_t id, const bt_addr_le_t *addr);
|
|||
#if defined(CONFIG_BT_GATT_CLIENT)
|
||||
void bt_gatt_notification(struct bt_conn *conn, u16_t handle,
|
||||
const void *data, u16_t length);
|
||||
|
||||
void bt_gatt_mult_notification(struct bt_conn *conn, const void *data,
|
||||
u16_t length);
|
||||
#else
|
||||
static inline void bt_gatt_notification(struct bt_conn *conn, u16_t handle,
|
||||
const void *data, u16_t length)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bt_gatt_mult_notification(struct bt_conn *conn,
|
||||
const void *data, u16_t length)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_BT_GATT_CLIENT */
|
||||
|
||||
struct bt_gatt_attr;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue