Bluetooth: GATT: Add bt_gatt_indicate

This uses ATT Indicate Value command to indicate changes and wait for
confirmation response.

Change-Id: I123a00e374929d779f96a02440215c32c0e79423
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2016-03-03 12:15:05 +02:00 committed by Gerrit Code Review
commit 68ee16d85c
4 changed files with 130 additions and 25 deletions

View file

@ -376,6 +376,12 @@ int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr,
return 0; return 0;
} }
int bt_gatt_indicate(struct bt_conn *conn,
struct bt_gatt_indicate_params *params)
{
return -ENOSYS;
}
int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func) int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func)
{ {
return -ENOSYS; return -ENOSYS;

View file

@ -731,6 +731,45 @@ ssize_t bt_gatt_attr_read_cpf(struct bt_conn *conn,
int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr, int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *data, uint16_t len); const void *data, uint16_t len);
/** @brief Indication complete result callback.
*
* @param conn Connection object.
* @param attr Attribute object.
* @param err: 0 success, error in the other case
*/
typedef void (*bt_gatt_indicate_func_t)(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
int err);
/** @brief GATT Indicate Value parameters */
struct bt_gatt_indicate_params {
/** Indicate Attribute object*/
const struct bt_gatt_attr *attr;
/** Indicate Value callback */
bt_gatt_indicate_func_t func;
/** Indicate Value data*/
const void *data;
/** Indicate Value length*/
uint16_t len;
};
/** @brief Indicate attribute value change.
*
* Send an indication of attribute value change.
* Note: This function should only be called if CCC is declared with
* BT_GATT_CCC otherwise it cannot find a valid peer configuration.
*
* Note: This procedure is asynchronous therefore the parameters need to
* remains valid while it is active.
*
* @param conn Connection object.
* @param params Indicate parameters.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_gatt_indicate(struct bt_conn *conn,
struct bt_gatt_indicate_params *params);
#if defined(CONFIG_BLUETOOTH_GATT_CLIENT) #if defined(CONFIG_BLUETOOTH_GATT_CLIENT)
/* Client API */ /* Client API */

View file

@ -1735,7 +1735,6 @@ void bt_att_init(void)
bt_l2cap_fixed_chan_register(&chan); bt_l2cap_fixed_chan_register(&chan);
} }
#if defined(CONFIG_BLUETOOTH_GATT_CLIENT)
uint16_t bt_att_get_mtu(struct bt_conn *conn) uint16_t bt_att_get_mtu(struct bt_conn *conn)
{ {
struct bt_att *att; struct bt_att *att;
@ -1810,4 +1809,3 @@ void bt_att_cancel(struct bt_conn *conn)
att_req_destroy(&att->req); att_req_destroy(&att->req);
} }
#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */

View file

