2015-04-28 09:44:37 +02:00
|
|
|
/* att.c - Attribute protocol handling */
|
|
|
|
|
|
|
|
/*
|
2016-06-10 11:10:18 +02:00
|
|
|
* Copyright (c) 2015-2016 Intel Corporation
|
2015-04-28 09:44:37 +02:00
|
|
|
*
|
2017-01-19 02:01:01 +01:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2015-04-28 09:44:37 +02:00
|
|
|
*/
|
|
|
|
|
2016-10-21 12:04:56 +02:00
|
|
|
#include <zephyr.h>
|
2015-04-28 09:44:37 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
2015-05-06 14:58:46 +02:00
|
|
|
#include <stdbool.h>
|
2019-06-25 18:25:32 +02:00
|
|
|
#include <sys/atomic.h>
|
2019-06-26 16:33:41 +02:00
|
|
|
#include <sys/byteorder.h>
|
2019-06-26 16:33:55 +02:00
|
|
|
#include <sys/util.h>
|
2015-04-28 09:44:37 +02:00
|
|
|
|
|
|
|
#include <bluetooth/hci.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
2015-05-18 09:57:05 +02:00
|
|
|
#include <bluetooth/uuid.h>
|
2015-05-12 15:03:09 +02:00
|
|
|
#include <bluetooth/gatt.h>
|
2020-01-16 17:21:12 +01:00
|
|
|
#include <drivers/bluetooth/hci_driver.h>
|
2015-04-28 09:44:37 +02:00
|
|
|
|
2017-08-09 08:21:11 +02:00
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_ATT)
|
2018-07-17 09:35:52 +02:00
|
|
|
#define LOG_MODULE_NAME bt_att
|
2017-05-10 16:27:16 +02:00
|
|
|
#include "common/log.h"
|
|
|
|
|
2015-04-28 09:44:37 +02:00
|
|
|
#include "hci_core.h"
|
2015-06-15 10:05:35 +02:00
|
|
|
#include "conn_internal.h"
|
2015-10-05 12:53:03 +02:00
|
|
|
#include "l2cap_internal.h"
|
2015-07-31 10:23:18 +02:00
|
|
|
#include "smp.h"
|
2016-02-17 10:18:10 +01:00
|
|
|
#include "att_internal.h"
|
2015-09-14 17:48:26 +02:00
|
|
|
#include "gatt_internal.h"
|
2015-04-28 09:44:37 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
#define ATT_CHAN(_ch) CONTAINER_OF(_ch, struct bt_att_chan, chan.chan)
|
2016-08-02 15:23:26 +02:00
|
|
|
#define ATT_REQ(_node) CONTAINER_OF(_node, struct bt_att_req, node)
|
2016-06-24 12:00:21 +02:00
|
|
|
|
2017-04-28 12:23:34 +02:00
|
|
|
#define ATT_CMD_MASK 0x40
|
2015-06-01 17:17:46 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
#if defined(CONFIG_BT_EATT)
|
|
|
|
#define ATT_CHAN_MAX (CONFIG_BT_EATT_MAX + 1)
|
|
|
|
#else
|
|
|
|
#define ATT_CHAN_MAX 1
|
|
|
|
#endif /* CONFIG_BT_EATT */
|
|
|
|
|
2017-03-19 11:12:06 +01:00
|
|
|
typedef enum __packed {
|
|
|
|
ATT_COMMAND,
|
|
|
|
ATT_REQUEST,
|
|
|
|
ATT_RESPONSE,
|
|
|
|
ATT_NOTIFICATION,
|
|
|
|
ATT_CONFIRMATION,
|
|
|
|
ATT_INDICATION,
|
|
|
|
ATT_UNKNOWN,
|
|
|
|
} att_type_t;
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static att_type_t att_op_get_type(uint8_t op);
|
2017-03-19 11:12:06 +01:00
|
|
|
|
2017-08-09 08:21:11 +02:00
|
|
|
#if CONFIG_BT_ATT_PREPARE_COUNT > 0
|
2016-06-01 11:05:37 +02:00
|
|
|
struct bt_attr_data {
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
|
|
|
uint16_t offset;
|
2016-06-01 11:05:37 +02:00
|
|
|
};
|
|
|
|
|
2017-01-07 16:05:58 +01:00
|
|
|
/* Pool for incoming ATT packets */
|
2017-08-09 08:21:11 +02:00
|
|
|
NET_BUF_POOL_DEFINE(prep_pool, CONFIG_BT_ATT_PREPARE_COUNT, BT_ATT_MTU,
|
2017-01-07 16:05:58 +01:00
|
|
|
sizeof(struct bt_attr_data), NULL);
|
2017-08-09 08:21:11 +02:00
|
|
|
#endif /* CONFIG_BT_ATT_PREPARE_COUNT */
|
2016-06-01 11:05:37 +02:00
|
|
|
|
2019-12-30 19:45:35 +01:00
|
|
|
K_MEM_SLAB_DEFINE(req_slab, sizeof(struct bt_att_req),
|
|
|
|
CONFIG_BT_ATT_TX_MAX, 16);
|
|
|
|
|
2017-03-19 11:12:06 +01:00
|
|
|
enum {
|
|
|
|
ATT_PENDING_RSP,
|
|
|
|
ATT_PENDING_CFM,
|
2017-03-30 17:45:01 +02:00
|
|
|
ATT_DISCONNECTED,
|
2020-02-27 02:14:20 +01:00
|
|
|
ATT_ENHANCED,
|
2020-06-09 02:33:14 +02:00
|
|
|
ATT_PENDING_SENT,
|
2017-03-19 11:12:06 +01:00
|
|
|
|
|
|
|
/* Total number of flags - must be at the end of the enum */
|
|
|
|
ATT_NUM_FLAGS,
|
|
|
|
};
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
/* ATT channel specific data */
|
|
|
|
struct bt_att_chan {
|
|
|
|
/* Connection this channel is associated with */
|
|
|
|
struct bt_att *att;
|
2016-05-26 14:03:04 +02:00
|
|
|
struct bt_l2cap_le_chan chan;
|
2017-03-19 11:12:06 +01:00
|
|
|
ATOMIC_DEFINE(flags, ATT_NUM_FLAGS);
|
2016-08-02 15:23:26 +02:00
|
|
|
struct bt_att_req *req;
|
2020-06-09 02:33:14 +02:00
|
|
|
struct k_fifo tx_queue;
|
2016-11-10 10:26:20 +01:00
|
|
|
struct k_delayed_work timeout_work;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct k_sem tx_sem;
|
|
|
|
void (*sent)(struct bt_att_chan *chan);
|
|
|
|
sys_snode_t node;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ATT connection specific data */
|
|
|
|
struct bt_att {
|
|
|
|
struct bt_conn *conn;
|
|
|
|
/* Shared request queue */
|
|
|
|
sys_slist_t reqs;
|
2019-06-13 20:26:36 +02:00
|
|
|
struct k_fifo tx_queue;
|
2017-08-09 08:21:11 +02:00
|
|
|
#if CONFIG_BT_ATT_PREPARE_COUNT > 0
|
2016-11-10 10:45:10 +01:00
|
|
|
struct k_fifo prep_queue;
|
2016-06-01 11:05:37 +02:00
|
|
|
#endif
|
2020-02-27 02:14:20 +01:00
|
|
|
/* Contains bt_att_chan instance(s) */
|
|
|
|
sys_slist_t chans;
|
2015-05-21 19:43:42 +02:00
|
|
|
};
|
|
|
|
|
2020-01-09 18:23:53 +01:00
|
|
|
K_MEM_SLAB_DEFINE(att_slab, sizeof(struct bt_att),
|
|
|
|
CONFIG_BT_MAX_CONN, 16);
|
2020-02-27 02:14:20 +01:00
|
|
|
K_MEM_SLAB_DEFINE(chan_slab, sizeof(struct bt_att_chan),
|
|
|
|
CONFIG_BT_MAX_CONN * ATT_CHAN_MAX, 16);
|
2019-07-16 17:13:03 +02:00
|
|
|
static struct bt_att_req cancel;
|
2016-08-02 15:23:26 +02:00
|
|
|
|
2015-06-30 15:22:17 +02:00
|
|
|
static void att_req_destroy(struct bt_att_req *req)
|
2015-06-29 13:40:40 +02:00
|
|
|
{
|
2017-05-29 19:32:57 +02:00
|
|
|
BT_DBG("req %p", req);
|
|
|
|
|
2015-09-04 13:29:57 +02:00
|
|
|
if (req->buf) {
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_unref(req->buf);
|
2015-09-04 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
2015-06-30 15:22:17 +02:00
|
|
|
if (req->destroy) {
|
2016-08-02 15:23:26 +02:00
|
|
|
req->destroy(req);
|
2015-06-29 13:40:40 +02:00
|
|
|
}
|
|
|
|
|
2019-12-30 19:45:35 +01:00
|
|
|
bt_att_req_free(req);
|
2015-06-29 13:40:40 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
typedef void (*bt_att_chan_sent_t)(struct bt_att_chan *chan);
|
|
|
|
|
|
|
|
static bt_att_chan_sent_t chan_cb(struct net_buf *buf);
|
2020-06-17 18:59:05 +02:00
|
|
|
static bt_conn_tx_cb_t att_cb(bt_att_chan_sent_t cb);
|
2020-02-27 02:14:20 +01:00
|
|
|
|
|
|
|
void att_sent(struct bt_conn *conn, void *user_data)
|
2017-03-19 11:12:06 +01:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_l2cap_chan *chan = user_data;
|
2017-03-19 11:12:06 +01:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("conn %p chan %p", conn, chan);
|
2017-03-19 11:12:06 +01:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->ops->sent) {
|
|
|
|
chan->ops->sent(chan);
|
|
|
|
}
|
2017-03-19 11:12:06 +01:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static int chan_send(struct bt_att_chan *chan, struct net_buf *buf,
|
|
|
|
bt_att_chan_sent_t cb)
|
2019-06-13 20:26:36 +02:00
|
|
|
{
|
|
|
|
struct bt_att_hdr *hdr;
|
|
|
|
|
|
|
|
hdr = (void *)buf->data;
|
|
|
|
|
|
|
|
BT_DBG("code 0x%02x", hdr->code);
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (IS_ENABLED(CONFIG_BT_EATT) &&
|
|
|
|
atomic_test_bit(chan->flags, ATT_ENHANCED)) {
|
2020-06-17 18:59:05 +02:00
|
|
|
/* Check if sent is pending already, if it does it cannot be
|
|
|
|
* modified so the operation will need to be queued.
|
|
|
|
*/
|
|
|
|
if (atomic_test_and_set_bit(chan->flags, ATT_PENDING_SENT)) {
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->sent = cb ? cb : chan_cb(buf);
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (hdr->code == BT_ATT_OP_SIGNED_WRITE_CMD) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the channel is ready to send in case of a request */
|
|
|
|
if (att_op_get_type(hdr->code) == ATT_REQUEST &&
|
|
|
|
!atomic_test_bit(chan->chan.chan.status,
|
|
|
|
BT_L2CAP_STATUS_OUT)) {
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bt_l2cap_chan_send(&chan->chan.chan, buf);
|
|
|
|
}
|
|
|
|
|
2019-06-13 20:26:36 +02:00
|
|
|
if (hdr->code == BT_ATT_OP_SIGNED_WRITE_CMD) {
|
|
|
|
int err;
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
err = bt_smp_sign(chan->att->conn, buf);
|
2019-06-13 20:26:36 +02:00
|
|
|
if (err) {
|
|
|
|
BT_ERR("Error signing data");
|
2019-12-02 14:46:00 +01:00
|
|
|
net_buf_unref(buf);
|
2019-06-13 20:26:36 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-17 18:59:05 +02:00
|
|
|
chan->sent = cb ? cb : chan_cb(buf);
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return bt_l2cap_send_cb(chan->att->conn, BT_L2CAP_CID_ATT, buf,
|
2020-06-17 18:59:05 +02:00
|
|
|
att_cb(chan->sent), &chan->chan.chan);
|
2019-06-13 20:26:36 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
static int process_queue(struct bt_att_chan *chan, struct k_fifo *queue)
|
2019-06-13 20:26:36 +02:00
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
2020-06-09 02:33:14 +02:00
|
|
|
int err;
|
2019-06-13 20:26:36 +02:00
|
|
|
|
2020-06-09 20:29:46 +02:00
|
|
|
buf = net_buf_get(queue, K_NO_WAIT);
|
|
|
|
if (buf) {
|
2020-06-09 02:33:14 +02:00
|
|
|
err = chan_send(chan, buf, NULL);
|
|
|
|
if (err) {
|
2020-02-27 02:14:20 +01:00
|
|
|
/* Push it back if it could not be send */
|
2020-06-09 02:33:14 +02:00
|
|
|
k_queue_prepend(&queue->_queue, buf);
|
|
|
|
return err;
|
2019-06-13 20:26:36 +02:00
|
|
|
}
|
2020-02-27 02:14:20 +01:00
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2020-06-09 20:29:46 +02:00
|
|
|
/* Send requests without taking tx_sem */
|
|
|
|
static int chan_req_send(struct bt_att_chan *chan, struct bt_att_req *req)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (chan->chan.tx.mtu < net_buf_frags_len(req->buf)) {
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("chan %p req %p len %zu", chan, req,
|
|
|
|
net_buf_frags_len(req->buf));
|
|
|
|
|
|
|
|
chan->req = req;
|
|
|
|
|
|
|
|
/* Save request state so it can be resent */
|
|
|
|
net_buf_simple_save(&req->buf->b, &req->state);
|
|
|
|
|
|
|
|
/* Keep a reference for resending in case of an error */
|
|
|
|
err = chan_send(chan, net_buf_ref(req->buf), NULL);
|
|
|
|
if (err < 0) {
|
|
|
|
net_buf_unref(req->buf);
|
|
|
|
chan->req = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
static void bt_att_sent(struct bt_l2cap_chan *ch)
|
|
|
|
{
|
|
|
|
struct bt_att_chan *chan = ATT_CHAN(ch);
|
|
|
|
struct bt_att *att = chan->att;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
if (chan->sent) {
|
|
|
|
chan->sent(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_clear_bit(chan->flags, ATT_PENDING_SENT);
|
|
|
|
|
2020-06-09 20:29:46 +02:00
|
|
|
/* Process pending requests first since they require a response they
|
|
|
|
* can only be processed one at time while if other queues were
|
|
|
|
* processed before they may always contain a buffer starving the
|
|
|
|
* request queue.
|
|
|
|
*/
|
|
|
|
if (!chan->req && !sys_slist_is_empty(&att->reqs)) {
|
|
|
|
sys_snode_t *node = sys_slist_get(&att->reqs);
|
|
|
|
|
|
|
|
if (chan_req_send(chan, ATT_REQ(node)) >= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepend back to the list as it could not be sent */
|
|
|
|
sys_slist_prepend(&att->reqs, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process channel queue */
|
2020-06-09 02:33:14 +02:00
|
|
|
err = process_queue(chan, &chan->tx_queue);
|
|
|
|
if (!err) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process global queue */
|
|
|
|
err = process_queue(chan, &att->tx_queue);
|
|
|
|
if (!err) {
|
2020-02-27 02:14:20 +01:00
|
|
|
return;
|
2019-06-13 20:26:36 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
k_sem_give(&chan->tx_sem);
|
2019-06-13 20:26:36 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static void chan_cfm_sent(struct bt_att_chan *chan)
|
2017-03-19 11:12:06 +01:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("chan %p", chan);
|
2017-03-30 17:45:01 +02:00
|
|
|
|
2019-10-04 10:59:43 +02:00
|
|
|
if (IS_ENABLED(CONFIG_BT_ATT_ENFORCE_FLOW)) {
|
2020-02-27 02:14:20 +01:00
|
|
|
atomic_clear_bit(chan->flags, ATT_PENDING_CFM);
|
2019-10-04 10:59:43 +02:00
|
|
|
}
|
2017-03-19 11:12:06 +01:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static void chan_rsp_sent(struct bt_att_chan *chan)
|
2017-03-19 11:12:06 +01:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("chan %p", chan);
|
2017-03-30 17:45:01 +02:00
|
|
|
|
2019-10-04 10:59:43 +02:00
|
|
|
if (IS_ENABLED(CONFIG_BT_ATT_ENFORCE_FLOW)) {
|
2020-02-27 02:14:20 +01:00
|
|
|
atomic_clear_bit(chan->flags, ATT_PENDING_RSP);
|
2019-10-04 10:59:43 +02:00
|
|
|
}
|
2017-03-30 17:45:01 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static void chan_req_sent(struct bt_att_chan *chan)
|
2017-03-30 18:46:56 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("chan %p chan->req %p", chan, chan->req);
|
2017-03-30 18:46:56 +02:00
|
|
|
|
|
|
|
/* Start timeout work */
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->req) {
|
|
|
|
k_delayed_work_submit(&chan->timeout_work, BT_ATT_TIMEOUT);
|
2017-06-11 15:31:16 +02:00
|
|
|
}
|
2017-03-19 11:12:06 +01:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static bt_att_chan_sent_t chan_cb(struct net_buf *buf)
|
2017-03-19 11:12:06 +01:00
|
|
|
{
|
|
|
|
switch (att_op_get_type(buf->data[0])) {
|
|
|
|
case ATT_RESPONSE:
|
2020-02-27 02:14:20 +01:00
|
|
|
return chan_rsp_sent;
|
2017-03-19 11:12:06 +01:00
|
|
|
case ATT_CONFIRMATION:
|
2020-02-27 02:14:20 +01:00
|
|
|
return chan_cfm_sent;
|
2017-03-30 18:46:56 +02:00
|
|
|
case ATT_REQUEST:
|
|
|
|
case ATT_INDICATION:
|
2020-02-27 02:14:20 +01:00
|
|
|
return chan_req_sent;
|
2017-03-19 11:12:06 +01:00
|
|
|
default:
|
2020-02-27 02:14:20 +01:00
|
|
|
return NULL;
|
2017-03-19 11:12:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-17 18:59:05 +02:00
|
|
|
static void att_cfm_sent(struct bt_conn *conn, void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *ch = user_data;
|
|
|
|
struct bt_att_chan *chan = ATT_CHAN(ch);
|
|
|
|
|
|
|
|
BT_DBG("conn %p chan %p", conn, chan);
|
|
|
|
|
|
|
|
chan->sent = chan_cfm_sent;
|
|
|
|
|
|
|
|
att_sent(conn, user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void att_rsp_sent(struct bt_conn *conn, void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *ch = user_data;
|
|
|
|
struct bt_att_chan *chan = ATT_CHAN(ch);
|
|
|
|
|
|
|
|
BT_DBG("conn %p chan %p", conn, chan);
|
|
|
|
|
|
|
|
chan->sent = chan_rsp_sent;
|
|
|
|
|
|
|
|
att_sent(conn, user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void att_req_sent(struct bt_conn *conn, void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *ch = user_data;
|
|
|
|
struct bt_att_chan *chan = ATT_CHAN(ch);
|
|
|
|
|
|
|
|
BT_DBG("conn %p chan %p", conn, chan);
|
|
|
|
|
|
|
|
chan->sent = chan_req_sent;
|
|
|
|
|
|
|
|
att_sent(conn, user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bt_conn_tx_cb_t att_cb(bt_att_chan_sent_t cb)
|
|
|
|
{
|
|
|
|
if (cb == chan_rsp_sent) {
|
|
|
|
return att_rsp_sent;
|
|
|
|
} else if (cb == chan_cfm_sent) {
|
|
|
|
return att_cfm_sent;
|
|
|
|
} else if (cb == chan_req_sent) {
|
|
|
|
return att_req_sent;
|
|
|
|
} else {
|
|
|
|
return att_sent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
struct net_buf *bt_att_chan_create_pdu(struct bt_att_chan *chan, uint8_t op,
|
2020-02-27 02:14:20 +01:00
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
struct bt_att_hdr *hdr;
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
if (len + sizeof(op) > chan->chan.tx.mtu) {
|
|
|
|
BT_WARN("ATT MTU exceeded, max %u, wanted %zu",
|
|
|
|
chan->chan.tx.mtu, len + sizeof(op));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (att_op_get_type(op)) {
|
|
|
|
case ATT_RESPONSE:
|
|
|
|
case ATT_CONFIRMATION:
|
|
|
|
/* Use a timeout only when responding/confirming */
|
|
|
|
buf = bt_l2cap_create_pdu_timeout(NULL, 0, BT_ATT_TIMEOUT);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
buf = bt_l2cap_create_pdu(NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!buf) {
|
|
|
|
BT_ERR("Unable to allocate buffer for op 0x%02x", op);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = net_buf_add(buf, sizeof(*hdr));
|
|
|
|
hdr->code = op;
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool att_chan_is_connected(struct bt_att_chan *chan)
|
|
|
|
{
|
|
|
|
return (chan->att->conn->state != BT_CONN_CONNECTED ||
|
|
|
|
!atomic_test_bit(chan->flags, ATT_DISCONNECTED));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bt_att_chan_send(struct bt_att_chan *chan, struct net_buf *buf,
|
|
|
|
bt_att_chan_sent_t cb)
|
|
|
|
{
|
|
|
|
struct bt_att_hdr *hdr;
|
|
|
|
|
|
|
|
hdr = (void *)buf->data;
|
|
|
|
|
|
|
|
BT_DBG("chan %p flags %u code 0x%02x", chan, atomic_get(chan->flags),
|
|
|
|
hdr->code);
|
|
|
|
|
|
|
|
/* Don't use tx_sem if caller has set it own callback */
|
|
|
|
if (!cb) {
|
|
|
|
if (k_sem_take(&chan->tx_sem, K_NO_WAIT) < 0) {
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return chan_send(chan, buf, cb);
|
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
static void bt_att_chan_send_rsp(struct bt_att_chan *chan, struct net_buf *buf,
|
|
|
|
bt_att_chan_sent_t cb)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = bt_att_chan_send(chan, buf, cb);
|
|
|
|
if (err) {
|
|
|
|
/* Responses need to be sent back using the same channel */
|
2020-06-17 23:07:38 +02:00
|
|
|
net_buf_put(&chan->tx_queue, buf);
|
2020-06-09 02:33:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static void send_err_rsp(struct bt_att_chan *chan, uint8_t req, uint16_t handle,
|
|
|
|
uint8_t err)
|
2015-04-28 09:44:37 +02:00
|
|
|
{
|
|
|
|
struct bt_att_error_rsp *rsp;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *buf;
|
2015-04-28 09:44:37 +02:00
|
|
|
|
2015-05-26 09:15:27 +02:00
|
|
|
/* Ignore opcode 0x00 */
|
|
|
|
if (!req) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
buf = bt_att_chan_create_pdu(chan, BT_ATT_OP_ERROR_RSP, sizeof(*rsp));
|
2015-05-05 09:50:14 +02:00
|
|
|
if (!buf) {
|
2015-04-28 09:44:37 +02:00
|
|
|
return;
|
2015-05-05 09:50:14 +02:00
|
|
|
}
|
2015-04-28 09:44:37 +02:00
|
|
|
|
2015-10-28 09:48:34 +01:00
|
|
|
rsp = net_buf_add(buf, sizeof(*rsp));
|
2015-04-28 09:44:37 +02:00
|
|
|
rsp->request = req;
|
|
|
|
rsp->handle = sys_cpu_to_le16(handle);
|
|
|
|
rsp->error = err;
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, buf, chan_rsp_sent);
|
2015-04-28 09:44:37 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_mtu_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-04 16:22:10 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->att->conn;
|
2015-05-04 16:22:10 +02:00
|
|
|
struct bt_att_exchange_mtu_req *req;
|
|
|
|
struct bt_att_exchange_mtu_rsp *rsp;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *pdu;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t mtu_client, mtu_server;
|
2015-05-04 16:22:10 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
/* Exchange MTU sub-procedure shall only be supported on the
|
|
|
|
* LE Fixed Channel Unenhanced ATT bearer.
|
|
|
|
*/
|
|
|
|
if (atomic_test_bit(chan->flags, ATT_ENHANCED)) {
|
|
|
|
return BT_ATT_ERR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2015-08-18 14:48:26 +02:00
|
|
|
req = (void *)buf->data;
|
2015-05-04 16:22:10 +02:00
|
|
|
|
2015-10-14 16:41:02 +02:00
|
|
|
mtu_client = sys_le16_to_cpu(req->mtu);
|
2015-05-04 16:22:10 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("Client MTU %u", mtu_client);
|
2015-05-04 16:22:10 +02:00
|
|
|
|
2015-10-14 16:16:05 +02:00
|
|
|
/* Check if MTU is valid */
|
2015-10-14 16:41:02 +02:00
|
|
|
if (mtu_client < BT_ATT_DEFAULT_LE_MTU) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_INVALID_PDU;
|
2015-05-04 16:22:10 +02:00
|
|
|
}
|
|
|
|
|
2015-08-18 14:48:26 +02:00
|
|
|
pdu = bt_att_create_pdu(conn, BT_ATT_OP_MTU_RSP, sizeof(*rsp));
|
2015-08-26 10:34:13 +02:00
|
|
|
if (!pdu) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-04 16:22:10 +02:00
|
|
|
}
|
|
|
|
|
2017-01-07 16:05:58 +01:00
|
|
|
mtu_server = BT_ATT_MTU;
|
2015-05-04 16:22:10 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("Server MTU %u", mtu_server);
|
2015-05-04 16:22:10 +02:00
|
|
|
|
2015-10-28 09:48:34 +01:00
|
|
|
rsp = net_buf_add(pdu, sizeof(*rsp));
|
2015-10-14 16:41:02 +02:00
|
|
|
rsp->mtu = sys_cpu_to_le16(mtu_server);
|
2015-05-04 16:22:10 +02:00
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, pdu, chan_rsp_sent);
|
2015-06-09 10:19:17 +02:00
|
|
|
|
2015-10-02 15:21:18 +02:00
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 484:
|
|
|
|
*
|
|
|
|
* A device's Exchange MTU Request shall contain the same MTU as the
|
|
|
|
* device's Exchange MTU Response (i.e. the MTU shall be symmetric).
|
|
|
|
*/
|
2020-02-27 02:14:20 +01:00
|
|
|
chan->chan.rx.mtu = MIN(mtu_client, mtu_server);
|
|
|
|
chan->chan.tx.mtu = chan->chan.rx.mtu;
|
2015-10-14 16:41:02 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("Negotiated MTU %u", chan->chan.rx.mtu);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-04 16:22:10 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static int bt_att_chan_req_send(struct bt_att_chan *chan,
|
|
|
|
struct bt_att_req *req)
|
2016-08-02 15:23:26 +02:00
|
|
|
{
|
2019-12-02 14:46:00 +01:00
|
|
|
int err;
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
__ASSERT_NO_MSG(chan);
|
2018-11-16 13:54:42 +01:00
|
|
|
__ASSERT_NO_MSG(req);
|
|
|
|
__ASSERT_NO_MSG(req->func);
|
2020-02-27 02:14:20 +01:00
|
|
|
__ASSERT_NO_MSG(!chan->req);
|
2018-11-16 13:54:42 +01:00
|
|
|
|
2016-08-02 15:23:26 +02:00
|
|
|
BT_DBG("req %p", req);
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (k_sem_take(&chan->tx_sem, K_NO_WAIT) < 0) {
|
|
|
|
return -EAGAIN;
|
2017-03-30 17:45:01 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 20:29:46 +02:00
|
|
|
err = chan_req_send(chan, req);
|
2020-02-27 02:14:20 +01:00
|
|
|
if (err < 0) {
|
|
|
|
k_sem_give(&chan->tx_sem);
|
2019-12-02 14:46:00 +01:00
|
|
|
}
|
2016-08-02 15:23:26 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return err;
|
2016-08-02 15:23:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void att_process(struct bt_att *att)
|
|
|
|
{
|
|
|
|
sys_snode_t *node;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan, *tmp;
|
2016-08-02 15:23:26 +02:00
|
|
|
|
2016-11-02 15:07:54 +01:00
|
|
|
/* Pull next request from the list */
|
|
|
|
node = sys_slist_get(&att->reqs);
|
2016-08-02 15:23:26 +02:00
|
|
|
if (!node) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("req %p", ATT_REQ(node));
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&att->chans, chan, tmp, node) {
|
|
|
|
/* If there is nothing pending use the channel */
|
|
|
|
if (!chan->req) {
|
|
|
|
if (bt_att_chan_req_send(chan, ATT_REQ(node)) >= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepend back to the list as it could not be sent */
|
|
|
|
sys_slist_prepend(&att->reqs, node);
|
2016-08-02 15:23:26 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_rsp(struct bt_att_chan *chan, void *pdu, uint16_t len,
|
|
|
|
uint8_t err)
|
2015-07-01 09:35:08 +02:00
|
|
|
{
|
2020-01-24 12:22:06 +01:00
|
|
|
bt_att_func_t func = NULL;
|
2019-12-30 19:45:35 +01:00
|
|
|
void *params;
|
2015-07-01 09:35:08 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("chan %p err 0x%02x len %u: %s", chan, err, len,
|
|
|
|
bt_hex(pdu, len));
|
2017-05-29 18:28:50 +02:00
|
|
|
|
2017-05-29 19:32:57 +02:00
|
|
|
/* Cancel timeout if ongoing */
|
2020-02-27 02:14:20 +01:00
|
|
|
k_delayed_work_cancel(&chan->timeout_work);
|
2017-05-29 19:32:57 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (!chan->req) {
|
2017-05-29 18:28:50 +02:00
|
|
|
BT_WARN("No pending ATT request");
|
2016-08-02 15:23:26 +02:00
|
|
|
goto process;
|
2015-07-01 09:35:08 +02:00
|
|
|
}
|
|
|
|
|
2019-07-16 17:13:03 +02:00
|
|
|
/* Check if request has been cancelled */
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->req == &cancel) {
|
|
|
|
chan->req = NULL;
|
2019-07-16 17:13:03 +02:00
|
|
|
goto process;
|
|
|
|
}
|
|
|
|
|
2016-08-02 15:23:26 +02:00
|
|
|
/* Release original buffer */
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->req->buf) {
|
|
|
|
net_buf_unref(chan->req->buf);
|
|
|
|
chan->req->buf = NULL;
|
2015-11-09 12:23:22 +01:00
|
|
|
}
|
|
|
|
|
2016-08-02 15:23:26 +02:00
|
|
|
/* Reset func so it can be reused by the callback */
|
2020-02-27 02:14:20 +01:00
|
|
|
func = chan->req->func;
|
|
|
|
chan->req->func = NULL;
|
|
|
|
params = chan->req->user_data;
|
2015-07-01 09:35:08 +02:00
|
|
|
|
2019-12-30 19:45:35 +01:00
|
|
|
/* free allocated request so its memory can be reused */
|
2020-02-27 02:14:20 +01:00
|
|
|
att_req_destroy(chan->req);
|
|
|
|
chan->req = NULL;
|
2016-08-02 15:23:26 +02:00
|
|
|
|
|
|
|
process:
|
|
|
|
/* Process pending requests */
|
2020-02-27 02:14:20 +01:00
|
|
|
att_process(chan->att);
|
2020-01-24 12:22:06 +01:00
|
|
|
if (func) {
|
2020-02-27 02:14:20 +01:00
|
|
|
func(chan->att->conn, err, pdu, len, params);
|
2020-01-24 12:22:06 +01:00
|
|
|
}
|
2015-07-01 09:35:08 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-05 16:40:33 +01:00
|
|
|
#if defined(CONFIG_BT_GATT_CLIENT)
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_mtu_rsp(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-07-01 09:35:08 +02:00
|
|
|
{
|
|
|
|
struct bt_att_exchange_mtu_rsp *rsp;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t mtu;
|
2015-07-01 09:35:08 +02:00
|
|
|
|
|
|
|
rsp = (void *)buf->data;
|
|
|
|
|
|
|
|
mtu = sys_le16_to_cpu(rsp->mtu);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("Server MTU %u", mtu);
|
2015-07-01 09:35:08 +02:00
|
|
|
|
2015-10-14 16:16:05 +02:00
|
|
|
/* Check if MTU is valid */
|
|
|
|
if (mtu < BT_ATT_DEFAULT_LE_MTU) {
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, NULL, 0, BT_ATT_ERR_INVALID_PDU);
|
2015-07-01 09:35:08 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
chan->chan.rx.mtu = MIN(mtu, BT_ATT_MTU);
|
2015-10-14 17:55:15 +02:00
|
|
|
|
2015-10-02 15:21:18 +02:00
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 484:
|
|
|
|
*
|
|
|
|
* A device's Exchange MTU Request shall contain the same MTU as the
|
|
|
|
* device's Exchange MTU Response (i.e. the MTU shall be symmetric).
|
|
|
|
*/
|
2020-02-27 02:14:20 +01:00
|
|
|
chan->chan.tx.mtu = chan->chan.rx.mtu;
|
2015-10-02 15:21:18 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("Negotiated MTU %u", chan->chan.rx.mtu);
|
2015-10-02 15:21:18 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, rsp, buf->len, 0);
|
2015-07-01 09:35:08 +02:00
|
|
|
}
|
2018-11-05 16:40:33 +01:00
|
|
|
#endif /* CONFIG_BT_GATT_CLIENT */
|
2015-07-01 09:35:08 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static bool range_is_valid(uint16_t start, uint16_t end, uint16_t *err)
|
2015-05-06 14:58:46 +02:00
|
|
|
{
|
|
|
|
/* Handle 0 is invalid */
|
|
|
|
if (!start || !end) {
|
2015-05-14 09:29:56 +02:00
|
|
|
if (err) {
|
2018-11-29 20:23:03 +01:00
|
|
|
*err = 0U;
|
2015-05-14 09:29:56 +02:00
|
|
|
}
|
2015-05-06 14:58:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if range is valid */
|
|
|
|
if (start > end) {
|
2015-05-14 09:29:56 +02:00
|
|
|
if (err) {
|
2015-05-06 14:58:46 +02:00
|
|
|
*err = start;
|
2015-05-14 09:29:56 +02:00
|
|
|
}
|
2015-05-06 14:58:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2015-07-07 11:08:48 +02:00
|
|
|
|
2015-05-21 16:50:03 +02:00
|
|
|
struct find_info_data {
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *buf;
|
2015-05-21 16:50:03 +02:00
|
|
|
struct bt_att_find_info_rsp *rsp;
|
|
|
|
union {
|
|
|
|
struct bt_att_info_16 *info16;
|
|
|
|
struct bt_att_info_128 *info128;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t find_info_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-05-21 16:50:03 +02:00
|
|
|
{
|
|
|
|
struct find_info_data *data = user_data;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan = data->chan;
|
2015-05-21 16:50:03 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", attr->handle);
|
2015-05-21 16:50:03 +02:00
|
|
|
|
|
|
|
/* Initialize rsp at first entry */
|
|
|
|
if (!data->rsp) {
|
2015-10-28 09:48:34 +01:00
|
|
|
data->rsp = net_buf_add(data->buf, sizeof(*data->rsp));
|
2016-01-28 04:43:34 +01:00
|
|
|
data->rsp->format = (attr->uuid->type == BT_UUID_TYPE_16) ?
|
2015-05-21 16:50:03 +02:00
|
|
|
BT_ATT_INFO_16 : BT_ATT_INFO_128;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (data->rsp->format) {
|
|
|
|
case BT_ATT_INFO_16:
|
2016-01-28 04:43:34 +01:00
|
|
|
if (attr->uuid->type != BT_UUID_TYPE_16) {
|
2015-05-21 16:50:03 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2019-06-18 20:45:40 +02:00
|
|
|
/* Fast forward to next item position */
|
2015-10-28 09:48:34 +01:00
|
|
|
data->info16 = net_buf_add(data->buf, sizeof(*data->info16));
|
2015-05-21 16:50:03 +02:00
|
|
|
data->info16->handle = sys_cpu_to_le16(attr->handle);
|
2016-01-28 04:43:34 +01:00
|
|
|
data->info16->uuid = sys_cpu_to_le16(BT_UUID_16(attr->uuid)->val);
|
2015-05-21 16:50:03 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->chan.tx.mtu - data->buf->len >
|
2016-05-26 14:03:04 +02:00
|
|
|
sizeof(*data->info16)) {
|
2015-10-02 15:21:18 +02:00
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
2015-10-21 11:46:22 +02:00
|
|
|
|
|
|
|
break;
|
2015-05-21 16:50:03 +02:00
|
|
|
case BT_ATT_INFO_128:
|
2016-01-28 04:43:34 +01:00
|
|
|
if (attr->uuid->type != BT_UUID_TYPE_128) {
|
2015-05-21 16:50:03 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2019-06-18 20:45:40 +02:00
|
|
|
/* Fast forward to next item position */
|
2015-10-28 09:48:34 +01:00
|
|
|
data->info128 = net_buf_add(data->buf, sizeof(*data->info128));
|
2015-05-21 16:50:03 +02:00
|
|
|
data->info128->handle = sys_cpu_to_le16(attr->handle);
|
2016-01-28 04:43:34 +01:00
|
|
|
memcpy(data->info128->uuid, BT_UUID_128(attr->uuid)->val,
|
2015-05-21 16:50:03 +02:00
|
|
|
sizeof(data->info128->uuid));
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->chan.tx.mtu - data->buf->len >
|
2015-10-02 15:21:18 +02:00
|
|
|
sizeof(*data->info128)) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
2015-05-21 16:50:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_find_info_rsp(struct bt_att_chan *chan, uint16_t start_handle,
|
|
|
|
uint16_t end_handle)
|
2015-05-21 16:50:03 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2015-05-21 16:50:03 +02:00
|
|
|
struct find_info_data data;
|
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&data, 0, sizeof(data));
|
2015-05-21 16:50:03 +02:00
|
|
|
|
|
|
|
data.buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_INFO_RSP, 0);
|
|
|
|
if (!data.buf) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-21 16:50:03 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
data.chan = chan;
|
2015-05-21 16:50:03 +02:00
|
|
|
bt_gatt_foreach_attr(start_handle, end_handle, find_info_cb, &data);
|
|
|
|
|
|
|
|
if (!data.rsp) {
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_unref(data.buf);
|
2015-06-09 10:19:17 +02:00
|
|
|
/* Respond since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_FIND_INFO_REQ, start_handle,
|
2015-05-21 16:50:03 +02:00
|
|
|
BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-21 16:50:03 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2015-06-09 10:19:17 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-05-21 16:50:03 +02:00
|
|
|
}
|
2015-05-06 14:58:46 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_find_info_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-06 13:44:09 +02:00
|
|
|
{
|
|
|
|
struct bt_att_find_info_req *req;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t start_handle, end_handle, err_handle;
|
2015-05-06 13:44:09 +02:00
|
|
|
|
2015-08-18 14:48:26 +02:00
|
|
|
req = (void *)buf->data;
|
2015-05-06 13:44:09 +02:00
|
|
|
|
|
|
|
start_handle = sys_le16_to_cpu(req->start_handle);
|
|
|
|
end_handle = sys_le16_to_cpu(req->end_handle);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x", start_handle,
|
2015-06-17 12:50:29 +02:00
|
|
|
end_handle);
|
2015-05-06 13:44:09 +02:00
|
|
|
|
2015-05-06 14:58:46 +02:00
|
|
|
if (!range_is_valid(start_handle, end_handle, &err_handle)) {
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_FIND_INFO_REQ, err_handle,
|
2015-05-06 14:58:46 +02:00
|
|
|
BT_ATT_ERR_INVALID_HANDLE);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-06 13:44:09 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_find_info_rsp(chan, start_handle, end_handle);
|
2015-05-06 13:44:09 +02:00
|
|
|
}
|
|
|
|
|
2015-05-20 15:23:19 +02:00
|
|
|
struct find_type_data {
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *buf;
|
2015-05-20 15:23:19 +02:00
|
|
|
struct bt_att_handle_group *group;
|
|
|
|
const void *value;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t value_len;
|
|
|
|
uint8_t err;
|
2015-05-20 15:23:19 +02:00
|
|
|
};
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t find_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-05-20 15:23:19 +02:00
|
|
|
{
|
|
|
|
struct find_type_data *data = user_data;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan = data->chan;
|
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2015-05-20 15:23:19 +02:00
|
|
|
int read;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t uuid[16];
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *frag;
|
|
|
|
size_t len;
|
2015-05-20 15:23:19 +02:00
|
|
|
|
2016-01-14 14:58:33 +01:00
|
|
|
/* Skip secondary services */
|
|
|
|
if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) {
|
2019-07-24 14:23:48 +02:00
|
|
|
goto skip;
|
2016-01-14 14:58:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update group end_handle if not a primary service */
|
2015-12-08 18:27:43 +01:00
|
|
|
if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY)) {
|
2019-02-07 10:00:53 +01:00
|
|
|
if (data->group &&
|
|
|
|
attr->handle > sys_le16_to_cpu(data->group->end_handle)) {
|
2015-05-20 15:23:19 +02:00
|
|
|
data->group->end_handle = sys_cpu_to_le16(attr->handle);
|
|
|
|
}
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", attr->handle);
|
2015-05-20 15:23:19 +02:00
|
|
|
|
|
|
|
/* stop if there is no space left */
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->chan.tx.mtu - net_buf_frags_len(data->buf) <
|
|
|
|
sizeof(*data->group)) {
|
2015-05-20 15:23:19 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
2016-01-14 16:43:48 +01:00
|
|
|
}
|
2015-05-20 15:23:19 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
frag = net_buf_frag_last(data->buf);
|
|
|
|
|
|
|
|
len = MIN(chan->chan.tx.mtu - net_buf_frags_len(data->buf),
|
|
|
|
net_buf_tailroom(frag));
|
|
|
|
if (!len) {
|
|
|
|
frag = net_buf_alloc(net_buf_pool_get(data->buf->pool_id),
|
|
|
|
K_NO_WAIT);
|
|
|
|
/* If not buffer can be allocated immediately stop */
|
|
|
|
if (!frag) {
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_frag_add(data->buf, frag);
|
|
|
|
}
|
|
|
|
|
2015-05-20 15:23:19 +02:00
|
|
|
/* Read attribute value and store in the buffer */
|
2015-10-02 15:21:18 +02:00
|
|
|
read = attr->read(conn, attr, uuid, sizeof(uuid), 0);
|
2015-05-20 15:23:19 +02:00
|
|
|
if (read < 0) {
|
2016-01-14 15:03:47 +01:00
|
|
|
/*
|
|
|
|
* Since we don't know if it is the service with requested UUID,
|
|
|
|
* we cannot respond with an error to this request.
|
|
|
|
*/
|
2019-07-24 14:23:48 +02:00
|
|
|
goto skip;
|
2015-05-20 15:23:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if data matches */
|
2019-07-24 14:23:48 +02:00
|
|
|
if (read != data->value_len) {
|
|
|
|
/* Use bt_uuid_cmp() to compare UUIDs of different form. */
|
|
|
|
struct bt_uuid_128 ref_uuid;
|
|
|
|
struct bt_uuid_128 recvd_uuid;
|
|
|
|
|
2019-08-26 09:57:57 +02:00
|
|
|
if (!bt_uuid_create(&recvd_uuid.uuid, data->value, data->value_len)) {
|
2019-07-24 14:23:48 +02:00
|
|
|
BT_WARN("Unable to create UUID: size %u", data->value_len);
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
if (!bt_uuid_create(&ref_uuid.uuid, uuid, read)) {
|
|
|
|
BT_WARN("Unable to create UUID: size %d", read);
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
if (bt_uuid_cmp(&recvd_uuid.uuid, &ref_uuid.uuid)) {
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
} else if (memcmp(data->value, uuid, read)) {
|
|
|
|
goto skip;
|
2015-05-20 15:23:19 +02:00
|
|
|
}
|
|
|
|
|
2016-01-14 14:58:33 +01:00
|
|
|
/* If service has been found, error should be cleared */
|
|
|
|
data->err = 0x00;
|
|
|
|
|
2019-06-18 20:45:40 +02:00
|
|
|
/* Fast forward to next item position */
|
2020-02-27 02:14:20 +01:00
|
|
|
data->group = net_buf_add(frag, sizeof(*data->group));
|
2015-05-20 15:23:19 +02:00
|
|
|
data->group->start_handle = sys_cpu_to_le16(attr->handle);
|
|
|
|
data->group->end_handle = sys_cpu_to_le16(attr->handle);
|
|
|
|
|
|
|
|
/* continue to find the end_handle */
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
2019-07-24 14:23:48 +02:00
|
|
|
|
|
|
|
skip:
|
|
|
|
data->group = NULL;
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
2015-05-20 15:23:19 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_find_type_rsp(struct bt_att_chan *chan, uint16_t start_handle,
|
|
|
|
uint16_t end_handle, const void *value,
|
|
|
|
uint8_t value_len)
|
2015-05-20 15:23:19 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2015-05-20 15:23:19 +02:00
|
|
|
struct find_type_data data;
|
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&data, 0, sizeof(data));
|
2015-05-20 15:23:19 +02:00
|
|
|
|
|
|
|
data.buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_TYPE_RSP, 0);
|
|
|
|
if (!data.buf) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-20 15:23:19 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
data.chan = chan;
|
2016-01-14 14:58:33 +01:00
|
|
|
data.group = NULL;
|
2015-05-20 15:23:19 +02:00
|
|
|
data.value = value;
|
|
|
|
data.value_len = value_len;
|
|
|
|
|
2016-01-14 14:58:33 +01:00
|
|
|
/* Pre-set error in case no service will be found */
|
|
|
|
data.err = BT_ATT_ERR_ATTRIBUTE_NOT_FOUND;
|
|
|
|
|
2015-05-20 15:23:19 +02:00
|
|
|
bt_gatt_foreach_attr(start_handle, end_handle, find_type_cb, &data);
|
|
|
|
|
2016-01-14 14:58:33 +01:00
|
|
|
/* If error has not been cleared, no service has been found */
|
|
|
|
if (data.err) {
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_unref(data.buf);
|
2015-06-09 10:19:17 +02:00
|
|
|
/* Respond since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_FIND_TYPE_REQ, start_handle,
|
2016-01-14 14:58:33 +01:00
|
|
|
data.err);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-20 15:23:19 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2015-06-09 10:19:17 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-05-20 15:23:19 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_find_type_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-06 14:03:11 +02:00
|
|
|
{
|
|
|
|
struct bt_att_find_type_req *req;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t start_handle, end_handle, err_handle, type;
|
|
|
|
uint8_t *value;
|
2015-05-06 14:03:11 +02:00
|
|
|
|
2019-01-26 15:51:25 +01:00
|
|
|
req = net_buf_pull_mem(buf, sizeof(*req));
|
2015-05-06 14:03:11 +02:00
|
|
|
|
|
|
|
start_handle = sys_le16_to_cpu(req->start_handle);
|
|
|
|
end_handle = sys_le16_to_cpu(req->end_handle);
|
|
|
|
type = sys_le16_to_cpu(req->type);
|
2019-01-26 15:51:25 +01:00
|
|
|
value = buf->data;
|
2015-05-06 14:03:11 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x type %u", start_handle,
|
2015-05-06 14:03:11 +02:00
|
|
|
end_handle, type);
|
|
|
|
|
2015-05-06 14:58:46 +02:00
|
|
|
if (!range_is_valid(start_handle, end_handle, &err_handle)) {
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_FIND_TYPE_REQ, err_handle,
|
2015-05-06 14:58:46 +02:00
|
|
|
BT_ATT_ERR_INVALID_HANDLE);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-06 14:03:11 +02:00
|
|
|
}
|
|
|
|
|
2015-05-20 15:23:19 +02:00
|
|
|
/* The Attribute Protocol Find By Type Value Request shall be used with
|
2016-07-12 16:09:25 +02:00
|
|
|
* the Attribute Type parameter set to the UUID for "Primary Service"
|
2015-05-20 15:23:19 +02:00
|
|
|
* and the Attribute Value set to the 16-bit Bluetooth UUID or 128-bit
|
|
|
|
* UUID for the specific primary service.
|
|
|
|
*/
|
2018-02-12 15:11:33 +01:00
|
|
|
if (bt_uuid_cmp(BT_UUID_DECLARE_16(type), BT_UUID_GATT_PRIMARY)) {
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_FIND_TYPE_REQ, start_handle,
|
2015-06-09 10:19:17 +02:00
|
|
|
BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
|
|
|
return 0;
|
2015-05-20 15:23:19 +02:00
|
|
|
}
|
2015-05-06 14:03:11 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_find_type_rsp(chan, start_handle, end_handle, value,
|
2015-08-18 14:48:26 +02:00
|
|
|
buf->len);
|
2015-05-06 14:58:46 +02:00
|
|
|
}
|
2015-05-06 14:03:11 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t err_to_att(int err)
|
2015-11-17 12:26:40 +01:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("%d", err);
|
2015-11-17 12:26:40 +01:00
|
|
|
|
2016-02-17 13:03:20 +01:00
|
|
|
if (err < 0 && err >= -0xff) {
|
|
|
|
return -err;
|
2015-11-17 12:26:40 +01:00
|
|
|
}
|
2016-02-17 13:03:20 +01:00
|
|
|
|
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-11-17 12:26:40 +01:00
|
|
|
}
|
|
|
|
|
2015-05-20 14:28:07 +02:00
|
|
|
struct read_type_data {
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan;
|
2015-05-20 14:28:07 +02:00
|
|
|
struct bt_uuid *uuid;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *buf;
|
2015-05-20 14:28:07 +02:00
|
|
|
struct bt_att_read_type_rsp *rsp;
|
|
|
|
struct bt_att_data *item;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t err;
|
2015-05-20 14:28:07 +02:00
|
|
|
};
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
typedef bool (*attr_read_cb)(struct net_buf *buf, ssize_t read,
|
|
|
|
void *user_data);
|
|
|
|
|
|
|
|
static bool attr_read_type_cb(struct net_buf *frag, ssize_t read,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct read_type_data *data = user_data;
|
|
|
|
|
|
|
|
if (!data->rsp->len) {
|
|
|
|
/* Set len to be the first item found */
|
|
|
|
data->rsp->len = read + sizeof(*data->item);
|
|
|
|
} else if (data->rsp->len != read + sizeof(*data->item)) {
|
|
|
|
/* All items should have the same size */
|
|
|
|
frag->len -= sizeof(*data->item);
|
|
|
|
data->item = NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t att_chan_read(struct bt_att_chan *chan,
|
|
|
|
const struct bt_gatt_attr *attr,
|
2020-05-27 18:26:57 +02:00
|
|
|
struct net_buf *buf, uint16_t offset,
|
2020-02-27 02:14:20 +01:00
|
|
|
attr_read_cb cb, void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
|
|
|
ssize_t read;
|
|
|
|
struct net_buf *frag;
|
|
|
|
size_t len, total = 0;
|
|
|
|
|
|
|
|
if (chan->chan.tx.mtu <= net_buf_frags_len(buf)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
frag = net_buf_frag_last(buf);
|
|
|
|
|
|
|
|
/* Create necessary fragments if MTU is bigger than what a buffer can
|
|
|
|
* hold.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
len = MIN(chan->chan.tx.mtu - net_buf_frags_len(buf),
|
|
|
|
net_buf_tailroom(frag));
|
|
|
|
if (!len) {
|
|
|
|
frag = net_buf_alloc(net_buf_pool_get(buf->pool_id),
|
|
|
|
K_NO_WAIT);
|
|
|
|
/* If not buffer can be allocated immediately return */
|
|
|
|
if (!frag) {
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_frag_add(buf, frag);
|
|
|
|
|
|
|
|
len = MIN(chan->chan.tx.mtu - net_buf_frags_len(buf),
|
|
|
|
net_buf_tailroom(frag));
|
|
|
|
}
|
|
|
|
|
|
|
|
read = attr->read(conn, attr, frag->data + frag->len, len,
|
|
|
|
offset);
|
|
|
|
if (read < 0) {
|
|
|
|
if (total) {
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
return read;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb && !cb(frag, read, user_data)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_add(frag, read);
|
|
|
|
total += read;
|
|
|
|
offset += read;
|
|
|
|
} while (chan->chan.tx.mtu > net_buf_frags_len(buf) && read == len);
|
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t read_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-05-20 14:28:07 +02:00
|
|
|
{
|
|
|
|
struct read_type_data *data = user_data;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan = data->chan;
|
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
|
|
|
ssize_t read;
|
2015-05-20 14:28:07 +02:00
|
|
|
|
|
|
|
/* Skip if doesn't match */
|
|
|
|
if (bt_uuid_cmp(attr->uuid, data->uuid)) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", attr->handle);
|
2015-05-20 14:28:07 +02:00
|
|
|
|
2015-10-29 15:50:17 +01:00
|
|
|
/*
|
|
|
|
* If an attribute in the set of requested attributes would cause an
|
|
|
|
* Error Response then this attribute cannot be included in a
|
|
|
|
* Read By Type Response and the attributes before this attribute
|
|
|
|
* shall be returned
|
|
|
|
*
|
|
|
|
* If the first attribute in the set of requested attributes would
|
|
|
|
* cause an Error Response then no other attributes in the requested
|
|
|
|
* attributes can be considered.
|
|
|
|
*/
|
2019-09-17 15:50:51 +02:00
|
|
|
data->err = bt_gatt_check_perm(conn, attr, BT_GATT_PERM_READ_MASK);
|
2015-10-29 15:50:17 +01:00
|
|
|
if (data->err) {
|
2015-11-20 10:35:31 +01:00
|
|
|
if (data->rsp->len) {
|
2015-10-29 15:50:17 +01:00
|
|
|
data->err = 0x00;
|
|
|
|
}
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If any attribute is founded in handle range it means that error
|
|
|
|
* should be changed from pre-set: attr not found error to no error.
|
|
|
|
*/
|
|
|
|
data->err = 0x00;
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
/* Fast foward to next item position */
|
|
|
|
data->item = net_buf_add(net_buf_frag_last(data->buf),
|
|
|
|
sizeof(*data->item));
|
2015-05-20 14:28:07 +02:00
|
|
|
data->item->handle = sys_cpu_to_le16(attr->handle);
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
read = att_chan_read(chan, attr, data->buf, 0, attr_read_type_cb, data);
|
2015-05-20 14:28:07 +02:00
|
|
|
if (read < 0) {
|
2015-11-17 12:26:40 +01:00
|
|
|
data->err = err_to_att(read);
|
2015-05-20 14:28:07 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (!data->item) {
|
2015-05-20 14:28:07 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
/* continue only if there are still space for more items */
|
|
|
|
return chan->chan.tx.mtu - net_buf_frags_len(data->buf) >
|
|
|
|
data->rsp->len ? BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP;
|
2015-05-20 14:28:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_type_rsp(struct bt_att_chan *chan, struct bt_uuid *uuid,
|
|
|
|
uint16_t start_handle, uint16_t end_handle)
|
2015-05-20 14:28:07 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2015-05-20 14:28:07 +02:00
|
|
|
struct read_type_data data;
|
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&data, 0, sizeof(data));
|
2015-05-20 14:28:07 +02:00
|
|
|
|
|
|
|
data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_RSP,
|
|
|
|
sizeof(*data.rsp));
|
|
|
|
if (!data.buf) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-20 14:28:07 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
data.chan = chan;
|
2015-05-20 14:28:07 +02:00
|
|
|
data.uuid = uuid;
|
2015-10-28 09:48:34 +01:00
|
|
|
data.rsp = net_buf_add(data.buf, sizeof(*data.rsp));
|
2018-11-29 20:23:03 +01:00
|
|
|
data.rsp->len = 0U;
|
2015-05-20 14:28:07 +02:00
|
|
|
|
2015-10-29 15:50:17 +01:00
|
|
|
/* Pre-set error if no attr will be found in handle */
|
|
|
|
data.err = BT_ATT_ERR_ATTRIBUTE_NOT_FOUND;
|
|
|
|
|
2015-05-20 14:28:07 +02:00
|
|
|
bt_gatt_foreach_attr(start_handle, end_handle, read_type_cb, &data);
|
|
|
|
|
2015-10-29 15:50:17 +01:00
|
|
|
if (data.err) {
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_unref(data.buf);
|
2015-06-09 10:19:17 +02:00
|
|
|
/* Response here since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_READ_TYPE_REQ, start_handle,
|
2015-10-29 15:50:17 +01:00
|
|
|
data.err);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-20 14:28:07 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2015-06-09 10:19:17 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-05-20 14:28:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_type_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-06 14:58:46 +02:00
|
|
|
{
|
|
|
|
struct bt_att_read_type_req *req;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t start_handle, end_handle, err_handle;
|
2016-01-28 04:43:34 +01:00
|
|
|
union {
|
|
|
|
struct bt_uuid uuid;
|
|
|
|
struct bt_uuid_16 u16;
|
|
|
|
struct bt_uuid_128 u128;
|
|
|
|
} u;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t uuid_len = buf->len - sizeof(*req);
|
2015-05-06 14:58:46 +02:00
|
|
|
|
|
|
|
/* Type can only be UUID16 or UUID128 */
|
2019-07-24 14:23:48 +02:00
|
|
|
if (uuid_len != 2 && uuid_len != 16) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_INVALID_PDU;
|
2015-05-06 14:58:46 +02:00
|
|
|
}
|
|
|
|
|
2019-01-26 15:51:25 +01:00
|
|
|
req = net_buf_pull_mem(buf, sizeof(*req));
|
2015-05-06 14:58:46 +02:00
|
|
|
|
|
|
|
start_handle = sys_le16_to_cpu(req->start_handle);
|
|
|
|
end_handle = sys_le16_to_cpu(req->end_handle);
|
2019-08-26 09:57:57 +02:00
|
|
|
if (!bt_uuid_create(&u.uuid, req->uuid, uuid_len)) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-06 14:58:46 +02:00
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x type %s",
|
2016-01-28 04:43:34 +01:00
|
|
|
start_handle, end_handle, bt_uuid_str(&u.uuid));
|
2015-05-06 14:58:46 +02:00
|
|
|
|
|
|
|
if (!range_is_valid(start_handle, end_handle, &err_handle)) {
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_READ_TYPE_REQ, err_handle,
|
2015-05-06 14:58:46 +02:00
|
|
|
BT_ATT_ERR_INVALID_HANDLE);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-06 14:58:46 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_read_type_rsp(chan, &u.uuid, start_handle, end_handle);
|
2015-05-06 14:03:11 +02:00
|
|
|
}
|
|
|
|
|
2015-05-20 17:00:12 +02:00
|
|
|
struct read_data {
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t offset;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *buf;
|
2015-05-20 17:00:12 +02:00
|
|
|
struct bt_att_read_rsp *rsp;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t err;
|
2015-05-20 17:00:12 +02:00
|
|
|
};
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t read_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-05-20 17:00:12 +02:00
|
|
|
{
|
|
|
|
struct read_data *data = user_data;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan = data->chan;
|
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
|
|
|
int ret;
|
2015-05-20 17:00:12 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", attr->handle);
|
2015-05-20 17:00:12 +02:00
|
|
|
|
2015-10-28 09:48:34 +01:00
|
|
|
data->rsp = net_buf_add(data->buf, sizeof(*data->rsp));
|
2015-05-20 17:00:12 +02:00
|
|
|
|
2015-09-10 12:20:58 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2015-06-01 17:17:46 +02:00
|
|
|
/* Check attribute permissions */
|
2019-09-17 15:50:51 +02:00
|
|
|
data->err = bt_gatt_check_perm(conn, attr, BT_GATT_PERM_READ_MASK);
|
2015-06-01 17:17:46 +02:00
|
|
|
if (data->err) {
|
2015-05-20 17:00:12 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read attribute value and store in the buffer */
|
2020-02-27 02:14:20 +01:00
|
|
|
ret = att_chan_read(chan, attr, data->buf, data->offset, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
data->err = err_to_att(ret);
|
2015-05-20 17:00:12 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_rsp(struct bt_att_chan *chan, uint8_t op, uint8_t rsp,
|
|
|
|
uint16_t handle, uint16_t offset)
|
2015-05-20 17:00:12 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2015-05-20 17:00:12 +02:00
|
|
|
struct read_data data;
|
2015-07-07 13:49:29 +02:00
|
|
|
|
2019-02-05 13:28:10 +01:00
|
|
|
if (!bt_gatt_change_aware(conn, true)) {
|
|
|
|
return BT_ATT_ERR_DB_OUT_OF_SYNC;
|
|
|
|
}
|
|
|
|
|
2015-07-07 13:49:29 +02:00
|
|
|
if (!handle) {
|
|
|
|
return BT_ATT_ERR_INVALID_HANDLE;
|
|
|
|
}
|
2015-05-20 17:00:12 +02:00
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&data, 0, sizeof(data));
|
2015-05-20 17:00:12 +02:00
|
|
|
|
|
|
|
data.buf = bt_att_create_pdu(conn, rsp, 0);
|
|
|
|
if (!data.buf) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-20 17:00:12 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
data.chan = chan;
|
2015-05-20 17:00:12 +02:00
|
|
|
data.offset = offset;
|
|
|
|
|
2015-09-10 12:20:58 +02:00
|
|
|
/* Pre-set error if no attr will be found in handle */
|
|
|
|
data.err = BT_ATT_ERR_INVALID_HANDLE;
|
|
|
|
|
2015-07-07 13:49:29 +02:00
|
|
|
bt_gatt_foreach_attr(handle, handle, read_cb, &data);
|
2015-05-20 17:00:12 +02:00
|
|
|
|
2015-06-01 17:17:46 +02:00
|
|
|
/* In case of error discard data and respond with an error */
|
|
|
|
if (data.err) {
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_unref(data.buf);
|
2015-06-09 10:19:17 +02:00
|
|
|
/* Respond here since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, op, handle, data.err);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-20 17:00:12 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2015-07-07 11:08:48 +02:00
|
|
|
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-20 17:00:12 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-07 11:12:46 +02:00
|
|
|
{
|
|
|
|
struct bt_att_read_req *req;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
2015-05-07 11:12:46 +02:00
|
|
|
|
2015-08-18 14:48:26 +02:00
|
|
|
req = (void *)buf->data;
|
2015-05-07 11:12:46 +02:00
|
|
|
|
|
|
|
handle = sys_le16_to_cpu(req->handle);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", handle);
|
2015-05-07 11:12:46 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_read_rsp(chan, BT_ATT_OP_READ_REQ, BT_ATT_OP_READ_RSP,
|
2015-07-07 13:49:29 +02:00
|
|
|
handle, 0);
|
2015-05-07 11:12:46 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_blob_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-07 11:20:13 +02:00
|
|
|
{
|
|
|
|
struct bt_att_read_blob_req *req;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle, offset;
|
2015-05-07 11:20:13 +02:00
|
|
|
|
2015-08-18 14:48:26 +02:00
|
|
|
req = (void *)buf->data;
|
2015-05-07 11:20:13 +02:00
|
|
|
|
|
|
|
handle = sys_le16_to_cpu(req->handle);
|
|
|
|
offset = sys_le16_to_cpu(req->offset);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x offset %u", handle, offset);
|
2015-05-07 11:20:13 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_read_rsp(chan, BT_ATT_OP_READ_BLOB_REQ,
|
2015-07-07 13:49:29 +02:00
|
|
|
BT_ATT_OP_READ_BLOB_RSP, handle, offset);
|
2015-05-07 11:20:13 +02:00
|
|
|
}
|
|
|
|
|
2018-06-21 17:17:56 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_READ_MULTIPLE)
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_mult_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-07 12:05:14 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2015-07-07 13:49:29 +02:00
|
|
|
struct read_data data;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
2015-05-07 12:05:14 +02:00
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&data, 0, sizeof(data));
|
2015-05-07 12:05:14 +02:00
|
|
|
|
2015-07-07 13:49:29 +02:00
|
|
|
data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_RSP, 0);
|
|
|
|
if (!data.buf) {
|
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-06-15 16:10:13 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
data.chan = chan;
|
2015-05-07 12:05:14 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
while (buf->len >= sizeof(uint16_t)) {
|
2015-10-28 09:48:34 +01:00
|
|
|
handle = net_buf_pull_le16(buf);
|
2015-05-07 12:05:14 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x ", handle);
|
2015-07-07 13:49:29 +02:00
|
|
|
|
2015-10-13 18:28:40 +02:00
|
|
|
/* An Error Response shall be sent by the server in response to
|
|
|
|
* the Read Multiple Request [....] if a read operation is not
|
|
|
|
* permitted on any of the Characteristic Values.
|
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
|
2015-07-07 13:49:29 +02:00
|
|
|
bt_gatt_foreach_attr(handle, handle, read_cb, &data);
|
2015-05-07 12:05:14 +02:00
|
|
|
|
2015-07-07 13:49:29 +02:00
|
|
|
/* Stop reading in case of error */
|
|
|
|
if (data.err) {
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_unref(data.buf);
|
2015-07-07 13:49:29 +02:00
|
|
|
/* Respond here since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_READ_MULT_REQ, handle,
|
2015-07-07 13:49:29 +02:00
|
|
|
data.err);
|
|
|
|
return 0;
|
|
|
|
}
|
2015-06-15 16:10:13 +02:00
|
|
|
}
|
2015-06-09 10:19:17 +02:00
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2015-07-07 13:49:29 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-05-07 12:05:14 +02:00
|
|
|
}
|
2020-02-27 02:23:55 +01:00
|
|
|
|
|
|
|
#if defined(CONFIG_BT_EATT)
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t read_vl_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2020-02-27 02:23:55 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_mult_vl_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2020-02-27 02:23:55 +01:00
|
|
|
{
|
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
|
|
|
struct read_data data;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
2020-02-27 02:23:55 +01:00
|
|
|
|
|
|
|
(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;
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
while (buf->len >= sizeof(uint16_t)) {
|
2020-02-27 02:23:55 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2020-02-27 02:23:55 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_BT_EATT */
|
2018-06-21 17:17:56 +02:00
|
|
|
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
2015-05-07 12:05:14 +02:00
|
|
|
|
2015-05-20 12:39:16 +02:00
|
|
|
struct read_group_data {
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan;
|
2015-05-20 12:39:16 +02:00
|
|
|
struct bt_uuid *uuid;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *buf;
|
2015-05-20 12:39:16 +02:00
|
|
|
struct bt_att_read_group_rsp *rsp;
|
|
|
|
struct bt_att_group_data *group;
|
|
|
|
};
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static bool attr_read_group_cb(struct net_buf *frag, ssize_t read,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct read_group_data *data = user_data;
|
|
|
|
|
|
|
|
if (!data->rsp->len) {
|
|
|
|
/* Set len to be the first group found */
|
|
|
|
data->rsp->len = read + sizeof(*data->group);
|
|
|
|
} else if (data->rsp->len != read + sizeof(*data->group)) {
|
|
|
|
/* All groups entries should have the same size */
|
|
|
|
data->buf->len -= sizeof(*data->group);
|
|
|
|
data->group = NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t read_group_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-05-20 12:39:16 +02:00
|
|
|
{
|
|
|
|
struct read_group_data *data = user_data;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan = data->chan;
|
2015-05-20 12:39:16 +02:00
|
|
|
int read;
|
|
|
|
|
2016-01-14 14:50:58 +01:00
|
|
|
/* Update group end_handle if attribute is not a service */
|
|
|
|
if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) &&
|
|
|
|
bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) {
|
2019-02-07 10:00:53 +01:00
|
|
|
if (data->group &&
|
|
|
|
attr->handle > sys_le16_to_cpu(data->group->end_handle)) {
|
2015-05-20 12:39:16 +02:00
|
|
|
data->group->end_handle = sys_cpu_to_le16(attr->handle);
|
|
|
|
}
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2016-01-14 14:50:58 +01:00
|
|
|
/* If Group Type don't match skip */
|
|
|
|
if (bt_uuid_cmp(attr->uuid, data->uuid)) {
|
|
|
|
data->group = NULL;
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", attr->handle);
|
2015-05-20 12:39:16 +02:00
|
|
|
|
|
|
|
/* Stop if there is no space left */
|
2015-10-02 15:21:18 +02:00
|
|
|
if (data->rsp->len &&
|
2020-02-27 02:14:20 +01:00
|
|
|
chan->chan.tx.mtu - data->buf->len < data->rsp->len) {
|
2015-05-20 12:39:16 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
2016-01-14 16:43:48 +01:00
|
|
|
}
|
2015-05-20 12:39:16 +02:00
|
|
|
|
2019-06-18 20:45:40 +02:00
|
|
|
/* Fast forward to next group position */
|
2015-10-28 09:48:34 +01:00
|
|
|
data->group = net_buf_add(data->buf, sizeof(*data->group));
|
2015-05-20 12:39:16 +02:00
|
|
|
|
|
|
|
/* Initialize group handle range */
|
|
|
|
data->group->start_handle = sys_cpu_to_le16(attr->handle);
|
|
|
|
data->group->end_handle = sys_cpu_to_le16(attr->handle);
|
|
|
|
|
|
|
|
/* Read attribute value and store in the buffer */
|
2020-02-27 02:14:20 +01:00
|
|
|
read = att_chan_read(chan, attr, data->buf, 0, attr_read_group_cb,
|
|
|
|
data);
|
2015-05-20 12:39:16 +02:00
|
|
|
if (read < 0) {
|
|
|
|
/* TODO: Handle read errors */
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (!data->group) {
|
|
|
|
return BT_GATT_ITER_STOP;
|
2015-05-20 12:39:16 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
/* continue only if there are still space for more items */
|
2015-05-20 12:39:16 +02:00
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_group_rsp(struct bt_att_chan *chan, struct bt_uuid *uuid,
|
|
|
|
uint16_t start_handle, uint16_t end_handle)
|
2015-05-20 12:39:16 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2015-05-20 12:39:16 +02:00
|
|
|
struct read_group_data data;
|
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&data, 0, sizeof(data));
|
2015-05-20 12:39:16 +02:00
|
|
|
|
|
|
|
data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_GROUP_RSP,
|
|
|
|
sizeof(*data.rsp));
|
|
|
|
if (!data.buf) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-20 12:39:16 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
data.chan = chan;
|
2015-05-20 12:39:16 +02:00
|
|
|
data.uuid = uuid;
|
2015-10-28 09:48:34 +01:00
|
|
|
data.rsp = net_buf_add(data.buf, sizeof(*data.rsp));
|
2018-11-29 20:23:03 +01:00
|
|
|
data.rsp->len = 0U;
|
2016-01-14 14:50:58 +01:00
|
|
|
data.group = NULL;
|
2015-05-20 12:39:16 +02:00
|
|
|
|
|
|
|
bt_gatt_foreach_attr(start_handle, end_handle, read_group_cb, &data);
|
|
|
|
|
|
|
|
if (!data.rsp->len) {
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_unref(data.buf);
|
2015-06-09 10:19:17 +02:00
|
|
|
/* Respond here since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_READ_GROUP_REQ, start_handle,
|
2015-05-20 12:39:16 +02:00
|
|
|
BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-20 12:39:16 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2015-06-09 10:19:17 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-05-20 12:39:16 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_read_group_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-07 12:57:46 +02:00
|
|
|
{
|
|
|
|
struct bt_att_read_group_req *req;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t start_handle, end_handle, err_handle;
|
2016-01-28 04:43:34 +01:00
|
|
|
union {
|
|
|
|
struct bt_uuid uuid;
|
|
|
|
struct bt_uuid_16 u16;
|
|
|
|
struct bt_uuid_128 u128;
|
|
|
|
} u;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t uuid_len = buf->len - sizeof(*req);
|
2015-05-07 12:57:46 +02:00
|
|
|
|
|
|
|
/* Type can only be UUID16 or UUID128 */
|
2019-07-24 14:23:48 +02:00
|
|
|
if (uuid_len != 2 && uuid_len != 16) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_INVALID_PDU;
|
2015-05-07 12:57:46 +02:00
|
|
|
}
|
|
|
|
|
2019-01-26 15:51:25 +01:00
|
|
|
req = net_buf_pull_mem(buf, sizeof(*req));
|
2015-05-07 12:57:46 +02:00
|
|
|
|
|
|
|
start_handle = sys_le16_to_cpu(req->start_handle);
|
|
|
|
end_handle = sys_le16_to_cpu(req->end_handle);
|
|
|
|
|
2019-08-26 09:57:57 +02:00
|
|
|
if (!bt_uuid_create(&u.uuid, req->uuid, uuid_len)) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-07 12:57:46 +02:00
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x type %s",
|
2016-01-28 04:43:34 +01:00
|
|
|
start_handle, end_handle, bt_uuid_str(&u.uuid));
|
2015-05-07 12:57:46 +02:00
|
|
|
|
|
|
|
if (!range_is_valid(start_handle, end_handle, &err_handle)) {
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_READ_GROUP_REQ, err_handle,
|
2015-05-07 12:57:46 +02:00
|
|
|
BT_ATT_ERR_INVALID_HANDLE);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-07 12:57:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Core v4.2, Vol 3, sec 2.5.3 Attribute Grouping:
|
|
|
|
* Not all of the grouping attributes can be used in the ATT
|
2016-07-12 16:09:25 +02:00
|
|
|
* Read By Group Type Request. The "Primary Service" and "Secondary
|
|
|
|
* Service" grouping types may be used in the Read By Group Type
|
|
|
|
* Request. The "Characteristic" grouping type shall not be used in
|
2015-05-07 12:57:46 +02:00
|
|
|
* the ATT Read By Group Type Request.
|
|
|
|
*/
|
2016-01-28 04:43:34 +01:00
|
|
|
if (bt_uuid_cmp(&u.uuid, BT_UUID_GATT_PRIMARY) &&
|
|
|
|
bt_uuid_cmp(&u.uuid, BT_UUID_GATT_SECONDARY)) {
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_READ_GROUP_REQ, start_handle,
|
2016-03-21 14:48:40 +01:00
|
|
|
BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE);
|
2015-06-09 10:19:17 +02:00
|
|
|
return 0;
|
2015-05-19 16:47:48 +02:00
|
|
|
}
|
2015-05-07 12:57:46 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_read_group_rsp(chan, &u.uuid, start_handle, end_handle);
|
2015-05-07 12:57:46 +02:00
|
|
|
}
|
|
|
|
|
2015-05-26 09:15:27 +02:00
|
|
|
struct write_data {
|
|
|
|
struct bt_conn *conn;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *buf;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t req;
|
2015-05-26 09:15:27 +02:00
|
|
|
const void *value;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t len;
|
|
|
|
uint16_t offset;
|
|
|
|
uint8_t err;
|
2015-05-26 09:15:27 +02:00
|
|
|
};
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t write_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-05-26 09:15:27 +02:00
|
|
|
{
|
|
|
|
struct write_data *data = user_data;
|
|
|
|
int write;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t flags = 0U;
|
2015-05-26 09:15:27 +02:00
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
BT_DBG("handle 0x%04x offset %u", attr->handle, data->offset);
|
2015-05-26 09:15:27 +02:00
|
|
|
|
2015-06-01 17:17:46 +02:00
|
|
|
/* Check attribute permissions */
|
2019-09-17 15:50:51 +02:00
|
|
|
data->err = bt_gatt_check_perm(data->conn, attr,
|
|
|
|
BT_GATT_PERM_WRITE_MASK);
|
2015-06-01 17:17:46 +02:00
|
|
|
if (data->err) {
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2018-11-16 11:13:43 +01:00
|
|
|
/* Set command flag if not a request */
|
|
|
|
if (!data->req) {
|
2018-11-14 10:09:24 +01:00
|
|
|
flags |= BT_GATT_WRITE_FLAG_CMD;
|
|
|
|
}
|
|
|
|
|
2018-11-14 10:32:09 +01:00
|
|
|
/* Write attribute value */
|
2015-06-25 12:54:17 +02:00
|
|
|
write = attr->write(data->conn, attr, data->value, data->len,
|
2018-11-14 10:09:24 +01:00
|
|
|
data->offset, flags);
|
2015-05-26 09:15:27 +02:00
|
|
|
if (write < 0 || write != data->len) {
|
2015-06-03 12:36:36 +02:00
|
|
|
data->err = err_to_att(write);
|
2015-05-26 09:15:27 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2018-11-29 20:23:03 +01:00
|
|
|
data->err = 0U;
|
2015-05-26 09:15:27 +02:00
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_write_rsp(struct bt_att_chan *chan, uint8_t req, uint8_t rsp,
|
|
|
|
uint16_t handle, uint16_t offset, const void *value,
|
|
|
|
uint16_t len)
|
2015-05-26 09:15:27 +02:00
|
|
|
{
|
|
|
|
struct write_data data;
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (!bt_gatt_change_aware(chan->att->conn, req ? true : false)) {
|
2019-02-05 13:28:10 +01:00
|
|
|
return BT_ATT_ERR_DB_OUT_OF_SYNC;
|
|
|
|
}
|
|
|
|
|
2015-05-26 09:15:27 +02:00
|
|
|
if (!handle) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_INVALID_HANDLE;
|
2015-05-26 09:15:27 +02:00
|
|
|
}
|
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&data, 0, sizeof(data));
|
2015-05-26 09:15:27 +02:00
|
|
|
|
|
|
|
/* Only allocate buf if required to respond */
|
|
|
|
if (rsp) {
|
2020-02-27 02:14:20 +01:00
|
|
|
data.buf = bt_att_chan_create_pdu(chan, rsp, 0);
|
2015-05-26 09:15:27 +02:00
|
|
|
if (!data.buf) {
|
2015-06-09 10:19:17 +02:00
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
2015-05-26 09:15:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
data.conn = chan->att->conn;
|
2018-11-16 11:13:43 +01:00
|
|
|
data.req = req;
|
2015-06-10 14:35:35 +02:00
|
|
|
data.offset = offset;
|
2015-05-26 09:15:27 +02:00
|
|
|
data.value = value;
|
|
|
|
data.len = len;
|
|
|
|
data.err = BT_ATT_ERR_INVALID_HANDLE;
|
|
|
|
|
|
|
|
bt_gatt_foreach_attr(handle, handle, write_cb, &data);
|
|
|
|
|
|
|
|
if (data.err) {
|
2015-09-23 09:58:46 +02:00
|
|
|
/* In case of error discard data and respond with an error */
|
2015-05-26 09:15:27 +02:00
|
|
|
if (rsp) {
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_unref(data.buf);
|
2015-06-09 10:19:17 +02:00
|
|
|
/* Respond here since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, req, handle, data.err);
|
2015-05-26 09:15:27 +02:00
|
|
|
}
|
2018-11-16 11:13:43 +01:00
|
|
|
return req == BT_ATT_OP_EXEC_WRITE_REQ ? data.err : 0;
|
2015-05-26 09:15:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (data.buf) {
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2015-05-26 09:15:27 +02:00
|
|
|
}
|
2015-06-09 10:19:17 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-05-26 09:15:27 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_write_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-08 10:06:34 +02:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
2015-05-08 10:06:34 +02:00
|
|
|
|
2016-12-28 10:28:50 +01:00
|
|
|
handle = net_buf_pull_le16(buf);
|
2015-05-08 10:06:34 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", handle);
|
2015-05-08 10:06:34 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_write_rsp(chan, BT_ATT_OP_WRITE_REQ, BT_ATT_OP_WRITE_RSP,
|
2015-08-18 14:48:26 +02:00
|
|
|
handle, 0, buf->data, buf->len);
|
2015-05-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2017-08-09 08:21:11 +02:00
|
|
|
#if CONFIG_BT_ATT_PREPARE_COUNT > 0
|
2016-06-01 11:05:37 +02:00
|
|
|
struct prep_data {
|
2015-06-10 15:21:15 +02:00
|
|
|
struct bt_conn *conn;
|
2015-10-28 09:48:34 +01:00
|
|
|
struct net_buf *buf;
|
2016-06-01 11:05:37 +02:00
|
|
|
const void *value;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t len;
|
|
|
|
uint16_t offset;
|
|
|
|
uint8_t err;
|
2015-06-10 15:21:15 +02:00
|
|
|
};
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t prep_write_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-06-10 15:21:15 +02:00
|
|
|
{
|
2016-06-01 11:05:37 +02:00
|
|
|
struct prep_data *data = user_data;
|
|
|
|
struct bt_attr_data *attr_data;
|
2016-06-22 12:24:23 +02:00
|
|
|
int write;
|
2015-06-10 15:21:15 +02:00
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
BT_DBG("handle 0x%04x offset %u", attr->handle, data->offset);
|
|
|
|
|
|
|
|
/* Check attribute permissions */
|
2019-09-17 15:50:51 +02:00
|
|
|
data->err = bt_gatt_check_perm(data->conn, attr,
|
|
|
|
BT_GATT_PERM_WRITE_MASK);
|
2016-06-01 11:05:37 +02:00
|
|
|
if (data->err) {
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
2015-06-10 15:21:15 +02:00
|
|
|
|
2018-09-18 09:12:06 +02:00
|
|
|
/* Check if attribute requires handler to accept the data */
|
2016-06-22 12:24:23 +02:00
|
|
|
if (!(attr->perm & BT_GATT_PERM_PREPARE_WRITE)) {
|
2018-09-18 09:12:06 +02:00
|
|
|
goto append;
|
2016-06-22 12:24:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Write attribute value to check if device is authorized */
|
|
|
|
write = attr->write(data->conn, attr, data->value, data->len,
|
|
|
|
data->offset, BT_GATT_WRITE_FLAG_PREPARE);
|
|
|
|
if (write != 0) {
|
|
|
|
data->err = err_to_att(write);
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2018-09-18 09:12:06 +02:00
|
|
|
append:
|
2016-06-01 11:05:37 +02:00
|
|
|
/* Copy data into the outstanding queue */
|
2016-10-18 22:24:51 +02:00
|
|
|
data->buf = net_buf_alloc(&prep_pool, K_NO_WAIT);
|
2016-06-01 11:05:37 +02:00
|
|
|
if (!data->buf) {
|
|
|
|
data->err = BT_ATT_ERR_PREPARE_QUEUE_FULL;
|
2015-06-10 15:21:15 +02:00
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
attr_data = net_buf_user_data(data->buf);
|
|
|
|
attr_data->handle = attr->handle;
|
|
|
|
attr_data->offset = data->offset;
|
|
|
|
|
2016-12-25 08:55:58 +01:00
|
|
|
net_buf_add_mem(data->buf, data->value, data->len);
|
2016-06-01 11:05:37 +02:00
|
|
|
|
2018-11-29 20:23:03 +01:00
|
|
|
data->err = 0U;
|
2015-06-10 15:21:15 +02:00
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_prep_write_rsp(struct bt_att_chan *chan, uint16_t handle,
|
|
|
|
uint16_t offset, const void *value, uint8_t len)
|
2015-06-10 15:21:15 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2016-06-01 11:05:37 +02:00
|
|
|
struct prep_data data;
|
|
|
|
struct bt_att_prepare_write_rsp *rsp;
|
|
|
|
|
2019-02-05 13:28:10 +01:00
|
|
|
if (!bt_gatt_change_aware(conn, true)) {
|
|
|
|
return BT_ATT_ERR_DB_OUT_OF_SYNC;
|
|
|
|
}
|
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
if (!handle) {
|
|
|
|
return BT_ATT_ERR_INVALID_HANDLE;
|
|
|
|
}
|
2015-06-10 15:21:15 +02:00
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&data, 0, sizeof(data));
|
2015-06-10 15:21:15 +02:00
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
data.conn = conn;
|
|
|
|
data.offset = offset;
|
|
|
|
data.value = value;
|
|
|
|
data.len = len;
|
|
|
|
data.err = BT_ATT_ERR_INVALID_HANDLE;
|
|
|
|
|
|
|
|
bt_gatt_foreach_attr(handle, handle, prep_write_cb, &data);
|
|
|
|
|
|
|
|
if (data.err) {
|
|
|
|
/* Respond here since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_PREPARE_WRITE_REQ, handle,
|
2016-06-01 11:05:37 +02:00
|
|
|
data.err);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-03 13:22:26 +01:00
|
|
|
BT_DBG("buf %p handle 0x%04x offset %u", data.buf, handle, offset);
|
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
/* Store buffer in the outstanding queue */
|
2020-02-27 02:14:20 +01:00
|
|
|
net_buf_put(&chan->att->prep_queue, data.buf);
|
2016-06-01 11:05:37 +02:00
|
|
|
|
|
|
|
/* Generate response */
|
|
|
|
data.buf = bt_att_create_pdu(conn, BT_ATT_OP_PREPARE_WRITE_RSP, 0);
|
2015-06-10 15:21:15 +02:00
|
|
|
if (!data.buf) {
|
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
|
|
|
}
|
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
rsp = net_buf_add(data.buf, sizeof(*rsp));
|
|
|
|
rsp->handle = sys_cpu_to_le16(handle);
|
|
|
|
rsp->offset = sys_cpu_to_le16(offset);
|
|
|
|
net_buf_add(data.buf, len);
|
|
|
|
memcpy(rsp->value, value, len);
|
2015-06-10 15:21:15 +02:00
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, data.buf, chan_rsp_sent);
|
2015-06-10 15:21:15 +02:00
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2017-08-09 08:21:11 +02:00
|
|
|
#endif /* CONFIG_BT_ATT_PREPARE_COUNT */
|
2016-06-01 11:05:37 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_prepare_write_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2016-06-01 11:05:37 +02:00
|
|
|
{
|
2017-08-09 08:21:11 +02:00
|
|
|
#if CONFIG_BT_ATT_PREPARE_COUNT == 0
|
2016-06-01 11:05:37 +02:00
|
|
|
return BT_ATT_ERR_NOT_SUPPORTED;
|
|
|
|
#else
|
|
|
|
struct bt_att_prepare_write_req *req;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle, offset;
|
2016-06-01 11:05:37 +02:00
|
|
|
|
2019-01-26 15:51:25 +01:00
|
|
|
req = net_buf_pull_mem(buf, sizeof(*req));
|
2016-06-01 11:05:37 +02:00
|
|
|
|
|
|
|
handle = sys_le16_to_cpu(req->handle);
|
|
|
|
offset = sys_le16_to_cpu(req->offset);
|
|
|
|
|
|
|
|
BT_DBG("handle 0x%04x offset %u", handle, offset);
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_prep_write_rsp(chan, handle, offset, buf->data, buf->len);
|
2017-08-09 08:21:11 +02:00
|
|
|
#endif /* CONFIG_BT_ATT_PREPARE_COUNT */
|
2016-06-01 11:05:37 +02:00
|
|
|
}
|
|
|
|
|
2017-08-09 08:21:11 +02:00
|
|
|
#if CONFIG_BT_ATT_PREPARE_COUNT > 0
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_exec_write_rsp(struct bt_att_chan *chan, uint8_t flags)
|
2016-06-01 11:05:37 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2016-06-01 11:05:37 +02:00
|
|
|
struct net_buf *buf;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t err = 0U;
|
2016-06-01 11:05:37 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
while ((buf = net_buf_get(&chan->att->prep_queue, K_NO_WAIT))) {
|
2016-06-01 11:05:37 +02:00
|
|
|
struct bt_attr_data *data = net_buf_user_data(buf);
|
|
|
|
|
2017-01-03 13:22:26 +01:00
|
|
|
BT_DBG("buf %p handle 0x%04x offset %u", buf, data->handle,
|
|
|
|
data->offset);
|
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
/* Just discard the data if an error was set */
|
|
|
|
if (!err && flags == BT_ATT_FLAG_EXEC) {
|
2020-02-27 02:14:20 +01:00
|
|
|
err = att_write_rsp(chan, BT_ATT_OP_EXEC_WRITE_REQ, 0,
|
2016-06-01 11:05:37 +02:00
|
|
|
data->handle, data->offset,
|
|
|
|
buf->data, buf->len);
|
|
|
|
if (err) {
|
|
|
|
/* Respond here since handle is set */
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(chan, BT_ATT_OP_EXEC_WRITE_REQ,
|
2016-06-01 11:05:37 +02:00
|
|
|
data->handle, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_unref(buf);
|
2015-06-10 15:21:15 +02:00
|
|
|
}
|
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
if (err) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate response */
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_EXEC_WRITE_RSP, 0);
|
|
|
|
if (!buf) {
|
|
|
|
return BT_ATT_ERR_UNLIKELY;
|
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, buf, chan_rsp_sent);
|
2015-06-10 15:21:15 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-08-09 08:21:11 +02:00
|
|
|
#endif /* CONFIG_BT_ATT_PREPARE_COUNT */
|
2016-06-01 11:05:37 +02:00
|
|
|
|
2015-06-10 15:21:15 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_exec_write_req(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-08 13:39:51 +02:00
|
|
|
{
|
2017-08-09 08:21:11 +02:00
|
|
|
#if CONFIG_BT_ATT_PREPARE_COUNT == 0
|
2016-06-01 11:05:37 +02:00
|
|
|
return BT_ATT_ERR_NOT_SUPPORTED;
|
|
|
|
#else
|
2015-05-08 13:39:51 +02:00
|
|
|
struct bt_att_exec_write_req *req;
|
|
|
|
|
2015-08-18 14:48:26 +02:00
|
|
|
req = (void *)buf->data;
|
2015-05-08 13:39:51 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("flags 0x%02x", req->flags);
|
2015-05-08 13:39:51 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_exec_write_rsp(chan, req->flags);
|
2017-08-09 08:21:11 +02:00
|
|
|
#endif /* CONFIG_BT_ATT_PREPARE_COUNT */
|
2015-05-08 13:39:51 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_write_cmd(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-08 13:10:49 +02:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
2015-05-08 13:10:49 +02:00
|
|
|
|
2016-12-28 10:28:50 +01:00
|
|
|
handle = net_buf_pull_le16(buf);
|
2015-05-08 13:10:49 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", handle);
|
2015-05-08 13:10:49 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_write_rsp(chan, 0, 0, handle, 0, buf->data, buf->len);
|
2015-05-08 13:10:49 +02:00
|
|
|
}
|
|
|
|
|
2018-06-21 16:19:19 +02:00
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_signed_write_cmd(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-05-08 13:28:42 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_conn *conn = chan->chan.chan.conn;
|
2015-05-13 15:26:19 +02:00
|
|
|
struct bt_att_signed_write_cmd *req;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
2015-07-31 10:23:18 +02:00
|
|
|
int err;
|
2015-05-08 13:28:42 +02:00
|
|
|
|
2015-08-18 14:48:26 +02:00
|
|
|
req = (void *)buf->data;
|
2015-05-08 13:28:42 +02:00
|
|
|
|
|
|
|
handle = sys_le16_to_cpu(req->handle);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x", handle);
|
2015-05-08 13:28:42 +02:00
|
|
|
|
2015-08-13 10:49:38 +02:00
|
|
|
/* Verifying data requires full buffer including attribute header */
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_push(buf, sizeof(struct bt_att_hdr));
|
2015-08-18 14:48:26 +02:00
|
|
|
err = bt_smp_sign_verify(conn, buf);
|
2015-07-31 10:23:18 +02:00
|
|
|
if (err) {
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_ERR("Error verifying data");
|
2015-07-31 10:23:18 +02:00
|
|
|
/* No response for this command */
|
|
|
|
return 0;
|
|
|
|
}
|
2015-06-10 12:50:13 +02:00
|
|
|
|
2015-10-28 09:48:34 +01:00
|
|
|
net_buf_pull(buf, sizeof(struct bt_att_hdr));
|
|
|
|
net_buf_pull(buf, sizeof(*req));
|
2015-08-13 10:49:38 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_write_rsp(chan, 0, 0, handle, 0, buf->data,
|
2015-08-18 14:48:26 +02:00
|
|
|
buf->len - sizeof(struct bt_att_signature));
|
2015-05-08 13:28:42 +02:00
|
|
|
}
|
2018-06-21 16:19:19 +02:00
|
|
|
#endif /* CONFIG_BT_SIGNING */
|
2015-05-08 13:28:42 +02:00
|
|
|
|
2018-11-05 16:40:33 +01:00
|
|
|
#if defined(CONFIG_BT_GATT_CLIENT)
|
2017-08-09 08:21:11 +02:00
|
|
|
#if defined(CONFIG_BT_SMP)
|
2020-05-27 18:26:57 +02:00
|
|
|
static int att_change_security(struct bt_conn *conn, uint8_t err)
|
2015-09-08 16:41:57 +02:00
|
|
|
{
|
|
|
|
bt_security_t sec;
|
|
|
|
|
|
|
|
switch (err) {
|
|
|
|
case BT_ATT_ERR_INSUFFICIENT_ENCRYPTION:
|
2019-08-26 15:50:48 +02:00
|
|
|
if (conn->sec_level >= BT_SECURITY_L2)
|
2015-09-08 16:41:57 +02:00
|
|
|
return -EALREADY;
|
2019-08-26 15:50:48 +02:00
|
|
|
sec = BT_SECURITY_L2;
|
2015-09-08 16:41:57 +02:00
|
|
|
break;
|
|
|
|
case BT_ATT_ERR_AUTHENTICATION:
|
2019-08-26 15:50:48 +02:00
|
|
|
if (conn->sec_level < BT_SECURITY_L2) {
|
2016-10-19 14:55:42 +02:00
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C]
|
|
|
|
* page 375:
|
|
|
|
*
|
|
|
|
* If an LTK is not available, the service request
|
2016-11-10 10:45:06 +01:00
|
|
|
* shall be rejected with the error code 'Insufficient
|
|
|
|
* Authentication'.
|
2016-10-19 14:55:42 +02:00
|
|
|
* Note: When the link is not encrypted, the error code
|
|
|
|
* "Insufficient Authentication" does not indicate that
|
|
|
|
* MITM protection is required.
|
|
|
|
*/
|
2019-08-26 15:50:48 +02:00
|
|
|
sec = BT_SECURITY_L2;
|
|
|
|
} else if (conn->sec_level < BT_SECURITY_L3) {
|
2016-10-19 14:55:42 +02:00
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C]
|
|
|
|
* page 375:
|
|
|
|
*
|
|
|
|
* If an authenticated pairing is required but only an
|
|
|
|
* unauthenticated pairing has occurred and the link is
|
|
|
|
* currently encrypted, the service request shall be
|
2016-11-10 10:45:06 +01:00
|
|
|
* rejected with the error code 'Insufficient
|
|
|
|
* Authentication'.
|
2016-10-19 14:55:42 +02:00
|
|
|
* Note: When unauthenticated pairing has occurred and
|
|
|
|
* the link is currently encrypted, the error code
|
2016-11-10 10:45:06 +01:00
|
|
|
* 'Insufficient Authentication' indicates that MITM
|
2016-10-19 14:55:42 +02:00
|
|
|
* protection is required.
|
|
|
|
*/
|
2019-08-26 15:50:48 +02:00
|
|
|
sec = BT_SECURITY_L3;
|
|
|
|
} else if (conn->sec_level < BT_SECURITY_L4) {
|
2016-10-19 14:55:42 +02:00
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C]
|
|
|
|
* page 375:
|
|
|
|
*
|
|
|
|
* If LE Secure Connections authenticated pairing is
|
|
|
|
* required but LE legacy pairing has occurred and the
|
|
|
|
* link is currently encrypted, the service request
|
2016-11-10 10:45:06 +01:00
|
|
|
* shall be rejected with the error code ''Insufficient
|
|
|
|
* Authentication'.
|
2016-10-19 14:55:42 +02:00
|
|
|
*/
|
2019-08-26 15:50:48 +02:00
|
|
|
sec = BT_SECURITY_L4;
|
2016-10-19 14:55:42 +02:00
|
|
|
} else {
|
2015-09-08 16:41:57 +02:00
|
|
|
return -EALREADY;
|
2016-10-19 14:55:42 +02:00
|
|
|
}
|
2015-09-08 16:41:57 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-08-26 15:53:20 +02:00
|
|
|
return bt_conn_set_security(conn, sec);
|
2015-09-08 16:41:57 +02:00
|
|
|
}
|
2017-08-09 08:21:11 +02:00
|
|
|
#endif /* CONFIG_BT_SMP */
|
2015-09-08 16:41:57 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_error_rsp(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-06-30 15:22:17 +02:00
|
|
|
{
|
|
|
|
struct bt_att_error_rsp *rsp;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t err;
|
2015-06-30 15:22:17 +02:00
|
|
|
|
2015-08-18 14:48:26 +02:00
|
|
|
rsp = (void *)buf->data;
|
2015-06-30 15:22:17 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("request 0x%02x handle 0x%04x error 0x%02x", rsp->request,
|
2015-06-30 15:22:17 +02:00
|
|
|
sys_le16_to_cpu(rsp->handle), rsp->error);
|
|
|
|
|
2019-07-16 17:13:03 +02:00
|
|
|
/* Don't retry if there is no req pending or it has been cancelled */
|
2020-02-27 02:14:20 +01:00
|
|
|
if (!chan->req || chan->req == &cancel) {
|
2015-09-04 13:29:57 +02:00
|
|
|
err = BT_ATT_ERR_UNLIKELY;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->req->buf) {
|
2016-11-09 12:35:28 +01:00
|
|
|
/* Restore state to be resent */
|
2020-02-27 02:14:20 +01:00
|
|
|
net_buf_simple_restore(&chan->req->buf->b, &chan->req->state);
|
2016-11-09 12:35:28 +01:00
|
|
|
}
|
2015-06-30 15:22:17 +02:00
|
|
|
|
2016-11-09 12:35:28 +01:00
|
|
|
err = rsp->error;
|
2017-08-09 08:21:11 +02:00
|
|
|
#if defined(CONFIG_BT_SMP)
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->req->retrying) {
|
2015-09-08 16:41:57 +02:00
|
|
|
goto done;
|
2016-01-14 16:43:48 +01:00
|
|
|
}
|
2015-09-08 16:41:57 +02:00
|
|
|
|
|
|
|
/* Check if security needs to be changed */
|
2020-02-27 02:14:20 +01:00
|
|
|
if (!att_change_security(chan->chan.chan.conn, err)) {
|
|
|
|
chan->req->retrying = true;
|
2015-09-08 16:41:57 +02:00
|
|
|
/* Wait security_changed: TODO: Handle fail case */
|
|
|
|
return 0;
|
|
|
|
}
|
2017-08-09 08:21:11 +02:00
|
|
|
#endif /* CONFIG_BT_SMP */
|
2015-09-08 16:41:57 +02:00
|
|
|
|
2015-09-04 13:29:57 +02:00
|
|
|
done:
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, NULL, 0, err);
|
2015-06-30 15:22:17 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_find_info_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2015-07-07 16:21:03 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-07 16:21:03 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-07 16:21:03 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_find_type_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2015-07-03 13:48:14 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-03 13:48:14 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-03 13:48:14 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_read_type_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2015-07-06 12:34:03 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-06 12:34:03 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-06 12:34:03 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_read_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2015-07-13 10:10:09 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-13 10:10:09 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-13 10:10:09 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_read_blob_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2015-07-13 13:05:56 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-13 13:05:56 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-13 13:05:56 +02:00
|
|
|
}
|
|
|
|
|
2018-06-21 17:17:56 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_READ_MULTIPLE)
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_read_mult_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2015-07-09 10:14:31 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-09 10:14:31 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-09 10:14:31 +02:00
|
|
|
}
|
2020-02-27 02:23:55 +01:00
|
|
|
|
|
|
|
#if defined(CONFIG_BT_EATT)
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_read_mult_vl_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:23:55 +01:00
|
|
|
struct net_buf *buf)
|
|
|
|
{
|
|
|
|
BT_DBG("");
|
|
|
|
|
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_BT_EATT */
|
2018-06-21 17:17:56 +02:00
|
|
|
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
2015-07-09 10:14:31 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_read_group_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2018-09-14 15:09:59 +02:00
|
|
|
{
|
|
|
|
BT_DBG("");
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2018-09-14 15:09:59 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_write_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2015-07-15 12:27:44 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-15 12:27:44 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-15 12:27:44 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_prepare_write_rsp(struct bt_att_chan *chan,
|
2017-04-21 08:01:26 +02:00
|
|
|
struct net_buf *buf)
|
2015-07-31 11:01:30 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-31 11:01:30 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-31 11:01:30 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_handle_exec_write_rsp(struct bt_att_chan *chan,
|
2020-02-27 02:14:20 +01:00
|
|
|
struct net_buf *buf)
|
2015-07-31 13:25:21 +02:00
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-31 13:25:21 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2015-07-31 13:25:21 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_notify(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-07-22 09:14:05 +02:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
2015-07-22 09:14:05 +02:00
|
|
|
|
2015-10-28 09:48:34 +01:00
|
|
|
handle = net_buf_pull_le16(buf);
|
2015-07-22 09:14:05 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("chan %p handle 0x%04x", chan, handle);
|
2015-07-22 09:14:05 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
bt_gatt_notification(chan->att->conn, handle, buf->data, buf->len);
|
2015-07-22 09:21:47 +02:00
|
|
|
|
2015-07-22 09:14:05 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_indicate(struct bt_att_chan *chan, struct net_buf *buf)
|
2015-09-15 09:38:40 +02:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t handle;
|
2015-09-15 09:38:40 +02:00
|
|
|
|
2015-10-28 09:48:34 +01:00
|
|
|
handle = net_buf_pull_le16(buf);
|
2015-09-15 09:38:40 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("chan %p handle 0x%04x", chan, handle);
|
2015-09-15 09:38:40 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
bt_gatt_notification(chan->att->conn, handle, buf->data, buf->len);
|
2015-09-15 09:38:40 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
buf = bt_att_chan_create_pdu(chan, BT_ATT_OP_CONFIRM, 0);
|
2015-09-15 09:38:40 +02:00
|
|
|
if (!buf) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(chan, buf, chan_cfm_sent);
|
2015-09-15 09:38:40 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-02-27 02:23:55 +01:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_notify_mult(struct bt_att_chan *chan, struct net_buf *buf)
|
2020-02-27 02:23:55 +01:00
|
|
|
{
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
bt_gatt_mult_notification(chan->att->conn, buf->data, buf->len);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-11-05 16:40:33 +01:00
|
|
|
#endif /* CONFIG_BT_GATT_CLIENT */
|
2015-09-15 09:38:40 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t att_confirm(struct bt_att_chan *chan, struct net_buf *buf)
|
2016-03-03 11:21:44 +01:00
|
|
|
{
|
|
|
|
BT_DBG("");
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_handle_rsp(chan, buf->data, buf->len, 0);
|
2016-03-03 11:21:44 +01:00
|
|
|
}
|
|
|
|
|
2017-03-17 12:21:20 +01:00
|
|
|
static const struct att_handler {
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t op;
|
|
|
|
uint8_t expect_len;
|
2017-03-19 11:12:06 +01:00
|
|
|
att_type_t type;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t (*func)(struct bt_att_chan *chan, struct net_buf *buf);
|
2015-06-09 10:19:17 +02:00
|
|
|
} handlers[] = {
|
2017-03-17 09:58:46 +01:00
|
|
|
{ BT_ATT_OP_MTU_REQ,
|
|
|
|
sizeof(struct bt_att_exchange_mtu_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_mtu_req },
|
|
|
|
{ BT_ATT_OP_FIND_INFO_REQ,
|
|
|
|
sizeof(struct bt_att_find_info_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_find_info_req },
|
|
|
|
{ BT_ATT_OP_FIND_TYPE_REQ,
|
|
|
|
sizeof(struct bt_att_find_type_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_find_type_req },
|
|
|
|
{ BT_ATT_OP_READ_TYPE_REQ,
|
|
|
|
sizeof(struct bt_att_read_type_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_read_type_req },
|
|
|
|
{ BT_ATT_OP_READ_REQ,
|
|
|
|
sizeof(struct bt_att_read_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_read_req },
|
|
|
|
{ BT_ATT_OP_READ_BLOB_REQ,
|
|
|
|
sizeof(struct bt_att_read_blob_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_read_blob_req },
|
2018-06-21 17:17:56 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_READ_MULTIPLE)
|
2017-03-17 09:58:46 +01:00
|
|
|
{ BT_ATT_OP_READ_MULT_REQ,
|
|
|
|
BT_ATT_READ_MULT_MIN_LEN_REQ,
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_read_mult_req },
|
2020-02-27 02:23:55 +01:00
|
|
|
#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 */
|
2018-06-21 17:17:56 +02:00
|
|
|
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
2017-03-17 09:58:46 +01:00
|
|
|
{ BT_ATT_OP_READ_GROUP_REQ,
|
|
|
|
sizeof(struct bt_att_read_group_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_read_group_req },
|
|
|
|
{ BT_ATT_OP_WRITE_REQ,
|
|
|
|
sizeof(struct bt_att_write_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_write_req },
|
|
|
|
{ BT_ATT_OP_PREPARE_WRITE_REQ,
|
|
|
|
sizeof(struct bt_att_prepare_write_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_prepare_write_req },
|
|
|
|
{ BT_ATT_OP_EXEC_WRITE_REQ,
|
|
|
|
sizeof(struct bt_att_exec_write_req),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_REQUEST,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_exec_write_req },
|
|
|
|
{ BT_ATT_OP_CONFIRM,
|
|
|
|
0,
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_CONFIRMATION,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_confirm },
|
|
|
|
{ BT_ATT_OP_WRITE_CMD,
|
|
|
|
sizeof(struct bt_att_write_cmd),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_COMMAND,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_write_cmd },
|
2018-06-21 16:19:19 +02:00
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
2017-03-17 09:58:46 +01:00
|
|
|
{ BT_ATT_OP_SIGNED_WRITE_CMD,
|
|
|
|
(sizeof(struct bt_att_write_cmd) +
|
|
|
|
sizeof(struct bt_att_signature)),
|
2017-03-17 12:21:20 +01:00
|
|
|
ATT_COMMAND,
|
2017-03-17 09:58:46 +01:00
|
|
|
att_signed_write_cmd },
|
2018-06-21 16:19:19 +02:00
|
|
|
#endif /* CONFIG_BT_SIGNING */
|
2018-11-05 16:40:33 +01:00
|
|
|
#if defined(CONFIG_BT_GATT_CLIENT)
|
|
|
|
{ BT_ATT_OP_ERROR_RSP,
|
|
|
|
sizeof(struct bt_att_error_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_error_rsp },
|
|
|
|
{ BT_ATT_OP_MTU_RSP,
|
|
|
|
sizeof(struct bt_att_exchange_mtu_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_mtu_rsp },
|
|
|
|
{ BT_ATT_OP_FIND_INFO_RSP,
|
|
|
|
sizeof(struct bt_att_find_info_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_find_info_rsp },
|
|
|
|
{ BT_ATT_OP_FIND_TYPE_RSP,
|
|
|
|
sizeof(struct bt_att_find_type_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_find_type_rsp },
|
|
|
|
{ BT_ATT_OP_READ_TYPE_RSP,
|
|
|
|
sizeof(struct bt_att_read_type_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_read_type_rsp },
|
|
|
|
{ BT_ATT_OP_READ_RSP,
|
|
|
|
sizeof(struct bt_att_read_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_read_rsp },
|
|
|
|
{ BT_ATT_OP_READ_BLOB_RSP,
|
|
|
|
sizeof(struct bt_att_read_blob_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_read_blob_rsp },
|
|
|
|
#if defined(CONFIG_BT_GATT_READ_MULTIPLE)
|
|
|
|
{ BT_ATT_OP_READ_MULT_RSP,
|
|
|
|
sizeof(struct bt_att_read_mult_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_read_mult_rsp },
|
2020-02-27 02:23:55 +01:00
|
|
|
#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 */
|
2018-11-05 16:40:33 +01:00
|
|
|
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
|
|
|
{ BT_ATT_OP_READ_GROUP_RSP,
|
|
|
|
sizeof(struct bt_att_read_group_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_read_group_rsp },
|
|
|
|
{ BT_ATT_OP_WRITE_RSP,
|
|
|
|
0,
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_write_rsp },
|
|
|
|
{ BT_ATT_OP_PREPARE_WRITE_RSP,
|
|
|
|
sizeof(struct bt_att_prepare_write_rsp),
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_prepare_write_rsp },
|
|
|
|
{ BT_ATT_OP_EXEC_WRITE_RSP,
|
|
|
|
0,
|
|
|
|
ATT_RESPONSE,
|
|
|
|
att_handle_exec_write_rsp },
|
|
|
|
{ BT_ATT_OP_NOTIFY,
|
|
|
|
sizeof(struct bt_att_notify),
|
|
|
|
ATT_NOTIFICATION,
|
|
|
|
att_notify },
|
|
|
|
{ BT_ATT_OP_INDICATE,
|
|
|
|
sizeof(struct bt_att_indicate),
|
|
|
|
ATT_INDICATION,
|
|
|
|
att_indicate },
|
2020-02-27 02:23:55 +01:00
|
|
|
{ BT_ATT_OP_NOTIFY_MULT,
|
|
|
|
sizeof(struct bt_att_notify_mult),
|
|
|
|
ATT_NOTIFICATION,
|
|
|
|
att_notify_mult },
|
2018-11-05 16:40:33 +01:00
|
|
|
#endif /* CONFIG_BT_GATT_CLIENT */
|
2015-06-09 10:19:17 +02:00
|
|
|
};
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static att_type_t att_op_get_type(uint8_t op)
|
2017-03-19 11:12:06 +01:00
|
|
|
{
|
2019-08-28 13:03:34 +02:00
|
|
|
switch (op) {
|
|
|
|
case BT_ATT_OP_MTU_REQ:
|
|
|
|
case BT_ATT_OP_FIND_INFO_REQ:
|
|
|
|
case BT_ATT_OP_FIND_TYPE_REQ:
|
|
|
|
case BT_ATT_OP_READ_TYPE_REQ:
|
|
|
|
case BT_ATT_OP_READ_REQ:
|
|
|
|
case BT_ATT_OP_READ_BLOB_REQ:
|
|
|
|
case BT_ATT_OP_READ_MULT_REQ:
|
|
|
|
case BT_ATT_OP_READ_GROUP_REQ:
|
|
|
|
case BT_ATT_OP_WRITE_REQ:
|
|
|
|
case BT_ATT_OP_PREPARE_WRITE_REQ:
|
|
|
|
case BT_ATT_OP_EXEC_WRITE_REQ:
|
|
|
|
return ATT_REQUEST;
|
|
|
|
case BT_ATT_OP_CONFIRM:
|
|
|
|
return ATT_CONFIRMATION;
|
|
|
|
case BT_ATT_OP_WRITE_CMD:
|
|
|
|
case BT_ATT_OP_SIGNED_WRITE_CMD:
|
2017-04-28 12:23:34 +02:00
|
|
|
return ATT_COMMAND;
|
2019-08-28 13:03:34 +02:00
|
|
|
case BT_ATT_OP_ERROR_RSP:
|
|
|
|
case BT_ATT_OP_MTU_RSP:
|
|
|
|
case BT_ATT_OP_FIND_INFO_RSP:
|
|
|
|
case BT_ATT_OP_FIND_TYPE_RSP:
|
|
|
|
case BT_ATT_OP_READ_TYPE_RSP:
|
|
|
|
case BT_ATT_OP_READ_RSP:
|
|
|
|
case BT_ATT_OP_READ_BLOB_RSP:
|
|
|
|
case BT_ATT_OP_READ_MULT_RSP:
|
|
|
|
case BT_ATT_OP_READ_GROUP_RSP:
|
|
|
|
case BT_ATT_OP_WRITE_RSP:
|
|
|
|
case BT_ATT_OP_PREPARE_WRITE_RSP:
|
|
|
|
case BT_ATT_OP_EXEC_WRITE_RSP:
|
|
|
|
return ATT_RESPONSE;
|
|
|
|
case BT_ATT_OP_NOTIFY:
|
|
|
|
return ATT_NOTIFICATION;
|
|
|
|
case BT_ATT_OP_INDICATE:
|
|
|
|
return ATT_INDICATION;
|
2017-04-28 12:23:34 +02:00
|
|
|
}
|
|
|
|
|
2019-09-09 15:53:38 +02:00
|
|
|
if (op & ATT_CMD_MASK) {
|
|
|
|
return ATT_COMMAND;
|
|
|
|
}
|
|
|
|
|
2017-03-19 11:12:06 +01:00
|
|
|
return ATT_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
2018-08-22 12:16:14 +02:00
|
|
|
static int bt_att_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
2015-04-28 09:44:37 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *att_chan = ATT_CHAN(chan);
|
2019-01-26 15:51:25 +01:00
|
|
|
struct bt_att_hdr *hdr;
|
2017-03-17 12:21:20 +01:00
|
|
|
const struct att_handler *handler;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t err;
|
2015-06-09 10:19:17 +02:00
|
|
|
size_t i;
|
2015-04-28 09:44:37 +02:00
|
|
|
|
|
|
|
if (buf->len < sizeof(*hdr)) {
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_ERR("Too small ATT PDU received");
|
2018-08-22 12:16:14 +02:00
|
|
|
return 0;
|
2015-04-28 09:44:37 +02:00
|
|
|
}
|
|
|
|
|
2019-01-26 15:51:25 +01:00
|
|
|
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_DBG("Received ATT chan %p code 0x%02x len %zu", att_chan, hdr->code,
|
|
|
|
net_buf_frags_len(buf));
|
2015-04-28 09:44:37 +02:00
|
|
|
|
2017-03-17 12:21:20 +01:00
|
|
|
for (i = 0, handler = NULL; i < ARRAY_SIZE(handlers); i++) {
|
|
|
|
if (hdr->code == handlers[i].op) {
|
|
|
|
handler = &handlers[i];
|
2015-06-09 10:19:17 +02:00
|
|
|
break;
|
|
|
|
}
|
2015-04-28 09:44:37 +02:00
|
|
|
}
|
|
|
|
|
2017-03-17 12:21:20 +01:00
|
|
|
if (!handler) {
|
2019-08-28 13:32:55 +02:00
|
|
|
BT_WARN("Unhandled ATT code 0x%02x", hdr->code);
|
2017-04-28 12:23:34 +02:00
|
|
|
if (att_op_get_type(hdr->code) != ATT_COMMAND) {
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(att_chan, hdr->code, 0,
|
2017-04-28 12:23:34 +02:00
|
|
|
BT_ATT_ERR_NOT_SUPPORTED);
|
|
|
|
}
|
2018-08-22 12:16:14 +02:00
|
|
|
return 0;
|
2015-07-07 11:08:48 +02:00
|
|
|
}
|
2015-06-09 10:19:17 +02:00
|
|
|
|
2017-08-09 08:21:11 +02:00
|
|
|
if (IS_ENABLED(CONFIG_BT_ATT_ENFORCE_FLOW)) {
|
2017-03-19 11:12:06 +01:00
|
|
|
if (handler->type == ATT_REQUEST &&
|
2020-02-27 02:14:20 +01:00
|
|
|
atomic_test_and_set_bit(att_chan->flags, ATT_PENDING_RSP)) {
|
2017-03-19 11:12:06 +01:00
|
|
|
BT_WARN("Ignoring unexpected request");
|
2018-08-22 12:16:14 +02:00
|
|
|
return 0;
|
2017-03-19 11:12:06 +01:00
|
|
|
} else if (handler->type == ATT_INDICATION &&
|
2020-02-27 02:14:20 +01:00
|
|
|
atomic_test_and_set_bit(att_chan->flags,
|
2017-03-19 11:12:06 +01:00
|
|
|
ATT_PENDING_CFM)) {
|
|
|
|
BT_WARN("Ignoring unexpected indication");
|
2018-08-22 12:16:14 +02:00
|
|
|
return 0;
|
2017-03-19 11:12:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 12:21:20 +01:00
|
|
|
if (buf->len < handler->expect_len) {
|
|
|
|
BT_ERR("Invalid len %u for code 0x%02x", buf->len, hdr->code);
|
|
|
|
err = BT_ATT_ERR_INVALID_PDU;
|
|
|
|
} else {
|
2020-02-27 02:14:20 +01:00
|
|
|
err = handler->func(att_chan, buf);
|
2017-03-17 12:21:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (handler->type == ATT_REQUEST && err) {
|
2015-06-09 10:19:17 +02:00
|
|
|
BT_DBG("ATT error 0x%02x", err);
|
2020-02-27 02:14:20 +01:00
|
|
|
send_err_rsp(att_chan, hdr->code, 0, err);
|
2015-06-09 10:19:17 +02:00
|
|
|
}
|
2018-08-22 12:16:14 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-04-28 09:44:37 +02:00
|
|
|
}
|
2015-05-04 18:12:51 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static struct bt_att *att_get(struct bt_conn *conn)
|
2015-10-02 15:21:18 +02:00
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *chan;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *att_chan;
|
2017-03-30 17:45:01 +02:00
|
|
|
|
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
BT_WARN("Not connected");
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-10-02 15:21:18 +02:00
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
chan = bt_l2cap_le_lookup_rx_cid(conn, BT_L2CAP_CID_ATT);
|
2015-10-02 15:21:18 +02:00
|
|
|
if (!chan) {
|
|
|
|
BT_ERR("Unable to find ATT channel");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att_chan = ATT_CHAN(chan);
|
|
|
|
if (atomic_test_bit(att_chan->flags, ATT_DISCONNECTED)) {
|
|
|
|
BT_WARN("ATT channel flagged as disconnected");
|
2017-03-30 17:45:01 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return att_chan->att;
|
2015-10-02 15:21:18 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
struct net_buf *bt_att_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
|
2015-05-04 18:12:51 +02:00
|
|
|
{
|
2015-10-02 15:21:18 +02:00
|
|
|
struct bt_att *att;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan, *tmp;
|
2015-10-02 15:21:18 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att = att_get(conn);
|
2015-10-02 15:21:18 +02:00
|
|
|
if (!att) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-07-09 12:19:35 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&att->chans, chan, tmp, node) {
|
|
|
|
if (len + sizeof(op) > chan->chan.tx.mtu) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-08-27 14:14:06 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return bt_att_chan_create_pdu(chan, op, len);
|
2019-08-01 17:02:14 +02:00
|
|
|
}
|
2015-05-04 18:12:51 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_WARN("No ATT channel for MTU %zu", len + sizeof(op));
|
2015-05-04 18:12:51 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
return NULL;
|
2015-05-04 18:12:51 +02:00
|
|
|
}
|
2015-05-21 17:53:13 +02:00
|
|
|
|
2016-06-27 13:28:40 +02:00
|
|
|
static void att_reset(struct bt_att *att)
|
2015-05-21 19:43:42 +02:00
|
|
|
{
|
2017-02-08 15:48:26 +01:00
|
|
|
struct bt_att_req *req, *tmp;
|
2016-06-27 13:28:40 +02:00
|
|
|
struct net_buf *buf;
|
|
|
|
|
2019-10-03 15:04:58 +02:00
|
|
|
#if CONFIG_BT_ATT_PREPARE_COUNT > 0
|
2016-06-27 13:28:40 +02:00
|
|
|
/* Discard queued buffers */
|
2020-06-17 23:07:38 +02:00
|
|
|
while ((buf = net_buf_get(&att->prep_queue, K_NO_WAIT))) {
|
2016-06-27 13:28:40 +02:00
|
|
|
net_buf_unref(buf);
|
|
|
|
}
|
2019-10-03 15:04:58 +02:00
|
|
|
#endif /* CONFIG_BT_ATT_PREPARE_COUNT > 0 */
|
2019-06-13 20:26:36 +02:00
|
|
|
|
2020-06-17 23:07:38 +02:00
|
|
|
while ((buf = net_buf_get(&att->tx_queue, K_NO_WAIT))) {
|
2019-06-13 20:26:36 +02:00
|
|
|
net_buf_unref(buf);
|
|
|
|
}
|
2016-06-27 13:28:40 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att->conn = NULL;
|
2017-03-30 17:45:01 +02:00
|
|
|
|
2016-08-02 15:23:26 +02:00
|
|
|
/* Notify pending requests */
|
2017-02-08 15:48:26 +01:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&att->reqs, req, tmp, node) {
|
2016-08-02 15:23:26 +02:00
|
|
|
if (req->func) {
|
2020-04-14 19:41:30 +02:00
|
|
|
req->func(NULL, BT_ATT_ERR_UNLIKELY, NULL, 0,
|
|
|
|
req->user_data);
|
2016-08-02 15:23:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
att_req_destroy(req);
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
k_mem_slab_free(&att_slab, (void **)&att);
|
|
|
|
}
|
2016-08-02 15:23:26 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static void att_chan_detach(struct bt_att_chan *chan)
|
|
|
|
{
|
2020-06-09 02:33:14 +02:00
|
|
|
struct net_buf *buf;
|
2020-02-27 02:14:20 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
sys_slist_find_and_remove(&chan->att->chans, &chan->node);
|
|
|
|
|
|
|
|
/* Ensure that any waiters are woken up */
|
|
|
|
for (i = 0; i < CONFIG_BT_ATT_TX_MAX; i++) {
|
|
|
|
k_sem_give(&chan->tx_sem);
|
|
|
|
}
|
|
|
|
|
2020-06-09 02:33:14 +02:00
|
|
|
/* Release pending buffers */
|
2020-06-17 23:07:38 +02:00
|
|
|
while ((buf = net_buf_get(&chan->tx_queue, K_NO_WAIT))) {
|
2020-06-09 02:33:14 +02:00
|
|
|
net_buf_unref(buf);
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (chan->req) {
|
|
|
|
/* Notify outstanding request */
|
|
|
|
att_handle_rsp(chan, NULL, 0, BT_ATT_ERR_UNLIKELY);
|
2016-08-02 15:23:26 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
chan->att = NULL;
|
2016-06-27 13:28:40 +02:00
|
|
|
}
|
|
|
|
|
2016-11-10 10:26:20 +01:00
|
|
|
static void att_timeout(struct k_work *work)
|
2016-06-27 13:28:40 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan = CONTAINER_OF(work, struct bt_att_chan,
|
|
|
|
timeout_work);
|
|
|
|
struct bt_att *att = chan->att;
|
|
|
|
struct bt_l2cap_le_chan *ch = &chan->chan;
|
2016-06-27 13:28:40 +02:00
|
|
|
|
|
|
|
BT_ERR("ATT Timeout");
|
|
|
|
|
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 480:
|
|
|
|
*
|
|
|
|
* A transaction not completed within 30 seconds shall time out. Such a
|
|
|
|
* transaction shall be considered to have failed and the local higher
|
|
|
|
* layers shall be informed of this failure. No more attribute protocol
|
|
|
|
* requests, commands, indications or notifications shall be sent to the
|
|
|
|
* target device on this ATT Bearer.
|
|
|
|
*/
|
2020-02-27 02:14:20 +01:00
|
|
|
att_chan_detach(chan);
|
|
|
|
|
|
|
|
/* Don't notify if there are still channels to be used */
|
|
|
|
if (!sys_slist_is_empty(&att->chans)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-27 13:28:40 +02:00
|
|
|
att_reset(att);
|
|
|
|
|
|
|
|
/* Consider the channel disconnected */
|
|
|
|
bt_gatt_disconnected(ch->chan.conn);
|
|
|
|
ch->chan.conn = NULL;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static struct bt_att_chan *att_get_fixed_chan(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
chan = bt_l2cap_le_lookup_tx_cid(conn, BT_L2CAP_CID_ATT);
|
|
|
|
__ASSERT(chan, "No ATT channel found");
|
|
|
|
|
|
|
|
return ATT_CHAN(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void att_chan_attach(struct bt_att *att, struct bt_att_chan *chan)
|
|
|
|
{
|
|
|
|
BT_DBG("att %p chan %p flags %u", att, chan, atomic_get(chan->flags));
|
|
|
|
|
|
|
|
if (sys_slist_is_empty(&att->chans)) {
|
|
|
|
/* Init general queues when attaching the first channel */
|
|
|
|
k_fifo_init(&att->tx_queue);
|
|
|
|
#if CONFIG_BT_ATT_PREPARE_COUNT > 0
|
|
|
|
k_fifo_init(&att->prep_queue);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
sys_slist_prepend(&att->chans, &chan->node);
|
|
|
|
}
|
|
|
|
|
2016-06-27 13:28:40 +02:00
|
|
|
static void bt_att_connected(struct bt_l2cap_chan *chan)
|
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *att_chan = att_get_fixed_chan(chan->conn);
|
|
|
|
struct bt_att *att = att_chan->att;
|
2016-06-24 12:08:46 +02:00
|
|
|
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
|
2015-05-21 19:43:42 +02:00
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
BT_DBG("chan %p cid 0x%04x", ch, ch->tx.cid);
|
2015-05-21 19:43:42 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att_chan = ATT_CHAN(chan);
|
2016-06-01 11:05:37 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att_chan_attach(att, att_chan);
|
2016-05-26 14:03:04 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (!atomic_test_bit(att_chan->flags, ATT_ENHANCED)) {
|
|
|
|
ch->tx.mtu = BT_ATT_DEFAULT_LE_MTU;
|
|
|
|
ch->rx.mtu = BT_ATT_DEFAULT_LE_MTU;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_delayed_work_init(&att_chan->timeout_work, att_timeout);
|
2016-06-01 11:05:37 +02:00
|
|
|
}
|
|
|
|
|
2015-10-02 15:21:18 +02:00
|
|
|
static void bt_att_disconnected(struct bt_l2cap_chan *chan)
|
2015-05-21 19:43:42 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *att_chan = ATT_CHAN(chan);
|
|
|
|
struct bt_att *att = att_chan->att;
|
2016-06-24 12:08:46 +02:00
|
|
|
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
|
2015-05-21 19:43:42 +02:00
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
BT_DBG("chan %p cid 0x%04x", ch, ch->tx.cid);
|
2015-05-21 19:43:42 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att_chan_detach(att_chan);
|
|
|
|
|
|
|
|
/* Don't reset if there are still channels to be used */
|
|
|
|
if (!sys_slist_is_empty(&att->chans)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-01 11:05:37 +02:00
|
|
|
att_reset(att);
|
2016-03-14 11:35:07 +01:00
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
bt_gatt_disconnected(ch->chan.conn);
|
2015-05-21 19:43:42 +02:00
|
|
|
}
|
|
|
|
|
2017-08-09 08:21:11 +02:00
|
|
|
#if defined(CONFIG_BT_SMP)
|
2016-09-13 09:38:10 +02:00
|
|
|
static void bt_att_encrypt_change(struct bt_l2cap_chan *chan,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t hci_status)
|
2015-09-08 16:41:57 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *att_chan = ATT_CHAN(chan);
|
2016-06-24 12:08:46 +02:00
|
|
|
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
|
2016-05-26 14:03:04 +02:00
|
|
|
struct bt_conn *conn = ch->chan.conn;
|
2015-09-08 16:41:57 +02:00
|
|
|
|
2016-09-13 09:38:10 +02:00
|
|
|
BT_DBG("chan %p conn %p handle %u sec_level 0x%02x status 0x%02x", ch,
|
|
|
|
conn, conn->handle, conn->sec_level, hci_status);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If status (HCI status of security procedure) is non-zero, notify
|
|
|
|
* outstanding request about security failure.
|
|
|
|
*/
|
|
|
|
if (hci_status) {
|
2020-02-27 02:14:20 +01:00
|
|
|
att_handle_rsp(att_chan, NULL, 0, BT_ATT_ERR_AUTHENTICATION);
|
2016-09-13 09:38:10 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-01-13 15:01:47 +01:00
|
|
|
|
2019-09-04 13:15:07 +02:00
|
|
|
bt_gatt_encrypt_change(conn);
|
|
|
|
|
2019-08-26 15:50:48 +02:00
|
|
|
if (conn->sec_level == BT_SECURITY_L1) {
|
2015-09-08 16:41:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (!att_chan->req || !att_chan->req->retrying) {
|
2017-03-30 17:45:01 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("Retrying");
|
2015-09-08 16:41:57 +02:00
|
|
|
|
|
|
|
/* Resend buffer */
|
2020-06-09 02:33:14 +02:00
|
|
|
bt_att_chan_send_rsp(att_chan, att_chan->req->buf,
|
|
|
|
chan_cb(att_chan->req->buf));
|
2020-02-27 02:14:20 +01:00
|
|
|
|
|
|
|
att_chan->req->buf = NULL;
|
2015-09-08 16:41:57 +02:00
|
|
|
}
|
2017-08-09 08:21:11 +02:00
|
|
|
#endif /* CONFIG_BT_SMP */
|
2015-09-08 16:41:57 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static void bt_att_status(struct bt_l2cap_chan *ch, atomic_t *status)
|
|
|
|
{
|
|
|
|
struct bt_att_chan *chan = ATT_CHAN(ch);
|
|
|
|
sys_snode_t *node;
|
|
|
|
|
|
|
|
BT_DBG("chan %p status %p", ch, status);
|
|
|
|
|
|
|
|
if (!atomic_test_bit(status, BT_L2CAP_STATUS_OUT)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there is a request pending don't attempt to send */
|
|
|
|
if (chan->req) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pull next request from the list */
|
|
|
|
node = sys_slist_get(&chan->att->reqs);
|
|
|
|
if (!node) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bt_att_chan_req_send(chan, ATT_REQ(node)) >= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepend back to the list as it could not be sent */
|
|
|
|
sys_slist_prepend(&chan->att->reqs, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bt_att_released(struct bt_l2cap_chan *ch)
|
|
|
|
{
|
|
|
|
struct bt_att_chan *chan = ATT_CHAN(ch);
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
k_mem_slab_free(&chan_slab, (void **)&chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bt_att_chan *att_chan_new(struct bt_att *att, atomic_val_t flags)
|
2015-10-02 15:21:18 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
int quota = 0;
|
|
|
|
static struct bt_l2cap_chan_ops ops = {
|
2015-10-02 15:21:18 +02:00
|
|
|
.connected = bt_att_connected,
|
|
|
|
.disconnected = bt_att_disconnected,
|
|
|
|
.recv = bt_att_recv,
|
2020-02-27 02:14:20 +01:00
|
|
|
.sent = bt_att_sent,
|
|
|
|
.status = bt_att_status,
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
2016-01-13 15:01:47 +01:00
|
|
|
.encrypt_change = bt_att_encrypt_change,
|
2020-02-27 02:14:20 +01:00
|
|
|
#endif /* CONFIG_BT_SMP */
|
|
|
|
.released = bt_att_released,
|
2015-10-02 15:21:18 +02:00
|
|
|
};
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&att->chans, chan, node) {
|
|
|
|
if (chan->att == att) {
|
|
|
|
quota++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (quota == ATT_CHAN_MAX) {
|
|
|
|
BT_ERR("Maximum number of channels reached: %d", quota);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (k_mem_slab_alloc(&chan_slab, (void **)&chan, K_NO_WAIT)) {
|
|
|
|
BT_ERR("No available ATT channel for conn %p", att->conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void)memset(chan, 0, sizeof(*chan));
|
|
|
|
chan->chan.chan.ops = &ops;
|
2020-06-09 02:33:14 +02:00
|
|
|
k_fifo_init(&chan->tx_queue);
|
2020-02-27 02:14:20 +01:00
|
|
|
k_sem_init(&chan->tx_sem, CONFIG_BT_ATT_TX_MAX, CONFIG_BT_ATT_TX_MAX);
|
|
|
|
atomic_set(chan->flags, flags);
|
|
|
|
chan->att = att;
|
|
|
|
|
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bt_att_accept(struct bt_conn *conn, struct bt_l2cap_chan **ch)
|
|
|
|
{
|
2020-01-09 18:23:53 +01:00
|
|
|
struct bt_att *att;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan;
|
2015-10-02 15:21:18 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("conn %p handle %u", conn, conn->handle);
|
2015-10-02 15:21:18 +02:00
|
|
|
|
2020-01-09 18:23:53 +01:00
|
|
|
if (k_mem_slab_alloc(&att_slab, (void **)&att, K_NO_WAIT)) {
|
|
|
|
BT_ERR("No available ATT context for conn %p", conn);
|
|
|
|
return -ENOMEM;
|
2015-10-02 15:21:18 +02:00
|
|
|
}
|
|
|
|
|
2020-01-09 18:23:53 +01:00
|
|
|
(void)memset(att, 0, sizeof(*att));
|
2020-02-27 02:14:20 +01:00
|
|
|
att->conn = conn;
|
|
|
|
sys_slist_init(&att->reqs);
|
|
|
|
sys_slist_init(&att->chans);
|
|
|
|
|
|
|
|
chan = att_chan_new(att, 0);
|
|
|
|
if (!chan) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ch = &chan->chan.chan;
|
2015-10-02 15:21:18 +02:00
|
|
|
|
2020-01-09 18:23:53 +01:00
|
|
|
return 0;
|
2015-10-02 15:21:18 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
BT_L2CAP_CHANNEL_DEFINE(att_fixed_chan, BT_L2CAP_CID_ATT, bt_att_accept, NULL);
|
|
|
|
|
|
|
|
#if defined(CONFIG_BT_EATT)
|
2020-05-27 18:26:57 +02:00
|
|
|
int bt_eatt_connect(struct bt_conn *conn, uint8_t num_channels)
|
2020-01-27 10:15:25 +01:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *att_chan = att_get_fixed_chan(conn);
|
|
|
|
struct bt_att *att = att_chan->att;
|
|
|
|
struct bt_l2cap_chan *chan[CONFIG_BT_EATT_MAX] = {};
|
|
|
|
int i = 0;
|
2020-01-27 10:15:25 +01:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
if (num_channels > CONFIG_BT_EATT_MAX) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-01-27 10:15:25 +01:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
while (num_channels--) {
|
|
|
|
att_chan = att_chan_new(att, BIT(ATT_ENHANCED));
|
|
|
|
if (!att_chan) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan[i] = &att_chan->chan.chan;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!i) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bt_l2cap_ecred_chan_connect(conn, chan, BT_EATT_PSM);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_eatt_disconnect(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct bt_att_chan *chan = att_get_fixed_chan(conn);
|
|
|
|
struct bt_att *att = chan->att;
|
|
|
|
int err = -ENOTCONN;
|
|
|
|
|
|
|
|
if (!conn) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&att->chans, chan, node) {
|
|
|
|
if (atomic_test_bit(chan->flags, ATT_ENHANCED)) {
|
|
|
|
err = bt_l2cap_chan_disconnect(&chan->chan.chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_BT_EATT */
|
|
|
|
|
|
|
|
static int bt_eatt_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
|
|
|
{
|
|
|
|
struct bt_att_chan *att_chan = att_get_fixed_chan(conn);
|
|
|
|
struct bt_att *att = att_chan->att;
|
|
|
|
|
|
|
|
BT_DBG("conn %p handle %u", conn, conn->handle);
|
|
|
|
|
|
|
|
att_chan = att_chan_new(att, BIT(ATT_ENHANCED));
|
|
|
|
if (att_chan) {
|
|
|
|
*chan = &att_chan->chan.chan;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOMEM;
|
2020-01-27 10:15:25 +01:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
static void bt_eatt_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
static struct bt_l2cap_server eatt_l2cap = {
|
|
|
|
.psm = BT_EATT_PSM,
|
|
|
|
#if defined(CONFIG_BT_EATT_SEC_LEVEL)
|
|
|
|
.sec_level = CONFIG_BT_EATT_SEC_LEVEL,
|
|
|
|
#endif
|
|
|
|
.accept = bt_eatt_accept,
|
|
|
|
};
|
|
|
|
|
|
|
|
BT_DBG("");
|
|
|
|
|
|
|
|
err = bt_l2cap_server_register(&eatt_l2cap);
|
|
|
|
if (err < 0) {
|
|
|
|
BT_ERR("EATT Server registration failed %d", err);
|
|
|
|
}
|
|
|
|
}
|
2019-05-20 14:11:39 +02:00
|
|
|
|
2015-05-21 17:53:13 +02:00
|
|
|
void bt_att_init(void)
|
|
|
|
{
|
2017-06-02 14:51:54 +02:00
|
|
|
bt_gatt_init();
|
2020-02-27 02:14:20 +01:00
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EATT)) {
|
|
|
|
bt_eatt_init();
|
|
|
|
}
|
2015-05-21 17:53:13 +02:00
|
|
|
}
|
2015-06-29 13:40:40 +02:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t bt_att_get_mtu(struct bt_conn *conn)
|
2015-07-31 10:00:44 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan, *tmp;
|
2015-10-02 15:21:18 +02:00
|
|
|
struct bt_att *att;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t mtu = 0;
|
2015-10-02 15:21:18 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att = att_get(conn);
|
2015-10-02 15:21:18 +02:00
|
|
|
if (!att) {
|
|
|
|
return 0;
|
|
|
|
}
|
2015-07-31 10:00:44 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&att->chans, chan, tmp, node) {
|
|
|
|
if (chan->chan.tx.mtu > mtu) {
|
|
|
|
mtu = chan->chan.tx.mtu;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mtu;
|
2015-07-31 10:00:44 +02:00
|
|
|
}
|
|
|
|
|
2020-04-06 13:33:41 +02:00
|
|
|
struct bt_att_req *bt_att_req_alloc(k_timeout_t timeout)
|
2019-12-30 19:45:35 +01:00
|
|
|
{
|
|
|
|
struct bt_att_req *req = NULL;
|
|
|
|
|
|
|
|
/* Reserve space for request */
|
|
|
|
if (k_mem_slab_alloc(&req_slab, (void **)&req, timeout)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("req %p", req);
|
|
|
|
|
2020-01-23 15:42:55 +01:00
|
|
|
memset(req, 0, sizeof(*req));
|
2019-12-30 19:45:35 +01:00
|
|
|
|
|
|
|
return req;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_att_req_free(struct bt_att_req *req)
|
|
|
|
{
|
|
|
|
BT_DBG("req %p", req);
|
|
|
|
|
|
|
|
k_mem_slab_free(&req_slab, (void **)&req);
|
|
|
|
}
|
|
|
|
|
2019-05-21 13:52:16 +02:00
|
|
|
int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_conn_tx_cb_t cb,
|
|
|
|
void *user_data)
|
2015-06-29 13:40:40 +02:00
|
|
|
{
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan, *tmp;
|
2015-06-29 13:40:40 +02:00
|
|
|
struct bt_att *att;
|
2020-02-27 02:14:20 +01:00
|
|
|
int ret;
|
2015-06-29 13:40:40 +02:00
|
|
|
|
2019-12-02 14:46:00 +01:00
|
|
|
__ASSERT_NO_MSG(conn);
|
|
|
|
__ASSERT_NO_MSG(buf);
|
2015-06-29 13:40:40 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att = att_get(conn);
|
2015-06-29 13:40:40 +02:00
|
|
|
if (!att) {
|
2019-12-02 14:46:00 +01:00
|
|
|
net_buf_unref(buf);
|
2015-06-29 13:40:40 +02:00
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
/* If callback is set use the fixed channel since bt_l2cap_chan_send
|
|
|
|
* cannot be used with a custom user_data.
|
|
|
|
*/
|
|
|
|
if (cb) {
|
2020-06-10 21:02:09 +02:00
|
|
|
return bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, buf, cb,
|
|
|
|
user_data);
|
2017-03-30 17:45:01 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&att->chans, chan, tmp, node) {
|
|
|
|
ret = bt_att_chan_send(chan, buf, NULL);
|
|
|
|
if (ret >= 0) {
|
|
|
|
break;
|
2019-06-13 20:42:29 +02:00
|
|
|
}
|
2020-02-27 02:14:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
/* Queue buffer to be send later */
|
|
|
|
BT_DBG("Queueing buffer %p", buf);
|
2020-06-17 23:07:38 +02:00
|
|
|
net_buf_put(&att->tx_queue, buf);
|
2015-07-30 15:06:06 +02:00
|
|
|
}
|
|
|
|
|
2015-06-29 13:40:40 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-02 15:23:26 +02:00
|
|
|
int bt_att_req_send(struct bt_conn *conn, struct bt_att_req *req)
|
|
|
|
{
|
|
|
|
struct bt_att *att;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan, *tmp;
|
2016-08-02 15:23:26 +02:00
|
|
|
|
|
|
|
BT_DBG("conn %p req %p", conn, req);
|
|
|
|
|
2019-12-02 14:46:00 +01:00
|
|
|
__ASSERT_NO_MSG(conn);
|
|
|
|
__ASSERT_NO_MSG(req);
|
2016-08-02 15:23:26 +02:00
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att = att_get(conn);
|
2016-08-02 15:23:26 +02:00
|
|
|
if (!att) {
|
2019-12-02 14:46:00 +01:00
|
|
|
net_buf_unref(req->buf);
|
|
|
|
req->buf = NULL;
|
2016-08-02 15:23:26 +02:00
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&att->chans, chan, tmp, node) {
|
|
|
|
/* If there is nothing pending use the channel */
|
|
|
|
if (!chan->req) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = bt_att_chan_req_send(chan, req);
|
|
|
|
if (ret >= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2016-08-02 15:23:26 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
/* Queue the request to be send later */
|
|
|
|
sys_slist_append(&att->reqs, &req->node);
|
|
|
|
|
|
|
|
BT_DBG("req %p queued", req);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool bt_att_chan_req_cancel(struct bt_att_chan *chan,
|
|
|
|
struct bt_att_req *req)
|
|
|
|
{
|
|
|
|
if (chan->req != req) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->req = &cancel;
|
|
|
|
|
|
|
|
att_req_destroy(req);
|
|
|
|
|
|
|
|
return true;
|
2016-08-02 15:23:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void bt_att_req_cancel(struct bt_conn *conn, struct bt_att_req *req)
|
2015-06-29 13:40:40 +02:00
|
|
|
{
|
|
|
|
struct bt_att *att;
|
2020-02-27 02:14:20 +01:00
|
|
|
struct bt_att_chan *chan, *tmp;
|
2015-06-29 13:40:40 +02:00
|
|
|
|
2017-05-29 19:32:57 +02:00
|
|
|
BT_DBG("req %p", req);
|
|
|
|
|
2016-08-02 15:23:26 +02:00
|
|
|
if (!conn || !req) {
|
2015-06-29 13:40:40 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
att = att_get(conn);
|
2015-06-29 13:40:40 +02:00
|
|
|
if (!att) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&att->chans, chan, tmp, node) {
|
|
|
|
/* Check if request is outstanding */
|
|
|
|
if (bt_att_chan_req_cancel(chan, req)) {
|
|
|
|
return;
|
|
|
|
}
|
2016-08-02 15:23:26 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 02:14:20 +01:00
|
|
|
/* Remove request from the list */
|
|
|
|
sys_slist_find_and_remove(&att->reqs, &req->node);
|
|
|
|
|
2016-08-02 15:23:26 +02:00
|
|
|
att_req_destroy(req);
|
2015-06-29 13:40:40 +02:00
|
|
|
}
|