@ -401,9 +401,11 @@ ssize_t bt_gatt_attr_read_cpf(struct bt_conn *conn,
} }
struct notify_data { struct notify_data {
uint16_t type;
const struct bt_gatt_attr *attr;
const void *data; const void *data;
size_t len; uint16_t len;
uint16_t handle; struct bt_gatt_indicate_params *params;
}; };
static int att_notify(struct bt_conn *conn, uint16_t handle, const void *data, static int att_notify(struct bt_conn *conn, uint16_t handle, const void *data,
@ -431,6 +433,53 @@ static int att_notify(struct bt_conn *conn, uint16_t handle, const void *data,
return 0; return 0;
} }
static void gatt_indicate_rsp(struct bt_conn *conn, uint8_t err,
const void *pdu, uint16_t length, void *user_data)
{
struct bt_gatt_indicate_params *params = user_data;
params->func(conn, params->attr, err);
}
static int gatt_send(struct bt_conn *conn, struct net_buf *buf,
bt_att_func_t func, void *user_data,
bt_att_destroy_t destroy)
{
int err;
err = bt_att_send(conn, buf, func, user_data, destroy);
if (err) {
BT_ERR("Error sending ATT PDU: %d", err);
net_buf_unref(buf);
}
return err;
}
static int att_indicate(struct bt_conn *conn,
struct bt_gatt_indicate_params *params)
{
struct net_buf *buf;
struct bt_att_indicate *ind;
buf = bt_att_create_pdu(conn, BT_ATT_OP_INDICATE,
sizeof(*ind) + params->len);
if (!buf) {
BT_WARN("No buffer available to send indication");
return -ENOMEM;
}
BT_DBG("conn %p handle 0x%04x", conn, params->attr->handle);
ind = net_buf_add(buf, sizeof(*ind));
ind->handle = sys_cpu_to_le16(params->attr->handle);
net_buf_add(buf, params->len);
memcpy(ind->value, params->data, params->len);
return gatt_send(conn, buf, gatt_indicate_rsp, params, NULL);
}
static uint8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data) static uint8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data)
{ {
struct notify_data *data = user_data; struct notify_data *data = user_data;
@ -455,9 +504,9 @@ static uint8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data)
/* Notify all peers configured */ /* Notify all peers configured */
for (i = 0; i < ccc->cfg_len; i++) { for (i = 0; i < ccc->cfg_len; i++) {
struct bt_conn *conn; struct bt_conn *conn;
int err;
/* TODO: Handle indications */ if (ccc->value != data->type) {
if (ccc->value != BT_GATT_CCC_NOTIFY) {
continue; continue;
} }
@ -466,12 +515,18 @@ static uint8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data)
continue; continue;
} }
if (att_notify(conn, data->handle, data->data, data->len) < 0) { if (data->type == BT_GATT_CCC_INDICATE) {
bt_conn_unref(conn); err = att_indicate(conn, data->params);
return BT_GATT_ITER_STOP; } else {
err = att_notify(conn, data->attr->handle, data->data,
data->len);
} }
bt_conn_unref(conn); bt_conn_unref(conn);
if (err < 0) {
return BT_GATT_ITER_STOP;
}
} }
return BT_GATT_ITER_CONTINUE; return BT_GATT_ITER_CONTINUE;
@ -490,7 +545,8 @@ int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr,
return att_notify(conn, attr->handle, data, len); return att_notify(conn, attr->handle, data, len);
} }
nfy.handle = attr->handle; nfy.attr = attr;
nfy.type = BT_GATT_CCC_NOTIFY;
nfy.data = data; nfy.data = data;
nfy.len = len; nfy.len = len;
@ -499,6 +555,27 @@ int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr,
return 0; return 0;
} }
int bt_gatt_indicate(struct bt_conn *conn,
struct bt_gatt_indicate_params *params)
{
struct notify_data nfy;
if (!params || !params->attr || !params->attr->handle) {
return -EINVAL;
}
if (conn) {
return att_indicate(conn, params);
}
nfy.type = BT_GATT_CCC_INDICATE;
nfy.params = params;
bt_gatt_foreach_attr(params->attr->handle, 0xffff, notify_cb, &nfy);
return 0;
}
static uint8_t connected_cb(const struct bt_gatt_attr *attr, void *user_data) static uint8_t connected_cb(const struct bt_gatt_attr *attr, void *user_data)
{ {
struct bt_conn *conn = user_data; struct bt_conn *conn = user_data;
@ -645,21 +722,6 @@ static void gatt_mtu_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
func(conn, err); func(conn, err);
} }
static int gatt_send(struct bt_conn *conn, struct net_buf *buf,
bt_att_func_t func, void *user_data,
bt_att_destroy_t destroy)
{
int err;
err = bt_att_send(conn, buf, func, user_data, destroy);
if (err) {
BT_ERR("Error sending ATT PDU: %d", err);
net_buf_unref(buf);
}
return err;
}
int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func) int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func)
{ {
struct bt_att_exchange_mtu_req *req; struct bt_att_exchange_mtu_req *req;