* [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support
@ 2020-02-28 23:46 Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 01/12] lib: Add definitions for Enhanced Credits Based Mode Luiz Augusto von Dentz
` (12 more replies)
0 siblings, 13 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This introduces the initial support for Bluetooth 5.2 features:
* ISO channels:
+ tools/isotest command line tool to run validation tests
* L2CAP Enhanced Credit Based Flow Control Mode
+ tools/l2test has been update to include the new mode
* Enhanced ATT Bearer:
+ Client and Server support
+ Include all new procedures
+ Automaticlly detects and enables channels
+ Number of channels configurable via main.conf
v2: Dropped ISO changes since there still under discussions how it will be
supported, change ECRED to EXT_FLOWCTL and use BT_MODE to set it.
Luiz Augusto von Dentz (12):
lib: Add definitions for Enhanced Credits Based Mode
btio: Add mode to for Enhanced Credit Mode
l2test: Add support for L2CAP_EXT_FLOWCTL_MODE
share/att: Add EATT support
shared/gatt-client: Add support for EATT features
gatt: Enable EATT bearer support
shared/gatt-server: Add support for Read Multiple Variable Length
shared/gatt-client: Add support for Read Multiple Variable Length
shared/gatt: Add support for Handle Value Multiple Notifications
gatt: Add support for Notify Multiple
core: Add support for setting the number of GATT bearers
monitor: Add support for decoding EATT
attrib/gattrib.c | 5 +-
btio/btio.c | 53 ++-
btio/btio.h | 3 +-
lib/bluetooth.h | 2 +
lib/l2cap.h | 1 +
lib/uuid.h | 3 +
monitor/l2cap.c | 39 ++
peripheral/gatt.c | 2 +-
src/device.c | 18 +-
src/gatt-client.c | 85 +++++
src/gatt-database.c | 125 +++++--
src/hcid.h | 1 +
src/main.c | 14 +
src/main.conf | 5 +
src/shared/att-types.h | 25 +-
src/shared/att.c | 780 ++++++++++++++++++++++++++-------------
src/shared/att.h | 18 +-
src/shared/gatt-client.c | 287 +++++++++++---
src/shared/gatt-client.h | 5 +-
src/shared/gatt-server.c | 389 ++++++++++++-------
src/shared/gatt-server.h | 2 +-
tools/btgatt-client.c | 2 +-
tools/btgatt-server.c | 4 +-
tools/l2test.c | 10 +-
unit/test-gatt.c | 25 +-
25 files changed, 1407 insertions(+), 496 deletions(-)
--
2.21.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 01/12] lib: Add definitions for Enhanced Credits Based Mode
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 02/12] btio: Add mode to for Enhanced Credit Mode Luiz Augusto von Dentz
` (11 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
lib/bluetooth.h | 2 ++
lib/l2cap.h | 1 +
2 files changed, 3 insertions(+)
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index d14217eac..47521d50e 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -141,6 +141,8 @@ struct bt_voice {
#define BT_PHY_LE_CODED_TX 0x00002000
#define BT_PHY_LE_CODED_RX 0x00004000
+#define BT_MODE 15
+
/* Connection and socket states */
enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
diff --git a/lib/l2cap.h b/lib/l2cap.h
index 5ce94c4ee..f9ceb2f33 100644
--- a/lib/l2cap.h
+++ b/lib/l2cap.h
@@ -197,6 +197,7 @@ typedef struct {
#define L2CAP_MODE_FLOWCTL 0x02
#define L2CAP_MODE_ERTM 0x03
#define L2CAP_MODE_STREAMING 0x04
+#define L2CAP_MODE_EXT_FLOWCTL 0x81
#define L2CAP_SERVTYPE_NOTRAFFIC 0x00
#define L2CAP_SERVTYPE_BESTEFFORT 0x01
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 02/12] btio: Add mode to for Enhanced Credit Mode
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 01/12] lib: Add definitions for Enhanced Credits Based Mode Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 03/12] l2test: Add support for L2CAP_EXT_FLOWCTL_MODE Luiz Augusto von Dentz
` (10 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds BT_IO_MODE_ECRED which directly maps to L2CAP_MODE_ECRED.
---
btio/btio.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++------
btio/btio.h | 3 ++-
2 files changed, 49 insertions(+), 7 deletions(-)
diff --git a/btio/btio.c b/btio/btio.c
index db37b99da..4c84da0ee 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -630,18 +630,34 @@ static gboolean set_le_imtu(int sock, uint16_t imtu, GError **err)
return TRUE;
}
+static gboolean set_le_mode(int sock, uint8_t mode, GError **err)
+{
+ if (setsockopt(sock, SOL_BLUETOOTH, BT_MODE, &mode,
+ sizeof(mode)) < 0) {
+ ERROR_FAILED(err, "setsockopt(BT_MODE)", errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static gboolean l2cap_set(int sock, uint8_t src_type, int sec_level,
uint16_t imtu, uint16_t omtu, uint8_t mode,
int master, int flushable, uint32_t priority,
GError **err)
{
if (imtu || omtu || mode) {
- gboolean ret;
+ gboolean ret = FALSE;
if (src_type == BDADDR_BREDR)
ret = set_l2opts(sock, imtu, omtu, mode, err);
- else
- ret = set_le_imtu(sock, imtu, err);
+ else {
+ if (imtu)
+ ret = set_le_imtu(sock, imtu, err);
+
+ if (ret && mode)
+ ret = set_le_mode(sock, mode, err);
+ }
if (!ret)
return ret;
@@ -980,6 +996,30 @@ static int get_phy(int sock, uint32_t *phy)
return 0;
}
+static int get_le_imtu(int sock, uint16_t *mtu)
+{
+ socklen_t len;
+
+ len = sizeof(*mtu);
+
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, mtu, &len) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int get_le_mode(int sock, uint8_t *mode)
+{
+ socklen_t len;
+
+ len = sizeof(*mode);
+
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_MODE, mode, &len) < 0)
+ return -errno;
+
+ return 0;
+}
+
static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
va_list args)
{
@@ -999,10 +1039,11 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
memset(&l2o, 0, sizeof(l2o));
if (src.l2_bdaddr_type != BDADDR_BREDR) {
- len = sizeof(l2o.imtu);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU,
- &l2o.imtu, &len) == 0)
+ if (get_le_imtu(sock, &l2o.imtu) == 0) {
+ /* Older kernels may not support BT_MODE */
+ get_le_mode(sock, &l2o.mode);
goto parse_opts;
+ }
/* Non-LE CoC enabled kernels will return one of these
* in which case we need to fall back to L2CAP_OPTIONS.
diff --git a/btio/btio.h b/btio/btio.h
index 41a017acb..5ebfb85c6 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -71,7 +71,8 @@ typedef enum {
BT_IO_MODE_RETRANS,
BT_IO_MODE_FLOWCTL,
BT_IO_MODE_ERTM,
- BT_IO_MODE_STREAMING
+ BT_IO_MODE_STREAMING,
+ BT_IO_MODE_EXT_FLOWCTL = 0x81
} BtIOMode;
typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 03/12] l2test: Add support for L2CAP_EXT_FLOWCTL_MODE
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 01/12] lib: Add definitions for Enhanced Credits Based Mode Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 02/12] btio: Add mode to for Enhanced Credit Mode Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 04/12] share/att: Add EATT support Luiz Augusto von Dentz
` (9 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This enables using l2test to connect or listen with
L2CAP_EXT_FLOWCTL_MODE.
---
tools/l2test.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/tools/l2test.c b/tools/l2test.c
index 8c6e08646..ee3637699 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -150,6 +150,7 @@ static struct lookup_table l2cap_modes[] = {
*/
{ "ertm", L2CAP_MODE_ERTM },
{ "streaming", L2CAP_MODE_STREAMING },
+ { "ext-flowctl",L2CAP_MODE_EXT_FLOWCTL },
{ 0 }
};
@@ -283,7 +284,7 @@ static int getopts(int sk, struct l2cap_options *opts, bool connected)
memset(opts, 0, sizeof(*opts));
- if (bdaddr_type == BDADDR_BREDR) {
+ if (bdaddr_type == BDADDR_BREDR || rfcmode) {
optlen = sizeof(*opts);
return getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, &optlen);
}
@@ -303,6 +304,13 @@ static int setopts(int sk, struct l2cap_options *opts)
return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts,
sizeof(*opts));
+ if (opts->mode) {
+ if (setsockopt(sk, SOL_BLUETOOTH, BT_MODE, &opts->mode,
+ sizeof(opts->mode)) < 0) {
+ return -errno;
+ }
+ }
+
return setsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu,
sizeof(opts->imtu));
}
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 04/12] share/att: Add EATT support
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (2 preceding siblings ...)
2020-02-28 23:46 ` [PATCH v2 03/12] l2test: Add support for L2CAP_EXT_FLOWCTL_MODE Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 05/12] shared/gatt-client: Add support for EATT features Luiz Augusto von Dentz
` (8 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds EATT support to bt_att, EATT bearers are handled as
additional channels which auto allocated for queued requests.
---
src/gatt-database.c | 4 +-
src/shared/att-types.h | 16 +-
src/shared/att.c | 566 +++++++++++++++++++++++++--------------
src/shared/att.h | 4 +
src/shared/gatt-client.c | 2 +-
5 files changed, 389 insertions(+), 203 deletions(-)
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 2bae8711a..419e4f9e1 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -2102,10 +2102,10 @@ static void append_options(DBusMessageIter *iter, void *user_data)
uint16_t mtu;
switch (op->link_type) {
- case BT_ATT_LINK_BREDR:
+ case BT_ATT_BREDR:
link = "BR/EDR";
break;
- case BT_ATT_LINK_LE:
+ case BT_ATT_LE:
link = "LE";
break;
default:
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index 8a2658de3..7b88e7d92 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -27,6 +27,10 @@
#define __packed __attribute__((packed))
#endif
+#define BT_ATT_CID 4
+#define BT_ATT_PSM 31
+#define BT_ATT_EATT_PSM 0x27
+
#define BT_ATT_SECURITY_AUTO 0
#define BT_ATT_SECURITY_LOW 1
#define BT_ATT_SECURITY_MEDIUM 2
@@ -37,9 +41,10 @@
#define BT_ATT_MAX_LE_MTU 517
#define BT_ATT_MAX_VALUE_LEN 512
-#define BT_ATT_LINK_BREDR 0x00
-#define BT_ATT_LINK_LE 0x01
-#define BT_ATT_LINK_LOCAL 0xff
+#define BT_ATT_BREDR 0x00
+#define BT_ATT_LE 0x01
+#define BT_ATT_EATT 0x02
+#define BT_ATT_LOCAL 0xff
/* ATT protocol opcodes */
#define BT_ATT_OP_ERROR_RSP 0x01
@@ -159,3 +164,8 @@ struct bt_att_pdu_error_rsp {
/* GATT Characteristic Client Features Bitfield values */
#define BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING 0x01
+#define BT_GATT_CHRC_CLI_FEAT_EATT 0x02
+#define BT_GATT_CHRC_CLI_FEAT_NFY_MULTI 0x04
+
+/* GATT Characteristic Server Features Bitfield values */
+#define BT_GATT_CHRC_SERVER_FEAT_EATT 0x01
diff --git a/src/shared/att.c b/src/shared/att.c
index 0ea6d55bd..1313703f9 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -49,32 +49,40 @@
struct att_send_op;
-struct bt_att {
- int ref_count;
+struct bt_att_chan {
+ struct bt_att *att;
int fd;
struct io *io;
- bool io_on_l2cap;
- int io_sec_level; /* Only used for non-L2CAP */
- uint8_t enc_size;
+ uint8_t type;
+ int sec_level; /* Only used for non-L2CAP */
- struct queue *req_queue; /* Queued ATT protocol requests */
struct att_send_op *pending_req;
- struct queue *ind_queue; /* Queued ATT protocol indications */
struct att_send_op *pending_ind;
- struct queue *write_queue; /* Queue of PDUs ready to send */
bool writer_active;
- struct queue *notify_list; /* List of registered callbacks */
- struct queue *disconn_list; /* List of disconnect handlers */
-
bool in_req; /* There's a pending incoming request */
uint8_t *buf;
uint16_t mtu;
+};
+
+struct bt_att {
+ int ref_count;
+ bool close_on_unref;
+ struct queue *chans;
+ uint8_t enc_size;
+ uint16_t mtu; /* Biggest possible MTU */
+
+ struct queue *notify_list; /* List of registered callbacks */
+ struct queue *disconn_list; /* List of disconnect handlers */
unsigned int next_send_id; /* IDs for "send" ops */
unsigned int next_reg_id; /* IDs for registered callbacks */
+ struct queue *req_queue; /* Queued ATT protocol requests */
+ struct queue *ind_queue; /* Queued ATT protocol indications */
+ struct queue *write_queue; /* Queue of PDUs ready to send */
+
bt_att_timeout_func_t timeout_callback;
bt_att_destroy_func_t timeout_destroy;
void *timeout_data;
@@ -362,8 +370,9 @@ static struct att_send_op *create_att_send_op(struct bt_att *att,
return op;
}
-static struct att_send_op *pick_next_send_op(struct bt_att *att)
+static struct att_send_op *pick_next_send_op(struct bt_att_chan *chan)
{
+ struct bt_att *att = chan->att;
struct att_send_op *op;
/* See if any operations are already in the write queue */
@@ -374,7 +383,7 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att)
/* If there is no pending request, pick an operation from the
* request queue.
*/
- if (!att->pending_req) {
+ if (!chan->pending_req) {
op = queue_pop_head(att->req_queue);
if (op)
return op;
@@ -383,7 +392,7 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att)
/* There is either a request pending or no requests queued. If there is
* no pending indication, pick an operation from the indication queue.
*/
- if (!att->pending_ind) {
+ if (!chan->pending_ind) {
op = queue_pop_head(att->ind_queue);
if (op)
return op;
@@ -393,22 +402,23 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att)
}
struct timeout_data {
- struct bt_att *att;
+ struct bt_att_chan *chan;
unsigned int id;
};
static bool timeout_cb(void *user_data)
{
struct timeout_data *timeout = user_data;
- struct bt_att *att = timeout->att;
+ struct bt_att_chan *chan = timeout->chan;
+ struct bt_att *att = chan->att;
struct att_send_op *op = NULL;
- if (att->pending_req && att->pending_req->id == timeout->id) {
- op = att->pending_req;
- att->pending_req = NULL;
- } else if (att->pending_ind && att->pending_ind->id == timeout->id) {
- op = att->pending_ind;
- att->pending_ind = NULL;
+ if (chan->pending_req && chan->pending_req->id == timeout->id) {
+ op = chan->pending_req;
+ chan->pending_req = NULL;
+ } else if (chan->pending_ind && chan->pending_ind->id == timeout->id) {
+ op = chan->pending_ind;
+ chan->pending_ind = NULL;
}
if (!op)
@@ -428,27 +438,28 @@ static bool timeout_cb(void *user_data)
* This should trigger an io disconnect event which will clean up the
* io and notify the upper layer.
*/
- io_shutdown(att->io);
+ io_shutdown(chan->io);
return false;
}
static void write_watch_destroy(void *user_data)
{
- struct bt_att *att = user_data;
+ struct bt_att_chan *chan = user_data;
- att->writer_active = false;
+ chan->writer_active = false;
}
static bool can_write_data(struct io *io, void *user_data)
{
- struct bt_att *att = user_data;
+ struct bt_att_chan *chan = user_data;
+ struct bt_att *att = chan->att;
struct att_send_op *op;
struct timeout_data *timeout;
ssize_t ret;
struct iovec iov;
- op = pick_next_send_op(att);
+ op = pick_next_send_op(chan);
if (!op)
return false;
@@ -478,14 +489,14 @@ static bool can_write_data(struct io *io, void *user_data)
*/
switch (op->type) {
case ATT_OP_TYPE_REQ:
- att->pending_req = op;
+ chan->pending_req = op;
break;
case ATT_OP_TYPE_IND:
- att->pending_ind = op;
+ chan->pending_ind = op;
break;
case ATT_OP_TYPE_RSP:
/* Set in_req to false to indicate that no request is pending */
- att->in_req = false;
+ chan->in_req = false;
/* fall through */
case ATT_OP_TYPE_CMD:
case ATT_OP_TYPE_NOT:
@@ -497,7 +508,7 @@ static bool can_write_data(struct io *io, void *user_data)
}
timeout = new0(struct timeout_data, 1);
- timeout->att = att;
+ timeout->chan = chan;
timeout->id = op->id;
op->timeout_id = timeout_add(ATT_TIMEOUT_INTERVAL, timeout_cb,
timeout, free);
@@ -506,25 +517,33 @@ static bool can_write_data(struct io *io, void *user_data)
return true;
}
-static void wakeup_writer(struct bt_att *att)
+static void wakeup_chan_writer(void *data, void *user_data)
{
- if (att->writer_active)
+ struct bt_att_chan *chan = data;
+ struct bt_att *att = chan->att;
+
+ if (chan->writer_active)
return;
/* Set the write handler only if there is anything that can be sent
* at all.
*/
if (queue_isempty(att->write_queue)) {
- if ((att->pending_req || queue_isempty(att->req_queue)) &&
- (att->pending_ind || queue_isempty(att->ind_queue)))
+ if ((chan->pending_req || queue_isempty(att->req_queue)) &&
+ (chan->pending_ind || queue_isempty(att->ind_queue)))
return;
}
- if (!io_set_write_handler(att->io, can_write_data, att,
+ if (!io_set_write_handler(chan->io, can_write_data, chan,
write_watch_destroy))
return;
- att->writer_active = true;
+ chan->writer_active = true;
+}
+
+static void wakeup_writer(struct bt_att *att)
+{
+ queue_foreach(att->chans, wakeup_chan_writer, NULL);
}
static void disconn_handler(void *data, void *user_data)
@@ -549,44 +568,66 @@ static void disc_att_send_op(void *data)
destroy_att_send_op(op);
}
+static void bt_att_chan_free(void *data)
+{
+ struct bt_att_chan *chan = data;
+
+ if (chan->pending_req)
+ destroy_att_send_op(chan->pending_req);
+
+ if (chan->pending_ind)
+ destroy_att_send_op(chan->pending_ind);
+
+ io_destroy(chan->io);
+
+ free(chan->buf);
+ free(chan);
+}
+
static bool disconnect_cb(struct io *io, void *user_data)
{
- struct bt_att *att = user_data;
+ struct bt_att_chan *chan = user_data;
+ struct bt_att *att = chan->att;
int err;
socklen_t len;
len = sizeof(err);
- if (getsockopt(att->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
- util_debug(att->debug_callback, att->debug_data,
+ if (getsockopt(chan->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ util_debug(chan->att->debug_callback, chan->att->debug_data,
"Failed to obtain disconnect error: %s",
strerror(errno));
err = 0;
}
- util_debug(att->debug_callback, att->debug_data,
- "Physical link disconnected: %s",
- strerror(err));
+ util_debug(chan->att->debug_callback, chan->att->debug_data,
+ "Channel %p disconnected: %s",
+ chan, strerror(err));
- io_destroy(att->io);
- att->io = NULL;
- att->fd = -1;
+ /* Dettach channel */
+ queue_remove(att->chans, chan);
/* Notify request callbacks */
queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op);
queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op);
queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op);
- if (att->pending_req) {
- disc_att_send_op(att->pending_req);
- att->pending_req = NULL;
+ if (chan->pending_req) {
+ disc_att_send_op(chan->pending_req);
+ chan->pending_req = NULL;
}
- if (att->pending_ind) {
- disc_att_send_op(att->pending_ind);
- att->pending_ind = NULL;
+ if (chan->pending_ind) {
+ disc_att_send_op(chan->pending_ind);
+ chan->pending_ind = NULL;
}
+ bt_att_chan_free(chan);
+
+ /* Don't run disconnect callback if there are channels left */
+ if (!queue_isempty(att->chans))
+ return false;
+
bt_att_ref(att);
queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err));
@@ -597,14 +638,49 @@ static bool disconnect_cb(struct io *io, void *user_data)
return false;
}
-static bool change_security(struct bt_att *att, uint8_t ecode)
+static int bt_att_chan_get_security(struct bt_att_chan *chan)
+{
+ struct bt_security sec;
+ socklen_t len;
+
+ if (chan->type == BT_ATT_LOCAL)
+ return chan->sec_level;
+
+ memset(&sec, 0, sizeof(sec));
+ len = sizeof(sec);
+ if (getsockopt(chan->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0)
+ return -EIO;
+
+ return sec.level;
+}
+
+static bool bt_att_chan_set_security(struct bt_att_chan *chan, int level)
+{
+ struct bt_security sec;
+
+ if (chan->type == BT_ATT_LOCAL) {
+ chan->sec_level = level;
+ return true;
+ }
+
+ memset(&sec, 0, sizeof(sec));
+ sec.level = level;
+
+ if (setsockopt(chan->fd, SOL_BLUETOOTH, BT_SECURITY, &sec,
+ sizeof(sec)) < 0)
+ return false;
+
+ return true;
+}
+
+static bool change_security(struct bt_att_chan *chan, uint8_t ecode)
{
int security;
- if (att->io_sec_level != BT_ATT_SECURITY_AUTO)
+ if (chan->sec_level != BT_ATT_SECURITY_AUTO)
return false;
- security = bt_att_get_security(att, NULL);
+ security = bt_att_chan_get_security(chan);
if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION &&
security < BT_ATT_SECURITY_MEDIUM) {
@@ -622,14 +698,15 @@ static bool change_security(struct bt_att *att, uint8_t ecode)
return false;
}
- return bt_att_set_security(att, security);
+ return bt_att_chan_set_security(chan, security);
}
-static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
+static bool handle_error_rsp(struct bt_att_chan *chan, uint8_t *pdu,
ssize_t pdu_len, uint8_t *opcode)
{
+ struct bt_att *att = chan->att;
const struct bt_att_pdu_error_rsp *rsp;
- struct att_send_op *op = att->pending_req;
+ struct att_send_op *op = chan->pending_req;
if (pdu_len != sizeof(*rsp)) {
*opcode = 0;
@@ -641,7 +718,7 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
*opcode = rsp->opcode;
/* Attempt to change security */
- if (!change_security(att, rsp->ecode))
+ if (!change_security(chan, rsp->ecode))
return false;
/* Remove timeout_id if outstanding */
@@ -653,16 +730,17 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
util_debug(att->debug_callback, att->debug_data,
"Retrying operation %p", op);
- att->pending_req = NULL;
+ chan->pending_req = NULL;
/* Push operation back to request queue */
return queue_push_head(att->req_queue, op);
}
-static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
+static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu,
ssize_t pdu_len)
{
- struct att_send_op *op = att->pending_req;
+ struct bt_att *att = chan->att;
+ struct att_send_op *op = chan->pending_req;
uint8_t req_opcode;
uint8_t rsp_opcode;
uint8_t *rsp_pdu = NULL;
@@ -675,7 +753,7 @@ static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
if (!op) {
util_debug(att->debug_callback, att->debug_data,
"Received unexpected ATT response");
- io_shutdown(att->io);
+ io_shutdown(chan->io);
return;
}
@@ -685,8 +763,8 @@ static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
*/
if (opcode == BT_ATT_OP_ERROR_RSP) {
/* Return if error response cause a retry */
- if (handle_error_rsp(att, pdu, pdu_len, &req_opcode)) {
- wakeup_writer(att);
+ if (handle_error_rsp(chan, pdu, pdu_len, &req_opcode)) {
+ wakeup_chan_writer(chan, NULL);
return;
}
} else if (!(req_opcode = get_req_opcode(opcode)))
@@ -715,14 +793,15 @@ done:
op->callback(rsp_opcode, rsp_pdu, rsp_pdu_len, op->user_data);
destroy_att_send_op(op);
- att->pending_req = NULL;
+ chan->pending_req = NULL;
- wakeup_writer(att);
+ wakeup_chan_writer(chan, NULL);
}
-static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
+static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len)
{
- struct att_send_op *op = att->pending_ind;
+ struct bt_att *att = chan->att;
+ struct att_send_op *op = chan->pending_ind;
/*
* Disconnect the bearer if the confirmation is unexpected or the PDU is
@@ -731,7 +810,7 @@ static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
if (!op || pdu_len) {
util_debug(att->debug_callback, att->debug_data,
"Received unexpected/invalid ATT confirmation");
- io_shutdown(att->io);
+ io_shutdown(chan->io);
return;
}
@@ -739,9 +818,9 @@ static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
op->callback(BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, op->user_data);
destroy_att_send_op(op);
- att->pending_ind = NULL;
+ chan->pending_ind = NULL;
- wakeup_writer(att);
+ wakeup_chan_writer(chan, NULL);
}
struct notify_data {
@@ -811,9 +890,10 @@ fail:
return false;
}
-static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
- ssize_t pdu_len)
+static void handle_notify(struct bt_att_chan *chan, uint8_t opcode,
+ uint8_t *pdu, ssize_t pdu_len)
{
+ struct bt_att *att = chan->att;
const struct queue_entry *entry;
bool found;
@@ -845,7 +925,7 @@ static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
* link since the MTU size is negotiated using L2CAP channel
* configuration procedures.
*/
- if (bt_att_get_link_type(att) == BT_ATT_LINK_BREDR) {
+ if (bt_att_get_link_type(att) == BT_ATT_BREDR) {
switch (opcode) {
case BT_ATT_OP_MTU_REQ:
goto not_supported;
@@ -876,22 +956,23 @@ not_supported:
static bool can_read_data(struct io *io, void *user_data)
{
- struct bt_att *att = user_data;
+ struct bt_att_chan *chan = user_data;
+ struct bt_att *att = chan->att;
uint8_t opcode;
uint8_t *pdu;
ssize_t bytes_read;
- bytes_read = read(att->fd, att->buf, att->mtu);
+ bytes_read = read(chan->fd, chan->buf, chan->mtu);
if (bytes_read < 0)
return false;
- util_hexdump('>', att->buf, bytes_read,
- att->debug_callback, att->debug_data);
+ util_hexdump('>', chan->buf, bytes_read,
+ att->debug_callback, att->debug_data);
if (bytes_read < ATT_MIN_PDU_LEN)
return true;
- pdu = att->buf;
+ pdu = chan->buf;
opcode = pdu[0];
bt_att_ref(att);
@@ -901,12 +982,12 @@ static bool can_read_data(struct io *io, void *user_data)
case ATT_OP_TYPE_RSP:
util_debug(att->debug_callback, att->debug_data,
"ATT response received: 0x%02x", opcode);
- handle_rsp(att, opcode, pdu + 1, bytes_read - 1);
+ handle_rsp(chan, opcode, pdu + 1, bytes_read - 1);
break;
case ATT_OP_TYPE_CONF:
util_debug(att->debug_callback, att->debug_data,
"ATT confirmation received: 0x%02x", opcode);
- handle_conf(att, pdu + 1, bytes_read - 1);
+ handle_conf(chan, pdu + 1, bytes_read - 1);
break;
case ATT_OP_TYPE_REQ:
/*
@@ -914,17 +995,17 @@ static bool can_read_data(struct io *io, void *user_data)
* protocol was violated. Disconnect the bearer, which will
* promptly notify the upper layer via disconnect handlers.
*/
- if (att->in_req) {
+ if (chan->in_req) {
util_debug(att->debug_callback, att->debug_data,
"Received request while another is "
"pending: 0x%02x", opcode);
- io_shutdown(att->io);
- bt_att_unref(att);
+ io_shutdown(chan->io);
+ bt_att_unref(chan->att);
return false;
}
- att->in_req = true;
+ chan->in_req = true;
/* fall through */
case ATT_OP_TYPE_CMD:
case ATT_OP_TYPE_NOT:
@@ -937,7 +1018,7 @@ static bool can_read_data(struct io *io, void *user_data)
*/
util_debug(att->debug_callback, att->debug_data,
"ATT PDU received: 0x%02x", opcode);
- handle_notify(att, opcode, pdu + 1, bytes_read - 1);
+ handle_notify(chan, opcode, pdu + 1, bytes_read - 1);
break;
}
@@ -973,21 +1054,8 @@ static bool is_io_l2cap_based(int fd)
static void bt_att_free(struct bt_att *att)
{
- if (att->pending_req)
- destroy_att_send_op(att->pending_req);
-
- if (att->pending_ind)
- destroy_att_send_op(att->pending_ind);
-
- io_destroy(att->io);
bt_crypto_unref(att->crypto);
- queue_destroy(att->req_queue, NULL);
- queue_destroy(att->ind_queue, NULL);
- queue_destroy(att->write_queue, NULL);
- queue_destroy(att->notify_list, NULL);
- queue_destroy(att->disconn_list, NULL);
-
if (att->timeout_destroy)
att->timeout_destroy(att->timeout_data);
@@ -997,7 +1065,12 @@ static void bt_att_free(struct bt_att *att)
free(att->local_sign);
free(att->remote_sign);
- free(att->buf);
+ queue_destroy(att->req_queue, NULL);
+ queue_destroy(att->ind_queue, NULL);
+ queue_destroy(att->write_queue, NULL);
+ queue_destroy(att->notify_list, NULL);
+ queue_destroy(att->disconn_list, NULL);
+ queue_destroy(att->chans, bt_att_chan_free);
free(att);
}
@@ -1014,60 +1087,101 @@ static uint16_t get_l2cap_mtu(int fd)
return l2o.omtu;
}
-struct bt_att *bt_att_new(int fd, bool ext_signed)
+static uint8_t io_get_type(int fd)
{
- struct bt_att *att;
+ struct sockaddr_l2 src;
+ socklen_t len;
- if (fd < 0)
- return NULL;
+ if (!is_io_l2cap_based(fd))
+ return BT_ATT_LOCAL;
- att = new0(struct bt_att, 1);
- att->fd = fd;
+ len = sizeof(src);
+ memset(&src, 0, len);
+ if (getsockname(fd, (void *)&src, &len) < 0)
+ return -errno;
- att->io = io_new(fd);
- if (!att->io)
- goto fail;
+ if (src.l2_bdaddr_type == BDADDR_BREDR)
+ return BT_ATT_BREDR;
- /* crypto is optional, if not available leave it NULL */
- if (!ext_signed)
- att->crypto = bt_crypto_new();
+ return BT_ATT_LE;
+}
- att->req_queue = queue_new();
- att->ind_queue = queue_new();
- att->write_queue = queue_new();
- att->notify_list = queue_new();
- att->disconn_list = queue_new();
+static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type)
+{
+ struct bt_att_chan *chan;
- if (!io_set_read_handler(att->io, can_read_data, att, NULL))
+ if (fd < 0)
+ return NULL;
+
+ chan = new0(struct bt_att_chan, 1);
+ chan->fd = fd;
+
+ chan->io = io_new(fd);
+ if (!chan->io)
goto fail;
- if (!io_set_disconnect_handler(att->io, disconnect_cb, att, NULL))
+ if (!io_set_read_handler(chan->io, can_read_data, chan, NULL))
goto fail;
- att->io_on_l2cap = is_io_l2cap_based(att->fd);
- if (!att->io_on_l2cap)
- att->io_sec_level = BT_ATT_SECURITY_LOW;
+ if (!io_set_disconnect_handler(chan->io, disconnect_cb, chan, NULL))
+ goto fail;
- if (bt_att_get_link_type(att) == BT_ATT_LINK_BREDR)
- att->mtu = get_l2cap_mtu(att->fd);
- else
- att->mtu = BT_ATT_DEFAULT_LE_MTU;
+ chan->type = type;
+ switch (chan->type) {
+ case BT_ATT_LOCAL:
+ chan->sec_level = BT_ATT_SECURITY_LOW;
+ /* fall through */
+ case BT_ATT_LE:
+ chan->mtu = BT_ATT_DEFAULT_LE_MTU;
+ break;
+ default:
+ chan->mtu = get_l2cap_mtu(chan->fd);
+ }
- if (att->mtu < BT_ATT_DEFAULT_LE_MTU)
+ if (chan->mtu < BT_ATT_DEFAULT_LE_MTU)
goto fail;
- att->buf = malloc(att->mtu);
- if (!att->buf)
+ chan->buf = malloc(chan->mtu);
+ if (!chan->buf)
goto fail;
- return bt_att_ref(att);
+ return chan;
fail:
- bt_att_free(att);
+ bt_att_chan_free(chan);
return NULL;
}
+struct bt_att *bt_att_new(int fd, bool ext_signed)
+{
+ struct bt_att *att;
+ struct bt_att_chan *chan;
+
+ chan = bt_att_chan_new(fd, io_get_type(fd));
+ if (!chan)
+ return NULL;
+
+ att = new0(struct bt_att, 1);
+ att->chans = queue_new();
+ att->mtu = chan->mtu;
+
+ queue_push_head(att->chans, chan);
+ chan->att = att;
+
+ /* crypto is optional, if not available leave it NULL */
+ if (!ext_signed)
+ att->crypto = bt_crypto_new();
+
+ att->req_queue = queue_new();
+ att->ind_queue = queue_new();
+ att->write_queue = queue_new();
+ att->notify_list = queue_new();
+ att->disconn_list = queue_new();
+
+ return bt_att_ref(att);
+}
+
struct bt_att *bt_att_ref(struct bt_att *att)
{
if (!att)
@@ -1094,18 +1208,67 @@ void bt_att_unref(struct bt_att *att)
bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
{
- if (!att || !att->io)
+ const struct queue_entry *entry;
+
+ if (!att)
return false;
- return io_set_close_on_destroy(att->io, do_close);
+ att->close_on_unref = do_close;
+
+ for (entry = queue_get_entries(att->chans); entry;
+ entry = entry->next) {
+ struct bt_att_chan *chan = entry->data;
+
+ if (!io_set_close_on_destroy(chan->io, do_close))
+ return false;
+ }
+
+ return true;
+}
+
+int bt_att_attach_fd(struct bt_att *att, int fd)
+{
+ struct bt_att_chan *chan;
+
+ if (!att || fd < 0)
+ return -EINVAL;
+
+ chan = bt_att_chan_new(fd, BT_ATT_EATT);
+ if (!chan)
+ return -EINVAL;
+
+ queue_push_tail(att->chans, chan);
+ chan->att = att;
+
+ if (chan->mtu > att->mtu)
+ att->mtu = chan->mtu;
+
+ io_set_close_on_destroy(chan->io, att->close_on_unref);
+
+ return 0;
}
int bt_att_get_fd(struct bt_att *att)
{
+ struct bt_att_chan *chan;
+
if (!att)
return -1;
- return att->fd;
+ if (queue_isempty(att->chans))
+ return -ENOTCONN;
+
+ chan = queue_peek_head(att->chans);
+
+ return chan->fd;
+}
+
+int bt_att_get_channels(struct bt_att *att)
+{
+ if (!att)
+ return 0;
+
+ return queue_length(att->chans);
}
bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
@@ -1134,6 +1297,7 @@ uint16_t bt_att_get_mtu(struct bt_att *att)
bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu)
{
+ struct bt_att_chan *chan;
void *buf;
if (!att)
@@ -1142,38 +1306,37 @@ bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu)
if (mtu < BT_ATT_DEFAULT_LE_MTU)
return false;
+ chan = queue_peek_head(att->chans);
+ if (!chan)
+ return -ENOTCONN;
+
buf = malloc(mtu);
if (!buf)
return false;
- free(att->buf);
+ free(chan->buf);
+
+ chan->mtu = mtu;
+ chan->buf = buf;
- att->mtu = mtu;
- att->buf = buf;
+ if (chan->mtu > att->mtu)
+ att->mtu = chan->mtu;
return true;
}
uint8_t bt_att_get_link_type(struct bt_att *att)
{
- struct sockaddr_l2 src;
- socklen_t len;
+ struct bt_att_chan *chan;
if (!att)
return -EINVAL;
- if (!att->io_on_l2cap)
- return BT_ATT_LINK_LOCAL;
+ chan = queue_peek_head(att->chans);
+ if (!chan)
+ return -ENOTCONN;
- len = sizeof(src);
- memset(&src, 0, len);
- if (getsockname(att->fd, (void *)&src, &len) < 0)
- return -errno;
-
- if (src.l2_bdaddr_type == BDADDR_BREDR)
- return BT_ATT_LINK_BREDR;
-
- return BT_ATT_LINK_LE;
+ return chan->type;
}
bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
@@ -1200,7 +1363,7 @@ unsigned int bt_att_register_disconnect(struct bt_att *att,
{
struct att_disconn *disconn;
- if (!att || !att->io)
+ if (!att || queue_isempty(att->chans))
return 0;
disconn = new0(struct att_disconn, 1);
@@ -1229,7 +1392,7 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id)
return false;
/* Check if disconnect is running */
- if (!att->io) {
+ if (!queue_isempty(att->chans)) {
disconn = queue_find(att->disconn_list, match_disconn_id,
UINT_TO_PTR(id));
if (!disconn)
@@ -1256,7 +1419,7 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
struct att_send_op *op;
bool result;
- if (!att || !att->io)
+ if (!att || queue_isempty(att->chans))
return 0;
op = create_att_send_op(att, opcode, pdu, length, callback, user_data,
@@ -1308,21 +1471,31 @@ static bool match_op_id(const void *a, const void *b)
bool bt_att_cancel(struct bt_att *att, unsigned int id)
{
+ const struct queue_entry *entry;
struct att_send_op *op;
if (!att || !id)
return false;
- if (att->pending_req && att->pending_req->id == id) {
- /* Don't cancel the pending request; remove it's handlers */
- cancel_att_send_op(att->pending_req);
- return true;
- }
+ for (entry = queue_get_entries(att->chans); entry;
+ entry = entry->next) {
+ struct bt_att_chan *chan = entry->data;
- if (att->pending_ind && att->pending_ind->id == id) {
- /* Don't cancel the pending indication; remove it's handlers */
- cancel_att_send_op(att->pending_ind);
- return true;
+ if (chan->pending_req && chan->pending_req->id == id) {
+ /* Don't cancel the pending request; remove it's
+ * handlers
+ */
+ cancel_att_send_op(chan->pending_req);
+ return true;
+ }
+
+ if (chan->pending_ind && chan->pending_ind->id == id) {
+ /* Don't cancel the pending indication; remove it's
+ * handlers.
+ */
+ cancel_att_send_op(chan->pending_ind);
+ return true;
+ }
}
op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id));
@@ -1350,6 +1523,8 @@ done:
bool bt_att_cancel_all(struct bt_att *att)
{
+ const struct queue_entry *entry;
+
if (!att)
return false;
@@ -1357,13 +1532,22 @@ bool bt_att_cancel_all(struct bt_att *att)
queue_remove_all(att->ind_queue, NULL, NULL, destroy_att_send_op);
queue_remove_all(att->write_queue, NULL, NULL, destroy_att_send_op);
- if (att->pending_req)
- /* Don't cancel the pending request; remove it's handlers */
- cancel_att_send_op(att->pending_req);
-
- if (att->pending_ind)
- /* Don't cancel the pending request; remove it's handlers */
- cancel_att_send_op(att->pending_ind);
+ for (entry = queue_get_entries(att->chans); entry;
+ entry = entry->next) {
+ struct bt_att_chan *chan = entry->data;
+
+ if (chan->pending_req)
+ /* Don't cancel the pending request; remove it's
+ * handlers
+ */
+ cancel_att_send_op(chan->pending_req);
+
+ if (chan->pending_ind)
+ /* Don't cancel the pending request; remove it's
+ * handlers
+ */
+ cancel_att_send_op(chan->pending_ind);
+ }
return true;
}
@@ -1424,7 +1608,7 @@ unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
{
struct att_notify *notify;
- if (!att || !callback || !att->io)
+ if (!att || !callback || queue_isempty(att->chans))
return 0;
notify = new0(struct att_notify, 1);
@@ -1475,51 +1659,39 @@ bool bt_att_unregister_all(struct bt_att *att)
int bt_att_get_security(struct bt_att *att, uint8_t *enc_size)
{
- struct bt_security sec;
- socklen_t len;
+ struct bt_att_chan *chan;
+ int ret;
if (!att)
return -EINVAL;
- if (!att->io_on_l2cap) {
- if (enc_size)
- *enc_size = att->enc_size;
+ chan = queue_peek_head(att->chans);
+ if (!chan)
+ return -ENOTCONN;
- return att->io_sec_level;
- }
-
- memset(&sec, 0, sizeof(sec));
- len = sizeof(sec);
- if (getsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0)
- return -EIO;
+ ret = bt_att_chan_get_security(chan);
+ if (ret < 0)
+ return ret;
if (enc_size)
*enc_size = att->enc_size;
- return sec.level;
+ return ret;
}
bool bt_att_set_security(struct bt_att *att, int level)
{
- struct bt_security sec;
+ struct bt_att_chan *chan;
if (!att || level < BT_ATT_SECURITY_AUTO ||
level > BT_ATT_SECURITY_HIGH)
return false;
- if (!att->io_on_l2cap) {
- att->io_sec_level = level;
- return true;
- }
+ chan = queue_peek_head(att->chans);
+ if (!chan)
+ return -ENOTCONN;
- memset(&sec, 0, sizeof(sec));
- sec.level = level;
-
- if (setsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec,
- sizeof(sec)) < 0)
- return false;
-
- return true;
+ return bt_att_chan_set_security(chan, level);
}
void bt_att_set_enc_key_size(struct bt_att *att, uint8_t enc_size)
diff --git a/src/shared/att.h b/src/shared/att.h
index 49d93269b..110700846 100644
--- a/src/shared/att.h
+++ b/src/shared/att.h
@@ -37,6 +37,10 @@ bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close);
int bt_att_get_fd(struct bt_att *att);
+int bt_att_attach_fd(struct bt_att *att, int fd);
+
+int bt_att_get_channels(struct bt_att *att);
+
typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data);
typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu,
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 29254cb61..3ce126485 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1909,7 +1909,7 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
* the MTU size is negotiated using L2CAP channel configuration
* procedures.
*/
- if (bt_att_get_link_type(client->att) == BT_ATT_LINK_BREDR)
+ if (bt_att_get_link_type(client->att) == BT_ATT_BREDR)
goto discover;
/* Check if MTU needs to be send */
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 05/12] shared/gatt-client: Add support for EATT features
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (3 preceding siblings ...)
2020-02-28 23:46 ` [PATCH v2 04/12] share/att: Add EATT support Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 06/12] gatt: Enable EATT bearer support Luiz Augusto von Dentz
` (7 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This enables EATT in the Client Features if the EATT characteristic is
present in the database.
---
attrib/gattrib.c | 5 +-
lib/uuid.h | 3 +
peripheral/gatt.c | 2 +-
src/device.c | 2 +-
src/shared/att.c | 271 ++++++++++++++++++++++++++-------------
src/shared/att.h | 14 +-
src/shared/gatt-client.c | 167 ++++++++++++++++++++++--
src/shared/gatt-client.h | 5 +-
src/shared/gatt-server.c | 205 ++++++++++++++++-------------
tools/btgatt-client.c | 2 +-
unit/test-gatt.c | 23 ++--
11 files changed, 494 insertions(+), 205 deletions(-)
diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 57ca01541..8aa0f5eff 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -275,8 +275,9 @@ static void attrib_callback_result(uint8_t opcode, const void *pdu,
free(buf);
}
-static void attrib_callback_notify(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void attrib_callback_notify(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
uint8_t *buf;
struct attrib_callbacks *cb = user_data;
diff --git a/lib/uuid.h b/lib/uuid.h
index fbc08f51e..ebdcf729c 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -154,6 +154,9 @@ extern "C" {
#define GATT_CHARAC_CLI_FEAT 0x2B29
#define GATT_CHARAC_DB_HASH 0x2B2A
+/* GATT Server Supported features */
+#define GATT_CHARAC_SERVER_FEAT 0x2B3A
+
typedef struct {
enum {
BT_UUID_UNSPEC = 0,
diff --git a/peripheral/gatt.c b/peripheral/gatt.c
index 08541c424..bbbf3f59f 100644
--- a/peripheral/gatt.c
+++ b/peripheral/gatt.c
@@ -136,7 +136,7 @@ static struct gatt_conn *gatt_conn_new(int fd)
return NULL;
}
- conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu);
+ conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu, 0);
if (!conn->gatt) {
fprintf(stderr, "Failed to create GATT client\n");
bt_gatt_server_unref(conn->gatt);
diff --git a/src/device.c b/src/device.c
index a8f4c22f3..3f4afa281 100644
--- a/src/device.c
+++ b/src/device.c
@@ -4939,7 +4939,7 @@ static void gatt_client_init(struct btd_device *device)
}
device->client = bt_gatt_client_new(device->db, device->att,
- device->att_mtu);
+ device->att_mtu, 0);
if (!device->client) {
DBG("Failed to initialize");
return;
diff --git a/src/shared/att.c b/src/shared/att.c
index 1313703f9..56ea40c46 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -56,6 +56,8 @@ struct bt_att_chan {
uint8_t type;
int sec_level; /* Only used for non-L2CAP */
+ struct queue *queue; /* Channel dedicated queue */
+
struct att_send_op *pending_req;
struct att_send_op *pending_ind;
bool writer_active;
@@ -375,32 +377,47 @@ static struct att_send_op *pick_next_send_op(struct bt_att_chan *chan)
struct bt_att *att = chan->att;
struct att_send_op *op;
- /* See if any operations are already in the write queue */
- op = queue_pop_head(att->write_queue);
+ /* Check if there is anything queued on the channel */
+ op = queue_pop_head(chan->queue);
if (op)
return op;
+ /* See if any operations are already in the write queue */
+ op = queue_peek_head(att->write_queue);
+ if (op && op->len <= chan->mtu)
+ return queue_pop_head(att->write_queue);
+
/* If there is no pending request, pick an operation from the
* request queue.
*/
if (!chan->pending_req) {
- op = queue_pop_head(att->req_queue);
- if (op)
- return op;
+ op = queue_peek_head(att->req_queue);
+ if (op && op->len <= chan->mtu)
+ return queue_pop_head(att->req_queue);
}
/* There is either a request pending or no requests queued. If there is
* no pending indication, pick an operation from the indication queue.
*/
if (!chan->pending_ind) {
- op = queue_pop_head(att->ind_queue);
- if (op)
- return op;
+ op = queue_peek_head(att->ind_queue);
+ if (op && op->len <= chan->mtu)
+ return queue_pop_head(att->ind_queue);
}
return NULL;
}
+static void disc_att_send_op(void *data)
+{
+ struct att_send_op *op = data;
+
+ if (op->callback)
+ op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data);
+
+ destroy_att_send_op(op);
+}
+
struct timeout_data {
struct bt_att_chan *chan;
unsigned int id;
@@ -425,13 +442,14 @@ static bool timeout_cb(void *user_data)
return false;
util_debug(att->debug_callback, att->debug_data,
- "Operation timed out: 0x%02x", op->opcode);
+ "(chan %p) Operation timed out: 0x%02x",
+ chan, op->opcode);
if (att->timeout_callback)
att->timeout_callback(op->id, op->opcode, att->timeout_data);
op->timeout_id = 0;
- destroy_att_send_op(op);
+ disc_att_send_op(op);
/*
* Directly terminate the connection as required by the ATT protocol.
@@ -450,39 +468,52 @@ static void write_watch_destroy(void *user_data)
chan->writer_active = false;
}
+static ssize_t bt_att_chan_write(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t len)
+{
+ struct bt_att *att = chan->att;
+ ssize_t ret;
+ struct iovec iov;
+
+ iov.iov_base = (void *) pdu;
+ iov.iov_len = len;
+
+ util_debug(att->debug_callback, att->debug_data,
+ "(chan %p) ATT op 0x%02x",
+ chan, opcode);
+
+ ret = io_send(chan->io, &iov, 1);
+ if (ret < 0) {
+ util_debug(att->debug_callback, att->debug_data,
+ "(chan %p) write failed: %s",
+ chan, strerror(-ret));
+
+ return ret;
+ }
+
+ util_hexdump('<', pdu, ret, att->debug_callback, att->debug_data);
+
+ return ret;
+}
+
static bool can_write_data(struct io *io, void *user_data)
{
struct bt_att_chan *chan = user_data;
- struct bt_att *att = chan->att;
struct att_send_op *op;
struct timeout_data *timeout;
- ssize_t ret;
- struct iovec iov;
op = pick_next_send_op(chan);
if (!op)
return false;
- iov.iov_base = op->pdu;
- iov.iov_len = op->len;
-
- ret = io_send(io, &iov, 1);
- if (ret < 0) {
- util_debug(att->debug_callback, att->debug_data,
- "write failed: %s", strerror(-ret));
+ if (!bt_att_chan_write(chan, op->opcode, op->pdu, op->len)) {
if (op->callback)
op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0,
op->user_data);
-
destroy_att_send_op(op);
return true;
}
- util_debug(att->debug_callback, att->debug_data,
- "ATT op 0x%02x", op->opcode);
-
- util_hexdump('<', op->pdu, ret, att->debug_callback, att->debug_data);
-
/* Based on the operation type, set either the pending request or the
* pending indication. If it came from the write queue, then there is
* no need to keep it around.
@@ -528,7 +559,7 @@ static void wakeup_chan_writer(void *data, void *user_data)
/* Set the write handler only if there is anything that can be sent
* at all.
*/
- if (queue_isempty(att->write_queue)) {
+ if (queue_isempty(chan->queue) && queue_isempty(att->write_queue)) {
if ((chan->pending_req || queue_isempty(att->req_queue)) &&
(chan->pending_ind || queue_isempty(att->ind_queue)))
return;
@@ -558,16 +589,6 @@ static void disconn_handler(void *data, void *user_data)
disconn->callback(err, disconn->user_data);
}
-static void disc_att_send_op(void *data)
-{
- struct att_send_op *op = data;
-
- if (op->callback)
- op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data);
-
- destroy_att_send_op(op);
-}
-
static void bt_att_chan_free(void *data)
{
struct bt_att_chan *chan = data;
@@ -578,6 +599,8 @@ static void bt_att_chan_free(void *data)
if (chan->pending_ind)
destroy_att_send_op(chan->pending_ind);
+ queue_destroy(chan->queue, destroy_att_send_op);
+
io_destroy(chan->io);
free(chan->buf);
@@ -595,8 +618,8 @@ static bool disconnect_cb(struct io *io, void *user_data)
if (getsockopt(chan->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
util_debug(chan->att->debug_callback, chan->att->debug_data,
- "Failed to obtain disconnect error: %s",
- strerror(errno));
+ "(chan %p) Failed to obtain disconnect"
+ " error: %s", chan, strerror(errno));
err = 0;
}
@@ -728,7 +751,8 @@ static bool handle_error_rsp(struct bt_att_chan *chan, uint8_t *pdu,
}
util_debug(att->debug_callback, att->debug_data,
- "Retrying operation %p", op);
+ "(chan %p) Retrying operation "
+ "%p", chan, op);
chan->pending_req = NULL;
@@ -752,7 +776,8 @@ static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu,
*/
if (!op) {
util_debug(att->debug_callback, att->debug_data,
- "Received unexpected ATT response");
+ "(chan %p) Received unexpected ATT "
+ "response", chan);
io_shutdown(chan->io);
return;
}
@@ -784,7 +809,8 @@ static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu,
fail:
util_debug(att->debug_callback, att->debug_data,
- "Failed to handle response PDU; opcode: 0x%02x", opcode);
+ "(chan %p) Failed to handle response PDU; opcode: "
+ "0x%02x", chan, opcode);
rsp_opcode = BT_ATT_OP_ERROR_RSP;
@@ -809,7 +835,8 @@ static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len)
*/
if (!op || pdu_len) {
util_debug(att->debug_callback, att->debug_data,
- "Received unexpected/invalid ATT confirmation");
+ "(chan %p) Received unexpected/invalid ATT "
+ "confirmation", chan);
io_shutdown(chan->io);
return;
}
@@ -935,7 +962,7 @@ static void handle_notify(struct bt_att_chan *chan, uint8_t opcode,
found = true;
if (notify->callback)
- notify->callback(opcode, pdu, pdu_len,
+ notify->callback(chan, opcode, pdu, pdu_len,
notify->user_data);
/* callback could remove all entries from notify list */
@@ -966,6 +993,10 @@ static bool can_read_data(struct io *io, void *user_data)
if (bytes_read < 0)
return false;
+ util_debug(att->debug_callback, att->debug_data,
+ "(chan %p) ATT received: %zd",
+ chan, bytes_read);
+
util_hexdump('>', chan->buf, bytes_read,
att->debug_callback, att->debug_data);
@@ -981,12 +1012,14 @@ static bool can_read_data(struct io *io, void *user_data)
switch (get_op_type(opcode)) {
case ATT_OP_TYPE_RSP:
util_debug(att->debug_callback, att->debug_data,
- "ATT response received: 0x%02x", opcode);
+ "(chan %p) ATT response received: 0x%02x",
+ chan, opcode);
handle_rsp(chan, opcode, pdu + 1, bytes_read - 1);
break;
case ATT_OP_TYPE_CONF:
util_debug(att->debug_callback, att->debug_data,
- "ATT confirmation received: 0x%02x", opcode);
+ "(chan %p) ATT confirmation received: 0x%02x",
+ chan, opcode);
handle_conf(chan, pdu + 1, bytes_read - 1);
break;
case ATT_OP_TYPE_REQ:
@@ -997,8 +1030,9 @@ static bool can_read_data(struct io *io, void *user_data)
*/
if (chan->in_req) {
util_debug(att->debug_callback, att->debug_data,
- "Received request while another is "
- "pending: 0x%02x", opcode);
+ "(chan %p) Received request while "
+ "another is pending: 0x%02x",
+ chan, opcode);
io_shutdown(chan->io);
bt_att_unref(chan->att);
@@ -1017,7 +1051,8 @@ static bool can_read_data(struct io *io, void *user_data)
* let them act on it.
*/
util_debug(att->debug_callback, att->debug_data,
- "ATT PDU received: 0x%02x", opcode);
+ "(chan %p) ATT PDU received: 0x%02x",
+ chan, opcode);
handle_notify(chan, opcode, pdu + 1, bytes_read - 1);
break;
}
@@ -1075,16 +1110,19 @@ static void bt_att_free(struct bt_att *att)
free(att);
}
-static uint16_t get_l2cap_mtu(int fd)
+static uint16_t io_get_mtu(int fd)
{
socklen_t len;
struct l2cap_options l2o;
len = sizeof(l2o);
- if (getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0)
- return 0;
+ if (!getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len))
+ return l2o.omtu;
- return l2o.omtu;
+ if (!getsockopt(fd, SOL_BLUETOOTH, BT_SNDMTU, &l2o.omtu, &len))
+ return l2o.omtu;
+
+ return 0;
}
static uint8_t io_get_type(int fd)
@@ -1135,7 +1173,7 @@ static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type)
chan->mtu = BT_ATT_DEFAULT_LE_MTU;
break;
default:
- chan->mtu = get_l2cap_mtu(chan->fd);
+ chan->mtu = io_get_mtu(chan->fd);
}
if (chan->mtu < BT_ATT_DEFAULT_LE_MTU)
@@ -1145,6 +1183,8 @@ static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type)
if (!chan->buf)
goto fail;
+ chan->queue = queue_new();
+
return chan;
fail:
@@ -1153,6 +1193,23 @@ fail:
return NULL;
}
+static void bt_att_attach_chan(struct bt_att *att, struct bt_att_chan *chan)
+{
+ /* Push to head as EATT channels have higher priority */
+ queue_push_head(att->chans, chan);
+ chan->att = att;
+
+ if (chan->mtu > att->mtu)
+ att->mtu = chan->mtu;
+
+ io_set_close_on_destroy(chan->io, att->close_on_unref);
+
+ util_debug(att->debug_callback, att->debug_data, "Channel %p attached",
+ chan);
+
+ wakeup_chan_writer(chan, NULL);
+}
+
struct bt_att *bt_att_new(int fd, bool ext_signed)
{
struct bt_att *att;
@@ -1166,9 +1223,6 @@ struct bt_att *bt_att_new(int fd, bool ext_signed)
att->chans = queue_new();
att->mtu = chan->mtu;
- queue_push_head(att->chans, chan);
- chan->att = att;
-
/* crypto is optional, if not available leave it NULL */
if (!ext_signed)
att->crypto = bt_crypto_new();
@@ -1179,6 +1233,8 @@ struct bt_att *bt_att_new(int fd, bool ext_signed)
att->notify_list = queue_new();
att->disconn_list = queue_new();
+ bt_att_attach_chan(att, chan);
+
return bt_att_ref(att);
}
@@ -1237,13 +1293,7 @@ int bt_att_attach_fd(struct bt_att *att, int fd)
if (!chan)
return -EINVAL;
- queue_push_tail(att->chans, chan);
- chan->att = att;
-
- if (chan->mtu > att->mtu)
- att->mtu = chan->mtu;
-
- io_set_close_on_destroy(chan->io, att->close_on_unref);
+ bt_att_attach_chan(att, chan);
return 0;
}
@@ -1258,7 +1308,7 @@ int bt_att_get_fd(struct bt_att *att)
if (queue_isempty(att->chans))
return -ENOTCONN;
- chan = queue_peek_head(att->chans);
+ chan = queue_peek_tail(att->chans);
return chan->fd;
}
@@ -1306,7 +1356,8 @@ bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu)
if (mtu < BT_ATT_DEFAULT_LE_MTU)
return false;
- chan = queue_peek_head(att->chans);
+ /* Original channel is always the last */
+ chan = queue_peek_tail(att->chans);
if (!chan)
return -ENOTCONN;
@@ -1332,7 +1383,7 @@ uint8_t bt_att_get_link_type(struct bt_att *att)
if (!att)
return -EINVAL;
- chan = queue_peek_head(att->chans);
+ chan = queue_peek_tail(att->chans);
if (!chan)
return -ENOTCONN;
@@ -1392,7 +1443,7 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id)
return false;
/* Check if disconnect is running */
- if (!queue_isempty(att->chans)) {
+ if (queue_isempty(att->chans)) {
disconn = queue_find(att->disconn_list, match_disconn_id,
UINT_TO_PTR(id));
if (!disconn)
@@ -1461,6 +1512,33 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
return op->id;
}
+unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t len,
+ bt_att_response_func_t callback,
+ void *user_data,
+ bt_att_destroy_func_t destroy)
+{
+ struct att_send_op *op;
+
+ if (!chan || !chan->att)
+ return -EINVAL;
+
+ op = create_att_send_op(chan->att, opcode, pdu, len, callback,
+ user_data, destroy);
+ if (!op)
+ return -EINVAL;
+
+ if (!queue_push_tail(chan->queue, op)) {
+ free(op->pdu);
+ free(op);
+ return 0;
+ }
+
+ wakeup_chan_writer(chan, NULL);
+
+ return op->id;
+}
+
static bool match_op_id(const void *a, const void *b)
{
const struct att_send_op *op = a;
@@ -1469,6 +1547,33 @@ static bool match_op_id(const void *a, const void *b)
return op->id == id;
}
+bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id)
+{
+ struct att_send_op *op;
+
+ if (chan->pending_req && chan->pending_req->id == id) {
+ /* Don't cancel the pending request; remove it's handlers */
+ cancel_att_send_op(chan->pending_req);
+ return true;
+ }
+
+ if (chan->pending_ind && chan->pending_ind->id == id) {
+ /* Don't cancel the pending indication; remove it's handlers. */
+ cancel_att_send_op(chan->pending_ind);
+ return true;
+ }
+
+ op = queue_remove_if(chan->queue, match_op_id, UINT_TO_PTR(id));
+ if (!op)
+ return false;
+
+ destroy_att_send_op(op);
+
+ wakeup_chan_writer(chan, NULL);
+
+ return true;
+}
+
bool bt_att_cancel(struct bt_att *att, unsigned int id)
{
const struct queue_entry *entry;
@@ -1477,25 +1582,13 @@ bool bt_att_cancel(struct bt_att *att, unsigned int id)
if (!att || !id)
return false;
+ /* Lookuo request on each channel first */
for (entry = queue_get_entries(att->chans); entry;
entry = entry->next) {
struct bt_att_chan *chan = entry->data;
- if (chan->pending_req && chan->pending_req->id == id) {
- /* Don't cancel the pending request; remove it's
- * handlers
- */
- cancel_att_send_op(chan->pending_req);
- return true;
- }
-
- if (chan->pending_ind && chan->pending_ind->id == id) {
- /* Don't cancel the pending indication; remove it's
- * handlers.
- */
- cancel_att_send_op(chan->pending_ind);
+ if (bt_att_chan_cancel(chan, id))
return true;
- }
}
op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id));
@@ -1580,14 +1673,14 @@ static uint8_t att_ecode_from_error(int err)
return BT_ATT_ERROR_UNLIKELY;
}
-unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
+int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode,
uint16_t handle, int error)
{
struct bt_att_pdu_error_rsp pdu;
uint8_t ecode;
- if (!att || !opcode)
- return 0;
+ if (!chan || !chan->att || !opcode)
+ return -EINVAL;
ecode = att_ecode_from_error(error);
@@ -1597,8 +1690,8 @@ unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
put_le16(handle, &pdu.handle);
pdu.ecode = ecode;
- return bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu),
- NULL, NULL, NULL);
+ return bt_att_chan_send_rsp(chan, BT_ATT_OP_ERROR_RSP, &pdu,
+ sizeof(pdu));
}
unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
@@ -1665,7 +1758,7 @@ int bt_att_get_security(struct bt_att *att, uint8_t *enc_size)
if (!att)
return -EINVAL;
- chan = queue_peek_head(att->chans);
+ chan = queue_peek_tail(att->chans);
if (!chan)
return -ENOTCONN;
@@ -1687,7 +1780,7 @@ bool bt_att_set_security(struct bt_att *att, int level)
level > BT_ATT_SECURITY_HIGH)
return false;
- chan = queue_peek_head(att->chans);
+ chan = queue_peek_tail(att->chans);
if (!chan)
return -ENOTCONN;
diff --git a/src/shared/att.h b/src/shared/att.h
index 110700846..ed20bb5b8 100644
--- a/src/shared/att.h
+++ b/src/shared/att.h
@@ -27,6 +27,7 @@
#include "src/shared/att-types.h"
struct bt_att;
+struct bt_att_chan;
struct bt_att *bt_att_new(int fd, bool ext_signed);
@@ -43,7 +44,8 @@ int bt_att_get_channels(struct bt_att *att);
typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data);
-typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu,
+typedef void (*bt_att_notify_func_t)(struct bt_att_chan *chan,
+ uint8_t opcode, const void *pdu,
uint16_t length, void *user_data);
typedef void (*bt_att_destroy_func_t)(void *user_data);
typedef void (*bt_att_debug_func_t)(const char *str, void *user_data);
@@ -68,10 +70,18 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
bt_att_response_func_t callback,
void *user_data,
bt_att_destroy_func_t destroy);
+unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t len,
+ bt_att_response_func_t callback,
+ void *user_data,
+ bt_att_destroy_func_t destroy);
+#define bt_att_chan_send_rsp(chan, opcode, pdu, len) \
+ bt_att_chan_send(chan, opcode, pdu, len, NULL, NULL, NULL)
+bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id);
bool bt_att_cancel(struct bt_att *att, unsigned int id);
bool bt_att_cancel_all(struct bt_att *att);
-unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
+int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode,
uint16_t handle, int error);
unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 3ce126485..2c5fe14dc 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -60,6 +60,7 @@ struct ready_cb {
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
+ uint8_t features;
struct bt_gatt_client *parent;
struct queue *clones;
@@ -326,6 +327,7 @@ struct discovery_op {
struct queue *ext_prop_desc;
struct gatt_db_attribute *cur_svc;
struct gatt_db_attribute *hash;
+ uint8_t server_feat;
bool success;
uint16_t start;
uint16_t end;
@@ -1278,6 +1280,9 @@ static void notify_client_ready(struct bt_gatt_client *client, bool success,
bt_gatt_client_ref(client);
client->ready = success;
+ if (client->parent)
+ client->features = client->parent->features;
+
for (entry = queue_get_entries(client->ready_cbs); entry;
entry = entry->next) {
struct ready_cb *ready = entry->data;
@@ -1381,7 +1386,7 @@ static void db_hash_read_cb(bool success, uint8_t att_ecode,
util_hexdump(' ', value, len, client->debug_callback,
client->debug_data);
- /* Store the new hash in the db */
+ /* Store ithe new hash in the db */
gatt_db_attribute_write(op->hash, 0, value, len, 0, NULL,
db_hash_write_value_cb, client);
@@ -1431,6 +1436,67 @@ static bool read_db_hash(struct discovery_op *op)
return true;
}
+static void db_server_feat_read(bool success, uint8_t att_ecode,
+ struct bt_gatt_result *result, void *user_data)
+{
+ struct discovery_op *op = user_data;
+ struct bt_gatt_client *client = op->client;
+ const uint8_t *value;
+ uint16_t len, handle;
+ struct bt_gatt_iter iter;
+
+ if (!result)
+ return;
+
+ bt_gatt_iter_init(&iter, result);
+ bt_gatt_iter_next_read_by_type(&iter, &handle, &len, &value);
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Server Features found: handle 0x%04x "
+ "length 0x%04x value 0x%02x", handle, len,
+ value[0]);
+
+ op->server_feat = value[0];
+}
+
+static void server_feat_read_value(struct gatt_db_attribute *attrib,
+ int err, const uint8_t *value,
+ size_t length, void *user_data)
+{
+ const uint8_t **feat = user_data;
+
+ if (err)
+ return;
+
+ *feat = value;
+}
+
+static void read_server_feat(struct discovery_op *op)
+{
+ struct bt_gatt_client *client = op->client;
+ struct gatt_db_attribute *attr = NULL;
+ const uint8_t *feat = NULL;
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
+
+ gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
+ get_first_attribute, &attr);
+ if (attr) {
+ /* Read stored value in the db */
+ gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL,
+ server_feat_read_value, &feat);
+ if (feat)
+ return;
+ }
+
+ if (!bt_gatt_read_by_type(client->att, 0x0001, 0xffff, &uuid,
+ db_server_feat_read,
+ discovery_op_ref(op),
+ discovery_op_unref))
+ discovery_op_unref(op);
+}
+
static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
{
struct discovery_op *op = user_data;
@@ -1464,6 +1530,8 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
bt_att_get_mtu(client->att));
discover:
+ read_server_feat(op);
+
if (read_db_hash(op)) {
op->success = false;
return;
@@ -1839,12 +1907,41 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
queue_push_tail(client->svc_chngd_queue, op);
}
+static void server_feat_write_value(struct gatt_db_attribute *attrib,
+ int err, void *user_data)
+{
+ struct bt_gatt_client *client = user_data;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Server Features Value set status: %d", err);
+}
+
+static void write_server_features(struct bt_gatt_client *client, uint8_t feat)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *attr = NULL;
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
+
+ gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
+ get_first_attribute, &attr);
+ if (!attr)
+ return;
+
+ /* Store value in the DB */
+ if (!gatt_db_attribute_write(attr, 0, &feat, sizeof(feat),
+ 0, NULL, server_feat_write_value,
+ client))
+ util_debug(client->debug_callback, client->debug_data,
+ "Unable to store Server Features");
+}
+
static void write_client_features(struct bt_gatt_client *client)
{
bt_uuid_t uuid;
struct gatt_db_attribute *attr = NULL;
uint16_t handle;
- uint8_t value;
+ const uint8_t *feat = NULL;
bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT);
@@ -1854,10 +1951,28 @@ static void write_client_features(struct bt_gatt_client *client)
return;
handle = gatt_db_attribute_get_handle(attr);
- value = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING;
- bt_gatt_client_write_value(client, handle, &value, sizeof(value), NULL,
- NULL, NULL);
+ client->features = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING;
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
+
+ attr = NULL;
+ gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
+ get_first_attribute, &attr);
+ if (attr) {
+ /* Read stored value in the db */
+ gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ,
+ NULL, server_feat_read_value,
+ &feat);
+ if (feat && feat[0] & BT_GATT_CHRC_SERVER_FEAT_EATT)
+ client->features |= BT_GATT_CHRC_CLI_FEAT_EATT;
+ }
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Writing Client Features 0x%02x", client->features);
+
+ bt_gatt_client_write_value(client, handle, &client->features,
+ sizeof(client->features), NULL, NULL, NULL);
}
static void init_complete(struct discovery_op *op, bool success,
@@ -1870,6 +1985,9 @@ static void init_complete(struct discovery_op *op, bool success,
if (!success)
goto fail;
+ if (op->server_feat)
+ write_server_features(client, op->server_feat);
+
write_client_features(client);
if (register_service_changed(client))
@@ -1932,6 +2050,8 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
return true;
discover:
+ read_server_feat(op);
+
if (read_db_hash(op)) {
op->success = false;
goto done;
@@ -2026,8 +2146,9 @@ static void notify_handler(void *data, void *user_data)
notify_data->user_data);
}
-static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
- void *user_data)
+static void notify_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_client *client = user_data;
struct pdu_data pdu_data;
@@ -2041,7 +2162,7 @@ static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
queue_foreach(client->notify_list, notify_handler, &pdu_data);
if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent)
- bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
+ bt_att_chan_send(chan, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
NULL, NULL, NULL);
bt_gatt_client_unref(client);
@@ -2099,7 +2220,8 @@ static void att_disconnect_cb(int err, void *user_data)
}
static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
- struct bt_att *att)
+ struct bt_att *att,
+ uint8_t features)
{
struct bt_gatt_client *client;
@@ -2129,6 +2251,7 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
client->att = bt_att_ref(att);
client->db = gatt_db_ref(db);
+ client->features = features;
return client;
@@ -2140,14 +2263,15 @@ fail:
struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
struct bt_att *att,
- uint16_t mtu)
+ uint16_t mtu,
+ uint8_t features)
{
struct bt_gatt_client *client;
if (!att || !db)
return NULL;
- client = gatt_client_new(db, att);
+ client = gatt_client_new(db, att, features);
if (!client)
return NULL;
@@ -2166,7 +2290,7 @@ struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client)
if (!client)
return NULL;
- clone = gatt_client_new(client->db, client->att);
+ clone = gatt_client_new(client->db, client->att, client->features);
if (!clone)
return NULL;
@@ -2284,6 +2408,14 @@ uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client)
return bt_att_get_mtu(client->att);
}
+struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client)
+{
+ if (!client)
+ return NULL;
+
+ return client->att;
+}
+
struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
{
if (!client || !client->db)
@@ -2292,6 +2424,17 @@ struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
return client->db;
}
+uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client)
+{
+ if (!client)
+ return 0;
+
+ if (client->parent)
+ return client->parent->features;
+
+ return client->features;
+}
+
static bool match_req_id(const void *a, const void *b)
{
const struct request *req = a;
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index 6d8bf8043..10900168b 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -31,7 +31,8 @@ struct bt_gatt_client;
struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
struct bt_att *att,
- uint16_t mtu);
+ uint16_t mtu,
+ uint8_t features);
struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client);
struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client);
@@ -73,7 +74,9 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
bt_gatt_client_destroy_func_t destroy);
uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client);
+struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client);
struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
+uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client);
bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id);
bool bt_gatt_client_cancel_all(struct bt_gatt_client *client);
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 0d9bb0762..ee0058486 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -52,6 +52,7 @@
#define DEFAULT_MAX_PREP_QUEUE_LEN 30
struct async_read_op {
+ struct bt_att_chan *chan;
struct bt_gatt_server *server;
uint8_t opcode;
bool done;
@@ -62,6 +63,7 @@ struct async_read_op {
};
struct async_write_op {
+ struct bt_att_chan *chan;
struct bt_gatt_server *server;
uint8_t opcode;
};
@@ -239,8 +241,9 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
return true;
}
-static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void read_by_grp_type_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t start, end;
@@ -308,15 +311,14 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
queue_destroy(q, NULL);
- bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP,
- rsp_pdu, rsp_len,
- NULL, NULL, NULL);
+ bt_att_chan_send_rsp(chan, BT_ATT_OP_READ_BY_GRP_TYPE_RSP,
+ rsp_pdu, rsp_len);
return;
error:
queue_destroy(q, NULL);
- bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
}
static void async_read_op_destroy(struct async_read_op *op)
@@ -350,7 +352,7 @@ static void read_by_type_read_complete_cb(struct gatt_db_attribute *attr,
/* Terminate the operation if there was an error */
if (err) {
- bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ,
handle, err);
async_read_op_destroy(op);
return;
@@ -451,10 +453,8 @@ static void process_read_by_type(struct async_read_op *op)
attr = queue_pop_head(op->db_data);
if (op->done || !attr) {
- bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu,
- op->pdu_len,
- NULL, NULL,
- NULL);
+ bt_att_chan_send_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_RSP,
+ op->pdu, op->pdu_len);
async_read_op_destroy(op);
return;
}
@@ -472,13 +472,14 @@ static void process_read_by_type(struct async_read_op *op)
ecode = BT_ATT_ERROR_UNLIKELY;
error:
- bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ,
gatt_db_attribute_get_handle(attr), ecode);
async_read_op_destroy(op);
}
-static void read_by_type_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void read_by_type_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t start, end;
@@ -535,6 +536,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu,
goto error;
}
+ op->chan = chan;
op->opcode = opcode;
op->server = server;
op->db_data = q;
@@ -545,7 +547,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu,
return;
error:
- bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
queue_destroy(q, NULL);
}
@@ -603,8 +605,9 @@ static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q,
return true;
}
-static void find_info_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void find_info_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t start, end;
@@ -653,14 +656,14 @@ static void find_info_cb(uint8_t opcode, const void *pdu,
goto error;
}
- bt_att_send(server->att, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len,
- NULL, NULL, NULL);
+ bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len);
+
queue_destroy(q, NULL);
return;
error:
- bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
queue_destroy(q, NULL);
}
@@ -702,8 +705,9 @@ static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib,
data->len += 4;
}
-static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void find_by_type_val_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t start, end, uuid16;
@@ -748,13 +752,13 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
if (data.ecode)
goto error;
- bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_RSP, data.pdu,
- data.len, NULL, NULL, NULL);
+ bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_BY_TYPE_RSP,
+ data.pdu, data.len);
return;
error:
- bt_att_send_error_rsp(server->att, opcode, ehandle, data.ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, ehandle, data.ecode);
}
static void async_write_op_destroy(struct async_write_op *op)
@@ -772,6 +776,9 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err,
struct bt_gatt_server *server = op->server;
uint16_t handle;
+ util_debug(server->debug_callback, server->debug_data,
+ "Write Complete: err %d", err);
+
if (!server || op->opcode == BT_ATT_OP_WRITE_CMD) {
async_write_op_destroy(op);
return;
@@ -780,10 +787,9 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err,
handle = gatt_db_attribute_get_handle(attr);
if (err)
- bt_att_send_error_rsp(server->att, op->opcode, handle, err);
+ bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err);
else
- bt_att_send(server->att, BT_ATT_OP_WRITE_RSP, NULL, 0,
- NULL, NULL, NULL);
+ bt_att_chan_send_rsp(op->chan, BT_ATT_OP_WRITE_RSP, NULL, 0);
async_write_op_destroy(op);
}
@@ -798,7 +804,7 @@ static uint8_t authorize_req(struct bt_gatt_server *server,
server->authorize_data);
}
-static void write_cb(uint8_t opcode, const void *pdu,
+static void write_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
struct bt_gatt_server *server = user_data;
@@ -840,6 +846,7 @@ static void write_cb(uint8_t opcode, const void *pdu,
}
op = new0(struct async_write_op, 1);
+ op->chan = chan;
op->server = server;
op->opcode = opcode;
server->pending_write_op = op;
@@ -857,7 +864,7 @@ error:
if (opcode == BT_ATT_OP_WRITE_CMD)
return;
- bt_att_send_error_rsp(server->att, opcode, handle, ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, handle, ecode);
}
static uint8_t get_read_rsp_opcode(uint8_t opcode)
@@ -893,6 +900,9 @@ static void read_complete_cb(struct gatt_db_attribute *attr, int err,
uint16_t mtu;
uint16_t handle;
+ util_debug(server->debug_callback, server->debug_data,
+ "Read Complete: err %d", err);
+
if (!server) {
async_read_op_destroy(op);
return;
@@ -902,22 +912,21 @@ static void read_complete_cb(struct gatt_db_attribute *attr, int err,
handle = gatt_db_attribute_get_handle(attr);
if (err) {
- bt_att_send_error_rsp(server->att, op->opcode, handle, err);
+ bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err);
async_read_op_destroy(op);
return;
}
rsp_opcode = get_read_rsp_opcode(op->opcode);
- bt_att_send(server->att, rsp_opcode, len ? value : NULL,
- MIN((unsigned) mtu - 1, len),
- NULL, NULL, NULL);
+ bt_att_chan_send_rsp(op->chan, rsp_opcode, len ? value : NULL,
+ MIN((unsigned int) mtu - 1, len));
async_read_op_destroy(op);
}
-static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
- uint16_t handle,
- uint16_t offset)
+static void handle_read_req(struct bt_att_chan *chan,
+ struct bt_gatt_server *server, uint8_t opcode,
+ uint16_t handle, uint16_t offset)
{
struct gatt_db_attribute *attr;
uint8_t ecode;
@@ -950,6 +959,7 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
}
op = new0(struct async_read_op, 1);
+ op->chan = chan;
op->opcode = opcode;
op->server = server;
server->pending_read_op = op;
@@ -964,34 +974,35 @@ error:
if (op)
async_read_op_destroy(op);
- bt_att_send_error_rsp(server->att, opcode, handle, ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, handle, ecode);
}
-static void read_cb(uint8_t opcode, const void *pdu,
+static void read_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t handle;
if (length != 2) {
- bt_att_send_error_rsp(server->att, opcode, 0,
+ bt_att_chan_send_error_rsp(chan, opcode, 0,
BT_ATT_ERROR_INVALID_PDU);
return;
}
handle = get_le16(pdu);
- handle_read_req(server, opcode, handle, 0);
+ handle_read_req(chan, server, opcode, handle, 0);
}
-static void read_blob_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void read_blob_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t handle, offset;
if (length != 4) {
- bt_att_send_error_rsp(server->att, opcode, 0,
+ bt_att_chan_send_error_rsp(chan, opcode, 0,
BT_ATT_ERROR_INVALID_PDU);
return;
}
@@ -999,10 +1010,11 @@ static void read_blob_cb(uint8_t opcode, const void *pdu,
handle = get_le16(pdu);
offset = get_le16(pdu + 2);
- handle_read_req(server, opcode, handle, offset);
+ handle_read_req(chan, server, opcode, handle, offset);
}
struct read_multiple_resp_data {
+ struct bt_att_chan *chan;
struct bt_gatt_server *server;
uint16_t *handles;
size_t cur_handle;
@@ -1029,7 +1041,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
uint8_t ecode;
if (err != 0) {
- bt_att_send_error_rsp(data->server->att,
+ bt_att_chan_send_error_rsp(data->chan,
BT_ATT_OP_READ_MULT_REQ, handle, err);
read_multiple_resp_data_free(data);
return;
@@ -1039,7 +1051,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
BT_ATT_PERM_READ_AUTHEN |
BT_ATT_PERM_READ_ENCRYPT);
if (ecode) {
- bt_att_send_error_rsp(data->server->att,
+ bt_att_chan_send_error_rsp(data->chan,
BT_ATT_OP_READ_MULT_REQ, handle, ecode);
read_multiple_resp_data_free(data);
return;
@@ -1054,8 +1066,8 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
if ((data->length >= data->mtu - 1) ||
(data->cur_handle == data->num_handles)) {
- bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP,
- data->rsp_data, data->length, NULL, NULL, NULL);
+ bt_att_chan_send_rsp(data->chan, BT_ATT_OP_READ_MULT_RSP,
+ data->rsp_data, data->length);
read_multiple_resp_data_free(data);
return;
}
@@ -1069,7 +1081,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
data->handles[data->cur_handle]);
if (!next_attr) {
- bt_att_send_error_rsp(data->server->att,
+ bt_att_chan_send_error_rsp(data->chan,
BT_ATT_OP_READ_MULT_REQ,
data->handles[data->cur_handle],
BT_ATT_ERROR_INVALID_HANDLE);
@@ -1080,7 +1092,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ,
data->server->att,
read_multiple_complete_cb, data)) {
- bt_att_send_error_rsp(data->server->att,
+ bt_att_chan_send_error_rsp(data->chan,
BT_ATT_OP_READ_MULT_REQ,
data->handles[data->cur_handle],
BT_ATT_ERROR_UNLIKELY);
@@ -1088,8 +1100,9 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
}
}
-static void read_multiple_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
struct gatt_db_attribute *attr;
@@ -1103,6 +1116,7 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu,
}
data = new0(struct read_multiple_resp_data, 1);
+ data->chan = chan;
data->handles = NULL;
data->rsp_data = NULL;
data->server = server;
@@ -1139,7 +1153,7 @@ error:
if (data)
read_multiple_resp_data_free(data);
- bt_att_send_error_rsp(server->att, opcode, 0, ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, 0, ecode);
}
static bool append_prep_data(struct prep_write_data *prep_data, uint16_t handle,
@@ -1230,6 +1244,7 @@ static bool store_prep_data(struct bt_gatt_server *server,
}
struct prep_write_complete_data {
+ struct bt_att_chan *chan;
void *pdu;
uint16_t length;
struct bt_gatt_server *server;
@@ -1245,8 +1260,8 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err,
handle = get_le16(pwcd->pdu);
if (err) {
- bt_att_send_error_rsp(pwcd->server->att,
- BT_ATT_OP_PREP_WRITE_REQ, handle, err);
+ bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_REQ,
+ handle, err);
free(pwcd->pdu);
free(pwcd);
@@ -1257,19 +1272,20 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err,
if (!store_prep_data(pwcd->server, handle, offset, pwcd->length - 4,
&((uint8_t *) pwcd->pdu)[4]))
- bt_att_send_error_rsp(pwcd->server->att,
- BT_ATT_OP_PREP_WRITE_RSP, handle,
+ bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP,
+ handle,
BT_ATT_ERROR_INSUFFICIENT_RESOURCES);
- bt_att_send(pwcd->server->att, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu,
- pwcd->length, NULL, NULL, NULL);
+ bt_att_chan_send_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu,
+ pwcd->length);
free(pwcd->pdu);
free(pwcd);
}
-static void prep_write_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void prep_write_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t handle = 0;
@@ -1307,6 +1323,7 @@ static void prep_write_cb(uint8_t opcode, const void *pdu,
goto error;
pwcd = new0(struct prep_write_complete_data, 1);
+ pwcd->chan = chan;
pwcd->pdu = malloc(length);
memcpy(pwcd->pdu, pdu, length);
pwcd->length = length;
@@ -1323,23 +1340,28 @@ static void prep_write_cb(uint8_t opcode, const void *pdu,
ecode = BT_ATT_ERROR_UNLIKELY;
error:
- bt_att_send_error_rsp(server->att, opcode, handle, ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, handle, ecode);
}
-static void exec_next_prep_write(struct bt_gatt_server *server,
- uint16_t ehandle, int err);
+struct exec_data {
+ struct bt_att_chan *chan;
+ struct bt_gatt_server *server;
+};
+
+static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle,
+ int err);
static void exec_write_complete_cb(struct gatt_db_attribute *attr, int err,
void *user_data)
{
- struct bt_gatt_server *server = user_data;
+ struct exec_data *data = user_data;
uint16_t handle = gatt_db_attribute_get_handle(attr);
- exec_next_prep_write(server, handle, err);
+ exec_next_prep_write(data, handle, err);
}
-static void exec_next_prep_write(struct bt_gatt_server *server,
- uint16_t ehandle, int err)
+static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle,
+ int err)
{
struct prep_write_data *next = NULL;
struct gatt_db_attribute *attr;
@@ -1348,14 +1370,15 @@ static void exec_next_prep_write(struct bt_gatt_server *server,
if (err)
goto error;
- next = queue_pop_head(server->prep_queue);
+ next = queue_pop_head(data->server->prep_queue);
if (!next) {
- bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0,
- NULL, NULL, NULL);
+ bt_att_chan_send_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_RSP,
+ NULL, 0);
+ free(data);
return;
}
- attr = gatt_db_get_attribute(server->db, next->handle);
+ attr = gatt_db_get_attribute(data->server->db, next->handle);
if (!attr) {
err = BT_ATT_ERROR_UNLIKELY;
goto error;
@@ -1364,8 +1387,8 @@ static void exec_next_prep_write(struct bt_gatt_server *server,
status = gatt_db_attribute_write(attr, next->offset,
next->value, next->length,
BT_ATT_OP_EXEC_WRITE_REQ,
- server->att,
- exec_write_complete_cb, server);
+ data->server->att,
+ exec_write_complete_cb, data);
prep_write_data_destroy(next);
@@ -1375,11 +1398,12 @@ static void exec_next_prep_write(struct bt_gatt_server *server,
err = BT_ATT_ERROR_UNLIKELY;
error:
- queue_remove_all(server->prep_queue, NULL, NULL,
+ queue_remove_all(data->server->prep_queue, NULL, NULL,
prep_write_data_destroy);
- bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_REQ,
ehandle, err);
+ free(data);
}
static bool find_no_reliable_characteristic(const void *data,
@@ -1390,10 +1414,12 @@ static bool find_no_reliable_characteristic(const void *data,
return !prep_data->reliable_supported;
}
-static void exec_write_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void exec_write_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
+ struct exec_data *data;
uint8_t flags;
uint8_t ecode;
bool write;
@@ -1421,8 +1447,7 @@ static void exec_write_cb(uint8_t opcode, const void *pdu,
if (!write) {
queue_remove_all(server->prep_queue, NULL, NULL,
prep_write_data_destroy);
- bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0,
- NULL, NULL, NULL);
+ bt_att_chan_send_rsp(chan, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0);
return;
}
@@ -1439,18 +1464,23 @@ static void exec_write_cb(uint8_t opcode, const void *pdu,
}
}
- exec_next_prep_write(server, 0, 0);
+ data = new0(struct exec_data, 1);
+ data->chan = chan;
+ data->server = server;
+
+ exec_next_prep_write(data, 0, 0);
return;
error:
queue_remove_all(server->prep_queue, NULL, NULL,
prep_write_data_destroy);
- bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
+ bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
}
-static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
- uint16_t length, void *user_data)
+static void exchange_mtu_cb(struct bt_att_chan *chan, uint8_t opcode,
+ const void *pdu, uint16_t length,
+ void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t client_rx_mtu;
@@ -1458,7 +1488,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
uint8_t rsp_pdu[2];
if (length != 2) {
- bt_att_send_error_rsp(server->att, opcode, 0,
+ bt_att_chan_send_error_rsp(chan, opcode, 0,
BT_ATT_ERROR_INVALID_PDU);
return;
}
@@ -1468,8 +1498,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
/* Respond with the server MTU */
put_le16(server->mtu, rsp_pdu);
- bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL,
- NULL);
+ bt_att_chan_send_rsp(chan, BT_ATT_OP_MTU_RSP, rsp_pdu, 2);
/* Set MTU to be the minimum */
server->mtu = final_mtu;
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 7df659747..82a9e3fe0 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -218,7 +218,7 @@ static struct client *client_create(int fd, uint16_t mtu)
return NULL;
}
- cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu);
+ cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0);
if (!cli->gatt) {
fprintf(stderr, "Failed to create GATT client\n");
gatt_db_unref(cli->db);
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index e35271b61..d94993b9c 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -124,8 +124,16 @@ struct context {
raw_pdu(0x02, 0x00, 0x02), \
raw_pdu(0x03, 0x00, 0x02)
-#define SERVICE_DATA_1_PDUS \
+#define READ_SERVER_FEAT_PDUS \
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x3a, 0x2b), \
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a)
+
+#define CLIENT_INIT_PDUS \
MTU_EXCHANGE_CLIENT_PDUS, \
+ READ_SERVER_FEAT_PDUS
+
+#define SERVICE_DATA_1_PDUS \
+ CLIENT_INIT_PDUS, \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \
@@ -150,7 +158,7 @@ struct context {
raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
#define SERVICE_DATA_2_PDUS \
- MTU_EXCHANGE_CLIENT_PDUS, \
+ CLIENT_INIT_PDUS, \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \
@@ -175,7 +183,7 @@ struct context {
raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x01, 0x29)
#define SERVICE_DATA_3_PDUS \
- MTU_EXCHANGE_CLIENT_PDUS, \
+ CLIENT_INIT_PDUS, \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
raw_pdu(0x11, 0x06, 0x00, 0x01, 0x21, 0x01, 0x00, 0x18, \
0x00, 0x02, 0x00, 0x02, 0x01, 0x18), \
@@ -683,7 +691,7 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
g_assert(context->client_db);
context->client = bt_gatt_client_new(context->client_db,
- context->att, mtu);
+ context->att, mtu, 0);
g_assert(context->client);
bt_gatt_client_set_debug(context->client, print_debug,
@@ -2371,8 +2379,7 @@ int main(int argc, char *argv[])
* Discovery of Services and Service Characteristics.
*/
define_test_att("/TP/GAD/CL/BV-01-C", test_search_primary, NULL, NULL,
- raw_pdu(0x02, 0x00, 0x02),
- raw_pdu(0x03, 0x00, 0x02),
+ MTU_EXCHANGE_CLIENT_PDUS,
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
raw_pdu(0x11, 0x06, 0x10, 0x00, 0x13, 0x00, 0x00, 0x18,
0x20, 0x00, 0x29, 0x00, 0xb0, 0x68,
@@ -3245,7 +3252,7 @@ int main(int argc, char *argv[])
define_test_client("/TP/GAN/CL/BV-01-C", test_client, ts_small_db,
&test_notification_1,
- MTU_EXCHANGE_CLIENT_PDUS,
+ CLIENT_INIT_PDUS,
SMALL_DB_DISCOVERY_PDUS,
raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00),
raw_pdu(0x13),
@@ -3271,7 +3278,7 @@ int main(int argc, char *argv[])
define_test_client("/TP/GAI/CL/BV-01-C", test_client, ts_small_db,
&test_indication_1,
- MTU_EXCHANGE_CLIENT_PDUS,
+ CLIENT_INIT_PDUS,
SMALL_DB_DISCOVERY_PDUS,
raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00),
raw_pdu(0x13),
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 06/12] gatt: Enable EATT bearer support
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (4 preceding siblings ...)
2020-02-28 23:46 ` [PATCH v2 05/12] shared/gatt-client: Add support for EATT features Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 07/12] shared/gatt-server: Add support for Read Multiple Variable Length Luiz Augusto von Dentz
` (6 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for EATT connections.
---
src/device.c | 11 +++++
src/gatt-client.c | 83 ++++++++++++++++++++++++++++++++++
src/gatt-database.c | 107 +++++++++++++++++++++++++++++++++-----------
3 files changed, 175 insertions(+), 26 deletions(-)
diff --git a/src/device.c b/src/device.c
index 3f4afa281..c86e9a64d 100644
--- a/src/device.c
+++ b/src/device.c
@@ -5048,6 +5048,17 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
return false;
}
+ if (dev->att) {
+ if (!bt_att_attach_fd(dev->att, g_io_channel_unix_get_fd(io))) {
+ DBG("EATT channel connected");
+ g_io_channel_set_close_on_unref(io, FALSE);
+ return true;
+ }
+
+ error("Failed to attach EATT channel");
+ return false;
+ }
+
if (sec_level == BT_IO_SEC_LOW && dev->le_state.paired) {
DBG("Elevating security level since LTK is available");
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 6bcdecf09..aa77661ad 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -35,9 +35,11 @@
#include "lib/uuid.h"
#include "gdbus/gdbus.h"
+#include "btio/btio.h"
#include "log.h"
#include "error.h"
+#include "hcid.h"
#include "adapter.h"
#include "device.h"
#include "src/shared/io.h"
@@ -57,8 +59,11 @@
#define GATT_CHARACTERISTIC_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"
+#define EATT_MAX_BEARERS 2
+
struct btd_gatt_client {
struct btd_device *device;
+ uint8_t features;
bool ready;
char devaddr[18];
struct gatt_db *db;
@@ -2154,6 +2159,70 @@ static void register_notify(void *data, void *user_data)
notify_client_free(notify_client);
}
+static void eatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ struct btd_gatt_client *client = user_data;
+
+ if (gerr)
+ return;
+
+ device_attach_att(client->device, io);
+}
+
+static void eatt_connect(struct btd_gatt_client *client)
+{
+ struct btd_device *dev = client->device;
+ struct btd_adapter *adapter = device_get_adapter(dev);
+ GIOChannel *io;
+ GError *gerr = NULL;
+ char addr[18];
+ int i;
+
+ ba2str(device_get_address(dev), addr);
+
+ DBG("Connection attempt to: %s", addr);
+
+ for (i = 0; i < EATT_MAX_BEARERS; i++) {
+ io = bt_io_connect(eatt_connect_cb, client, NULL, NULL,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(adapter),
+ BT_IO_OPT_SOURCE_TYPE,
+ btd_adapter_get_address_type(adapter),
+ BT_IO_OPT_DEST_BDADDR, device_get_address(dev),
+ BT_IO_OPT_DEST_TYPE,
+ device_get_le_address_type(dev),
+ BT_IO_OPT_MODE, BT_IO_MODE_EXT_FLOWCTL,
+ BT_IO_OPT_PSM, BT_ATT_EATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_MTU, main_opts.gatt_mtu,
+ BT_IO_OPT_INVALID);
+ if (io == NULL) {
+ /* Fallback to regular LE mode */
+ io = bt_io_connect(eatt_connect_cb, client, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(adapter),
+ BT_IO_OPT_SOURCE_TYPE,
+ btd_adapter_get_address_type(adapter),
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(dev),
+ BT_IO_OPT_DEST_TYPE,
+ device_get_le_address_type(dev),
+ BT_IO_OPT_PSM, BT_ATT_EATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_MTU, main_opts.gatt_mtu,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("EATT bt_io_connect(%s): %s", addr,
+ gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+ }
+
+ g_io_channel_unref(io);
+ }
+}
+
void btd_gatt_client_ready(struct btd_gatt_client *client)
{
if (!client)
@@ -2175,6 +2244,15 @@ void btd_gatt_client_ready(struct btd_gatt_client *client)
DBG("GATT client ready");
create_services(client);
+
+ DBG("Features 0x%02x", client->features);
+
+ if (!client->features) {
+ client->features = bt_gatt_client_get_features(client->gatt);
+ DBG("Update Features 0x%02x", client->features);
+ if (client->features & BT_GATT_CHRC_CLI_FEAT_EATT)
+ eatt_connect(client);
+ }
}
void btd_gatt_client_connected(struct btd_gatt_client *client)
@@ -2197,6 +2275,11 @@ void btd_gatt_client_connected(struct btd_gatt_client *client)
* for any pre-registered notification sessions.
*/
queue_foreach(client->all_notify_clients, register_notify, client);
+
+ if (!(client->features & BT_GATT_CHRC_CLI_FEAT_EATT))
+ return;
+
+ eatt_connect(client);
}
void btd_gatt_client_service_added(struct btd_gatt_client *client,
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 419e4f9e1..2445d1fa5 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -48,14 +48,6 @@
#include "profile.h"
#include "service.h"
-#ifndef ATT_CID
-#define ATT_CID 4
-#endif
-
-#ifndef ATT_PSM
-#define ATT_PSM 31
-#endif
-
#define GATT_MANAGER_IFACE "org.bluez.GattManager1"
#define GATT_PROFILE_IFACE "org.bluez.GattProfile1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
@@ -80,7 +72,8 @@ struct btd_gatt_database {
struct gatt_db *db;
unsigned int db_id;
GIOChannel *le_io;
- GIOChannel *l2cap_io;
+ GIOChannel *eatt_io;
+ GIOChannel *bredr_io;
struct queue *records;
struct queue *device_states;
struct queue *ccc_callbacks;
@@ -88,6 +81,7 @@ struct btd_gatt_database {
struct gatt_db_attribute *svc_chngd_ccc;
struct gatt_db_attribute *cli_feat;
struct gatt_db_attribute *db_hash;
+ struct gatt_db_attribute *eatt;
struct queue *apps;
struct queue *profiles;
};
@@ -594,9 +588,14 @@ static void gatt_database_free(void *data)
g_io_channel_unref(database->le_io);
}
- if (database->l2cap_io) {
- g_io_channel_shutdown(database->l2cap_io, FALSE, NULL);
- g_io_channel_unref(database->l2cap_io);
+ if (database->eatt_io) {
+ g_io_channel_shutdown(database->eatt_io, FALSE, NULL);
+ g_io_channel_unref(database->eatt_io);
+ }
+
+ if (database->bredr_io) {
+ g_io_channel_shutdown(database->bredr_io, FALSE, NULL);
+ g_io_channel_unref(database->bredr_io);
}
/* TODO: Persistently store CCC states before freeing them */
@@ -717,7 +716,7 @@ static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end)
uuid_t root_uuid, proto_uuid, l2cap;
sdp_record_t *record;
sdp_data_t *psm, *sh, *eh;
- uint16_t lp = ATT_PSM;
+ uint16_t lp = BT_ATT_PSM;
if (uuid == NULL)
return NULL;
@@ -1098,7 +1097,10 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib,
{
struct btd_gatt_database *database = user_data;
struct device_state *state;
+ uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING,
+ BT_GATT_CHRC_CLI_FEAT_EATT };
uint8_t ecode = 0;
+ unsigned int i;
DBG("Client Features write");
@@ -1113,13 +1115,12 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib,
goto done;
}
- /* A client shall never clear a bit it has set.
- * TODO: make it generic to any bits.
- */
- if (state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING &&
- !(value[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING)) {
- ecode = BT_ATT_ERROR_VALUE_NOT_ALLOWED;
- goto done;
+ for (i = 0; i < sizeof(bits); i++) {
+ /* A client shall never clear a bit it has set */
+ if (state->cli_feat[0] & (1 << i) && !(value[0] & (1 << i))) {
+ ecode = BT_ATT_ERROR_VALUE_NOT_ALLOWED;
+ goto done;
+ }
}
/* Shall we reallocate the feat array if bigger? */
@@ -1129,7 +1130,7 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib,
len--;
}
- state->cli_feat[0] &= BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING;
+ state->cli_feat[0] &= ((1 << sizeof(bits)) - 1);
state->change_aware = true;
done:
@@ -1161,6 +1162,28 @@ static void db_hash_read_cb(struct gatt_db_attribute *attrib,
state->change_aware = true;
}
+static void server_feat_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ struct device_state *state;
+ uint8_t ecode = 0;
+ uint8_t value = 0;
+
+ state = get_device_state(database, att);
+ if (!state) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ value |= BT_GATT_CHRC_SERVER_FEAT_EATT;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, ecode, &value, sizeof(value));
+}
+
static void populate_gatt_service(struct btd_gatt_database *database)
{
bt_uuid_t uuid;
@@ -1168,7 +1191,7 @@ static void populate_gatt_service(struct btd_gatt_database *database)
/* Add the GATT service */
bt_uuid16_create(&uuid, UUID_GATT);
- service = gatt_db_add_service(database->db, &uuid, true, 8);
+ service = gatt_db_add_service(database->db, &uuid, true, 10);
bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
@@ -1191,6 +1214,11 @@ static void populate_gatt_service(struct btd_gatt_database *database)
&uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
db_hash_read_cb, NULL, database);
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
+ database->eatt = gatt_db_service_add_characteristic(service,
+ &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+ server_feat_read_cb, NULL, database);
+
gatt_db_service_set_active(service, true);
database_add_record(database, service);
@@ -3525,7 +3553,7 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
BT_IO_OPT_SOURCE_BDADDR, addr,
BT_IO_OPT_SOURCE_TYPE,
btd_adapter_get_address_type(adapter),
- BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_CID, BT_ATT_CID,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
BT_IO_OPT_INVALID);
if (!database->le_io) {
@@ -3534,14 +3562,41 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
goto fail;
}
+ /* EATT socket */
+ database->eatt_io = bt_io_listen(connect_cb, NULL, NULL, NULL, NULL,
+ BT_IO_OPT_SOURCE_BDADDR, addr,
+ BT_IO_OPT_SOURCE_TYPE,
+ btd_adapter_get_address_type(adapter),
+ BT_IO_OPT_MODE, BT_IO_MODE_EXT_FLOWCTL,
+ BT_IO_OPT_PSM, BT_ATT_EATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_MTU, main_opts.gatt_mtu,
+ BT_IO_OPT_INVALID);
+ if (!database->eatt_io) {
+ /* Fallback to regular LE mode */
+ database->eatt_io = bt_io_listen(connect_cb, NULL, NULL, NULL,
+ &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, addr,
+ BT_IO_OPT_SOURCE_TYPE,
+ btd_adapter_get_address_type(adapter),
+ BT_IO_OPT_PSM, BT_ATT_EATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_MTU, main_opts.gatt_mtu,
+ BT_IO_OPT_INVALID);
+ if (!database->eatt_io) {
+ g_error_free(gerr);
+ goto fail;
+ }
+ }
+
/* BR/EDR socket */
- database->l2cap_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
+ database->bredr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, addr,
- BT_IO_OPT_PSM, ATT_PSM,
+ BT_IO_OPT_PSM, BT_ATT_PSM,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_MTU, main_opts.gatt_mtu,
BT_IO_OPT_INVALID);
- if (database->l2cap_io == NULL) {
+ if (database->bredr_io == NULL) {
error("Failed to start listening: %s", gerr->message);
g_error_free(gerr);
goto fail;
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 07/12] shared/gatt-server: Add support for Read Multiple Variable Length
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (5 preceding siblings ...)
2020-02-28 23:46 ` [PATCH v2 06/12] gatt: Enable EATT bearer support Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 08/12] shared/gatt-client: " Luiz Augusto von Dentz
` (5 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The Read Multiple Variable Length Request is used to request that the
server read two or more values of a set of attributes that have a
variable or unknown value length and return their values in a
Read Multiple Variable Length Response.
---
src/shared/att-types.h | 2 +
src/shared/gatt-server.c | 113 ++++++++++++++++++++++++++-------------
2 files changed, 79 insertions(+), 36 deletions(-)
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index 7b88e7d92..cc9cc9fd6 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -75,6 +75,8 @@
#define BT_ATT_OP_HANDLE_VAL_NOT 0x1B
#define BT_ATT_OP_HANDLE_VAL_IND 0x1D
#define BT_ATT_OP_HANDLE_VAL_CONF 0x1E
+#define BT_ATT_OP_READ_MULT_VL_REQ 0x20
+#define BT_ATT_OP_READ_MULT_VL_RSP 0x21
/* Packed struct definitions for ATT protocol PDUs */
/* TODO: Complete these definitions for all opcodes */
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index ee0058486..8b18cb21f 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -102,6 +102,7 @@ struct bt_gatt_server {
unsigned int read_id;
unsigned int read_blob_id;
unsigned int read_multiple_id;
+ unsigned int read_multiple_vl_id;
unsigned int prep_write_id;
unsigned int exec_write_id;
@@ -136,6 +137,7 @@ static void bt_gatt_server_free(struct bt_gatt_server *server)
bt_att_unregister(server->att, server->read_id);
bt_att_unregister(server->att, server->read_blob_id);
bt_att_unregister(server->att, server->read_multiple_id);
+ bt_att_unregister(server->att, server->read_multiple_vl_id);
bt_att_unregister(server->att, server->prep_write_id);
bt_att_unregister(server->att, server->exec_write_id);
@@ -1013,9 +1015,10 @@ static void read_blob_cb(struct bt_att_chan *chan, uint8_t opcode,
handle_read_req(chan, server, opcode, handle, offset);
}
-struct read_multiple_resp_data {
+struct read_mult_data {
struct bt_att_chan *chan;
struct bt_gatt_server *server;
+ uint8_t opcode;
uint16_t *handles;
size_t cur_handle;
size_t num_handles;
@@ -1024,7 +1027,7 @@ struct read_multiple_resp_data {
size_t mtu;
};
-static void read_multiple_resp_data_free(struct read_multiple_resp_data *data)
+static void read_mult_data_free(struct read_mult_data *data)
{
free(data->handles);
free(data->rsp_data);
@@ -1035,15 +1038,16 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
const uint8_t *value, size_t len,
void *user_data)
{
- struct read_multiple_resp_data *data = user_data;
+ struct read_mult_data *data = user_data;
struct gatt_db_attribute *next_attr;
uint16_t handle = gatt_db_attribute_get_handle(attr);
uint8_t ecode;
+ uint16_t length;
if (err != 0) {
- bt_att_chan_send_error_rsp(data->chan,
- BT_ATT_OP_READ_MULT_REQ, handle, err);
- read_multiple_resp_data_free(data);
+ bt_att_chan_send_error_rsp(data->chan, data->opcode, handle,
+ err);
+ read_mult_data_free(data);
return;
}
@@ -1051,29 +1055,45 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
BT_ATT_PERM_READ_AUTHEN |
BT_ATT_PERM_READ_ENCRYPT);
if (ecode) {
- bt_att_chan_send_error_rsp(data->chan,
- BT_ATT_OP_READ_MULT_REQ, handle, ecode);
- read_multiple_resp_data_free(data);
+ bt_att_chan_send_error_rsp(data->chan, data->opcode, handle,
+ ecode);
+ read_mult_data_free(data);
return;
}
- len = MIN(len, data->mtu - data->length - 1);
+ length = data->opcode == BT_ATT_OP_READ_MULT_VL_REQ ?
+ MIN(len, data->mtu - data->length - 3) :
+ MIN(len, data->mtu - data->length - 1);
- memcpy(data->rsp_data + data->length, value, len);
- data->length += len;
+ if (data->opcode == BT_ATT_OP_READ_MULT_VL_REQ) {
+ /* 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.
+ */
+ put_le16(len, data->rsp_data + data->length);
+ data->length += 2;
+ }
+
+ memcpy(data->rsp_data + data->length, value, length);
+ data->length += length;
data->cur_handle++;
- if ((data->length >= data->mtu - 1) ||
- (data->cur_handle == data->num_handles)) {
- bt_att_chan_send_rsp(data->chan, BT_ATT_OP_READ_MULT_RSP,
+ len = data->opcode == BT_ATT_OP_READ_MULT_VL_REQ ?
+ data->mtu - data->length - 3 : data->mtu - data->length - 1;
+
+ if (!len || (data->cur_handle == data->num_handles)) {
+ bt_att_chan_send_rsp(data->chan, data->opcode + 1,
data->rsp_data, data->length);
- read_multiple_resp_data_free(data);
+ read_mult_data_free(data);
return;
}
util_debug(data->server->debug_callback, data->server->debug_data,
- "Read Multiple Req - #%zu of %zu: 0x%04x",
+ "%s Req - #%zu of %zu: 0x%04x",
+ data->opcode == BT_ATT_OP_READ_MULT_REQ ?
+ "Read Multiple" :
+ "Read Multiple Variable Length",
data->cur_handle + 1, data->num_handles,
data->handles[data->cur_handle]);
@@ -1085,7 +1105,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
BT_ATT_OP_READ_MULT_REQ,
data->handles[data->cur_handle],
BT_ATT_ERROR_INVALID_HANDLE);
- read_multiple_resp_data_free(data);
+ read_mult_data_free(data);
return;
}
@@ -1096,17 +1116,39 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
BT_ATT_OP_READ_MULT_REQ,
data->handles[data->cur_handle],
BT_ATT_ERROR_UNLIKELY);
- read_multiple_resp_data_free(data);
+ read_mult_data_free(data);
}
}
+static struct read_mult_data *read_mult_data_new(struct bt_gatt_server *server,
+ struct bt_att_chan *chan,
+ uint8_t opcode,
+ uint16_t num_handles)
+{
+ struct read_mult_data *data;
+
+ data = new0(struct read_mult_data, 1);
+ data->chan = chan;
+ data->opcode = opcode;
+ data->handles = new0(uint16_t, num_handles);
+ data->rsp_data = NULL;
+ data->server = server;
+ data->num_handles = num_handles;
+ data->cur_handle = 0;
+ data->mtu = bt_att_get_mtu(server->att);
+ data->length = 0;
+ data->rsp_data = new0(uint8_t, data->mtu - 1);
+
+ return data;
+}
+
static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode,
const void *pdu, uint16_t length,
void *user_data)
{
struct bt_gatt_server *server = user_data;
struct gatt_db_attribute *attr;
- struct read_multiple_resp_data *data = NULL;
+ struct read_mult_data *data = NULL;
uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
size_t i = 0;
@@ -1115,27 +1157,17 @@ static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode,
goto error;
}
- data = new0(struct read_multiple_resp_data, 1);
- data->chan = chan;
- data->handles = NULL;
- data->rsp_data = NULL;
- data->server = server;
- data->num_handles = length / 2;
- data->cur_handle = 0;
- data->mtu = bt_att_get_mtu(server->att);
- data->length = 0;
- data->rsp_data = malloc(data->mtu - 1);
-
- if (!data->rsp_data)
+ data = read_mult_data_new(server, chan, opcode, length / 2);
+ if (!data)
goto error;
- data->handles = new0(uint16_t, data->num_handles);
-
for (i = 0; i < data->num_handles; i++)
data->handles[i] = get_le16(pdu + i * 2);
util_debug(server->debug_callback, server->debug_data,
- "Read Multiple Req - %zu handles, 1st: 0x%04x",
+ "%s Req - %zu handles, 1st: 0x%04x",
+ data->opcode == BT_ATT_OP_READ_MULT_REQ ?
+ "Read Multiple" : "Read Multiple Variable Length",
data->num_handles, data->handles[0]);
attr = gatt_db_get_attribute(server->db, data->handles[0]);
@@ -1151,7 +1183,7 @@ static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode,
error:
if (data)
- read_multiple_resp_data_free(data);
+ read_mult_data_free(data);
bt_att_chan_send_error_rsp(chan, opcode, 0, ecode);
}
@@ -1588,6 +1620,15 @@ static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
if (!server->read_multiple_id)
return false;
+ /* Read Multiple Variable Length Request */
+ server->read_multiple_vl_id = bt_att_register(server->att,
+ BT_ATT_OP_READ_MULT_VL_REQ,
+ read_multiple_cb,
+ server, NULL);
+
+ if (!server->read_multiple_vl_id)
+ return false;
+
/* Prepare Write Request */
server->prep_write_id = bt_att_register(server->att,
BT_ATT_OP_PREP_WRITE_REQ,
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 08/12] shared/gatt-client: Add support for Read Multiple Variable Length
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (6 preceding siblings ...)
2020-02-28 23:46 ` [PATCH v2 07/12] shared/gatt-server: Add support for Read Multiple Variable Length Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 09/12] shared/gatt: Add support for Handle Value Multiple Notifications Luiz Augusto von Dentz
` (4 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The Read Multiple Variable Length Request is used to request that the
server read two or more values of a set of attributes that have a
variable or unknown value length and return their values in a
Read Multiple Variable Length Response.
---
src/shared/gatt-client.c | 42 ++++++++++++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 4 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 2c5fe14dc..5b6723f97 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -2642,7 +2642,9 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length,
uint8_t att_ecode;
bool success;
- if (opcode != BT_ATT_OP_READ_MULT_RSP || (!pdu && length)) {
+ if ((opcode != BT_ATT_OP_READ_MULT_RSP &&
+ opcode != BT_ATT_OP_READ_MULT_VL_RSP) ||
+ (!pdu && length)) {
success = false;
if (opcode == BT_ATT_OP_ERROR_RSP)
@@ -2657,8 +2659,36 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length,
att_ecode = 0;
}
- if (op->callback)
+ if (!op->callback)
+ return;
+
+ if (opcode == BT_ATT_OP_READ_MULT_RSP || att_ecode) {
op->callback(success, att_ecode, pdu, length, op->user_data);
+ return;
+ }
+
+ if (length < 2) {
+ op->callback(success, att_ecode, pdu, length, op->user_data);
+ return;
+ }
+
+ /* Parse response */
+ while (length >= 2) {
+ uint16_t len;
+
+ len = get_le16(pdu);
+ length -= 2;
+ pdu += 2;
+
+ /* 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 (len > length)
+ length = len;
+
+ op->callback(success, att_ecode, pdu, len, op->user_data);
+ }
}
unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
@@ -2670,6 +2700,7 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
uint8_t pdu[num_handles * 2];
struct request *req;
struct read_op *op;
+ uint8_t opcode;
int i;
if (!client)
@@ -2699,8 +2730,11 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
for (i = 0; i < num_handles; i++)
put_le16(handles[i], pdu + (2 * i));
- req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_MULT_REQ,
- pdu, sizeof(pdu),
+ opcode = bt_gatt_client_get_features(client) &
+ BT_GATT_CHRC_CLI_FEAT_EATT ? BT_ATT_OP_READ_MULT_VL_REQ :
+ BT_ATT_OP_READ_MULT_REQ;
+
+ req->att_id = bt_att_send(client->att, opcode, pdu, sizeof(pdu),
read_multiple_cb, req,
request_unref);
if (!req->att_id) {
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 09/12] shared/gatt: Add support for Handle Value Multiple Notifications
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (7 preceding siblings ...)
2020-02-28 23:46 ` [PATCH v2 08/12] shared/gatt-client: " Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 10/12] gatt: Add support for Notify Multiple Luiz Augusto von Dentz
` (3 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Handle Value Multiple Notification can be used to notify multiple
values at once.
---
src/shared/att-types.h | 7 ++--
src/shared/att.c | 16 ++++-----
src/shared/gatt-client.c | 76 ++++++++++++++++++++++++++--------------
src/shared/gatt-server.c | 4 +--
4 files changed, 64 insertions(+), 39 deletions(-)
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index cc9cc9fd6..99b108990 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -72,11 +72,12 @@
#define BT_ATT_OP_PREP_WRITE_RSP 0x17
#define BT_ATT_OP_EXEC_WRITE_REQ 0x18
#define BT_ATT_OP_EXEC_WRITE_RSP 0x19
-#define BT_ATT_OP_HANDLE_VAL_NOT 0x1B
-#define BT_ATT_OP_HANDLE_VAL_IND 0x1D
-#define BT_ATT_OP_HANDLE_VAL_CONF 0x1E
+#define BT_ATT_OP_HANDLE_NFY 0x1B
+#define BT_ATT_OP_HANDLE_IND 0x1D
+#define BT_ATT_OP_HANDLE_CONF 0x1E
#define BT_ATT_OP_READ_MULT_VL_REQ 0x20
#define BT_ATT_OP_READ_MULT_VL_RSP 0x21
+#define BT_ATT_OP_HANDLE_NFY_MULT 0x23
/* Packed struct definitions for ATT protocol PDUs */
/* TODO: Complete these definitions for all opcodes */
diff --git a/src/shared/att.c b/src/shared/att.c
index 56ea40c46..6fa16e422 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -110,7 +110,7 @@ enum att_op_type {
ATT_OP_TYPE_RSP,
ATT_OP_TYPE_CMD,
ATT_OP_TYPE_IND,
- ATT_OP_TYPE_NOT,
+ ATT_OP_TYPE_NFY,
ATT_OP_TYPE_CONF,
ATT_OP_TYPE_UNKNOWN,
};
@@ -144,9 +144,9 @@ static const struct {
{ BT_ATT_OP_PREP_WRITE_RSP, ATT_OP_TYPE_RSP },
{ BT_ATT_OP_EXEC_WRITE_REQ, ATT_OP_TYPE_REQ },
{ BT_ATT_OP_EXEC_WRITE_RSP, ATT_OP_TYPE_RSP },
- { BT_ATT_OP_HANDLE_VAL_NOT, ATT_OP_TYPE_NOT },
- { BT_ATT_OP_HANDLE_VAL_IND, ATT_OP_TYPE_IND },
- { BT_ATT_OP_HANDLE_VAL_CONF, ATT_OP_TYPE_CONF },
+ { BT_ATT_OP_HANDLE_NFY, ATT_OP_TYPE_NFY },
+ { BT_ATT_OP_HANDLE_IND, ATT_OP_TYPE_IND },
+ { BT_ATT_OP_HANDLE_CONF, ATT_OP_TYPE_CONF },
{ }
};
@@ -530,7 +530,7 @@ static bool can_write_data(struct io *io, void *user_data)
chan->in_req = false;
/* fall through */
case ATT_OP_TYPE_CMD:
- case ATT_OP_TYPE_NOT:
+ case ATT_OP_TYPE_NFY:
case ATT_OP_TYPE_CONF:
case ATT_OP_TYPE_UNKNOWN:
default:
@@ -842,7 +842,7 @@ static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len)
}
if (op->callback)
- op->callback(BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, op->user_data);
+ op->callback(BT_ATT_OP_HANDLE_NFY, NULL, 0, op->user_data);
destroy_att_send_op(op);
chan->pending_ind = NULL;
@@ -1042,7 +1042,7 @@ static bool can_read_data(struct io *io, void *user_data)
chan->in_req = true;
/* fall through */
case ATT_OP_TYPE_CMD:
- case ATT_OP_TYPE_NOT:
+ case ATT_OP_TYPE_NFY:
case ATT_OP_TYPE_UNKNOWN:
case ATT_OP_TYPE_IND:
/* fall through */
@@ -1492,7 +1492,7 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
result = queue_push_tail(att->ind_queue, op);
break;
case ATT_OP_TYPE_CMD:
- case ATT_OP_TYPE_NOT:
+ case ATT_OP_TYPE_NFY:
case ATT_OP_TYPE_UNKNOWN:
case ATT_OP_TYPE_RSP:
case ATT_OP_TYPE_CONF:
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 5b6723f97..7381f96e0 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -95,7 +95,7 @@ struct bt_gatt_client {
struct queue *notify_list;
struct queue *notify_chrcs;
int next_reg_id;
- unsigned int disc_id, notify_id, ind_id;
+ unsigned int disc_id, nfy_id, nfy_mult_id, ind_id;
/*
* Handles of the GATT Service and the Service Changed characteristic
@@ -2072,9 +2072,10 @@ done:
return true;
}
-struct pdu_data {
- const void *pdu;
- uint16_t length;
+struct value_data {
+ uint16_t handle;
+ uint16_t len;
+ const void *data;
};
static void disable_ccc_callback(uint8_t opcode, const void *pdu,
@@ -2125,25 +2126,18 @@ done:
static void notify_handler(void *data, void *user_data)
{
struct notify_data *notify_data = data;
- struct pdu_data *pdu_data = user_data;
- uint16_t value_handle;
- const uint8_t *value = NULL;
-
- value_handle = get_le16(pdu_data->pdu);
+ struct value_data *value_data = user_data;
- if (notify_data->chrc->value_handle != value_handle)
+ if (notify_data->chrc->value_handle != value_data->handle)
return;
- if (pdu_data->length > 2)
- value = pdu_data->pdu + 2;
-
/*
* Even if the notify data has a pending ATT request to write to the
* CCC, there is really no reason not to notify the handlers.
*/
if (notify_data->notify)
- notify_data->notify(value_handle, value, pdu_data->length - 2,
- notify_data->user_data);
+ notify_data->notify(value_data->handle, value_data->data,
+ value_data->len, notify_data->user_data);
}
static void notify_cb(struct bt_att_chan *chan, uint8_t opcode,
@@ -2151,18 +2145,42 @@ static void notify_cb(struct bt_att_chan *chan, uint8_t opcode,
void *user_data)
{
struct bt_gatt_client *client = user_data;
- struct pdu_data pdu_data;
+ struct value_data data;
bt_gatt_client_ref(client);
- memset(&pdu_data, 0, sizeof(pdu_data));
- pdu_data.pdu = pdu;
- pdu_data.length = length;
+ memset(&data, 0, sizeof(data));
+
+ if (opcode == BT_ATT_OP_HANDLE_NFY_MULT) {
+ while (length >= 4) {
+ data.handle = get_le16(pdu);
+ length -= 2;
+ pdu += 2;
+
+ data.len = get_le16(pdu);
+ length -= 2;
+ pdu += 2;
+
+ data.data = pdu;
+
+ queue_foreach(client->notify_list, notify_handler,
+ &data);
- queue_foreach(client->notify_list, notify_handler, &pdu_data);
+ length -= data.len;
+ }
+ } else {
+ data.handle = get_le16(pdu);
+ length -= 2;
+ pdu += 2;
+
+ data.len = length;
+ data.data = pdu;
- if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent)
- bt_att_chan_send(chan, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
+ queue_foreach(client->notify_list, notify_handler, &data);
+ }
+
+ if (opcode == BT_ATT_OP_HANDLE_IND && !client->parent)
+ bt_att_chan_send(chan, BT_ATT_OP_HANDLE_CONF, NULL, 0,
NULL, NULL, NULL);
bt_gatt_client_unref(client);
@@ -2181,7 +2199,8 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
if (client->att) {
bt_att_unregister_disconnect(client->att, client->disc_id);
- bt_att_unregister(client->att, client->notify_id);
+ bt_att_unregister(client->att, client->nfy_id);
+ bt_att_unregister(client->att, client->nfy_mult_id);
bt_att_unregister(client->att, client->ind_id);
bt_att_unref(client->att);
}
@@ -2239,12 +2258,17 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
client->notify_chrcs = queue_new();
client->pending_requests = queue_new();
- client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
+ client->nfy_id = bt_att_register(att, BT_ATT_OP_HANDLE_NFY,
+ notify_cb, client, NULL);
+ if (!client->nfy_id)
+ goto fail;
+
+ client->nfy_mult_id = bt_att_register(att, BT_ATT_OP_HANDLE_NFY_MULT,
notify_cb, client, NULL);
- if (!client->notify_id)
+ if (!client->nfy_mult_id)
goto fail;
- client->ind_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_IND,
+ client->ind_id = bt_att_register(att, BT_ATT_OP_HANDLE_IND,
notify_cb, client, NULL);
if (!client->ind_id)
goto fail;
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 8b18cb21f..8e81d6e0e 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -1745,7 +1745,7 @@ bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
put_le16(handle, pdu);
memcpy(pdu + 2, value, pdu_len - 2);
- result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_NOT, pdu,
+ result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY, pdu,
pdu_len, NULL, NULL, NULL);
free(pdu);
@@ -1806,7 +1806,7 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
put_le16(handle, pdu);
memcpy(pdu + 2, value, pdu_len - 2);
- result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_IND, pdu,
+ result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_IND, pdu,
pdu_len, conf_cb,
data, destroy_ind_data);
if (!result)
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 10/12] gatt: Add support for Notify Multiple
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (8 preceding siblings ...)
2020-02-28 23:46 ` [PATCH v2 09/12] shared/gatt: Add support for Handle Value Multiple Notifications Luiz Augusto von Dentz
@ 2020-02-28 23:46 ` Luiz Augusto von Dentz
2020-02-28 23:47 ` [PATCH v2 11/12] core: Add support for setting the number of GATT bearers Luiz Augusto von Dentz
` (2 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:46 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for Notify Multiple procedure marking its bit as
supported in the Client Features.
---
src/gatt-database.c | 8 +++--
src/shared/att.c | 3 +-
src/shared/gatt-client.c | 2 ++
src/shared/gatt-server.c | 77 +++++++++++++++++++++++++++++++++-------
src/shared/gatt-server.h | 2 +-
tools/btgatt-server.c | 4 +--
unit/test-gatt.c | 2 +-
7 files changed, 78 insertions(+), 20 deletions(-)
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 2445d1fa5..1b514aa4f 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -1011,7 +1011,7 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
if (ccc_cb->callback) {
struct pending_op *op;
- op = pending_ccc_new(att, attrib, get_le16(value),
+ op = pending_ccc_new(att, attrib, val,
bt_att_get_link_type(att));
if (!op) {
ecode = BT_ATT_ERROR_UNLIKELY;
@@ -1098,7 +1098,8 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib,
struct btd_gatt_database *database = user_data;
struct device_state *state;
uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING,
- BT_GATT_CHRC_CLI_FEAT_EATT };
+ BT_GATT_CHRC_CLI_FEAT_EATT,
+ BT_GATT_CHRC_CLI_FEAT_NFY_MULTI };
uint8_t ecode = 0;
unsigned int i;
@@ -1330,7 +1331,8 @@ static void send_notification_to_device(void *data, void *user_data)
DBG("GATT server sending notification");
bt_gatt_server_send_notification(server,
notify->handle, notify->value,
- notify->len);
+ notify->len, device_state->cli_feat[0] &
+ BT_GATT_CHRC_CLI_FEAT_NFY_MULTI);
return;
}
diff --git a/src/shared/att.c b/src/shared/att.c
index 6fa16e422..948a5548b 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -145,6 +145,7 @@ static const struct {
{ BT_ATT_OP_EXEC_WRITE_REQ, ATT_OP_TYPE_REQ },
{ BT_ATT_OP_EXEC_WRITE_RSP, ATT_OP_TYPE_RSP },
{ BT_ATT_OP_HANDLE_NFY, ATT_OP_TYPE_NFY },
+ { BT_ATT_OP_HANDLE_NFY_MULT, ATT_OP_TYPE_NFY },
{ BT_ATT_OP_HANDLE_IND, ATT_OP_TYPE_IND },
{ BT_ATT_OP_HANDLE_CONF, ATT_OP_TYPE_CONF },
{ }
@@ -842,7 +843,7 @@ static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len)
}
if (op->callback)
- op->callback(BT_ATT_OP_HANDLE_NFY, NULL, 0, op->user_data);
+ op->callback(BT_ATT_OP_HANDLE_CONF, NULL, 0, op->user_data);
destroy_att_send_op(op);
chan->pending_ind = NULL;
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 7381f96e0..963ad619f 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1968,6 +1968,8 @@ static void write_client_features(struct bt_gatt_client *client)
client->features |= BT_GATT_CHRC_CLI_FEAT_EATT;
}
+ client->features |= BT_GATT_CHRC_CLI_FEAT_NFY_MULTI;
+
util_debug(client->debug_callback, client->debug_data,
"Writing Client Features 0x%02x", client->features);
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 8e81d6e0e..7e5d652e4 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -36,6 +36,7 @@
#include "src/shared/gatt-server.h"
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
+#include "src/shared/timeout.h"
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
@@ -51,6 +52,8 @@
*/
#define DEFAULT_MAX_PREP_QUEUE_LEN 30
+#define NFY_MULT_TIMEOUT 10
+
struct async_read_op {
struct bt_att_chan *chan;
struct bt_gatt_server *server;
@@ -86,6 +89,13 @@ static void prep_write_data_destroy(void *user_data)
free(data);
}
+struct nfy_mult_data {
+ unsigned int id;
+ uint8_t *pdu;
+ uint16_t offset;
+ uint16_t len;
+};
+
struct bt_gatt_server {
struct gatt_db *db;
struct bt_att *att;
@@ -120,6 +130,8 @@ struct bt_gatt_server {
bt_gatt_server_authorize_cb_t authorize;
void *authorize_data;
+
+ struct nfy_mult_data *nfy_mult;
};
static void bt_gatt_server_free(struct bt_gatt_server *server)
@@ -1726,28 +1738,69 @@ bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
return true;
}
+static bool notify_multiple(void *user_data)
+{
+ struct bt_gatt_server *server = user_data;
+
+ bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY_MULT,
+ server->nfy_mult->pdu, server->nfy_mult->offset, NULL,
+ NULL, NULL);
+
+ free(server->nfy_mult->pdu);
+ free(server->nfy_mult);
+ server->nfy_mult = NULL;
+
+ return false;
+}
+
bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
uint16_t handle, const uint8_t *value,
- uint16_t length)
+ uint16_t length, bool multiple)
{
- uint16_t pdu_len;
- uint8_t *pdu;
+ struct nfy_mult_data *data = NULL;
bool result;
if (!server || (length && !value))
return false;
- pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2);
- pdu = malloc(pdu_len);
- if (!pdu)
- return false;
+ if (multiple)
+ data = server->nfy_mult;
- put_le16(handle, pdu);
- memcpy(pdu + 2, value, pdu_len - 2);
+ if (!data) {
+ data = new0(struct nfy_mult_data, 1);
+ data->len = bt_att_get_mtu(server->att) - 1;
+ data->pdu = malloc(data->len);
+ }
- result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY, pdu,
- pdu_len, NULL, NULL, NULL);
- free(pdu);
+ put_le16(handle, data->pdu + data->offset);
+ data->offset += 2;
+
+ length = MIN(data->len - data->offset, length);
+
+ if (multiple) {
+ put_le16(length, data->pdu + data->offset);
+ data->offset += 2;
+ }
+
+ memcpy(data->pdu + data->offset, value, length);
+ data->offset += length;
+
+ if (multiple) {
+ if (!server->nfy_mult)
+ server->nfy_mult = data;
+
+ if (!server->nfy_mult->id)
+ server->nfy_mult->id = timeout_add(NFY_MULT_TIMEOUT,
+ notify_multiple, server,
+ NULL);
+
+ return true;
+ }
+
+ result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY,
+ data->pdu, data->offset, NULL, NULL, NULL);
+ free(data->pdu);
+ free(data);
return result;
}
diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h
index c3d83f225..a2492d275 100644
--- a/src/shared/gatt-server.h
+++ b/src/shared/gatt-server.h
@@ -52,7 +52,7 @@ bool bt_gatt_server_set_authorize(struct bt_gatt_server *server,
bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
uint16_t handle, const uint8_t *value,
- uint16_t length);
+ uint16_t length, bool multiple);
bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
uint16_t handle, const uint8_t *value,
diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c
index d9d96e691..5b7857b00 100644
--- a/tools/btgatt-server.c
+++ b/tools/btgatt-server.c
@@ -305,7 +305,7 @@ static bool hr_msrmt_cb(void *user_data)
bt_gatt_server_send_notification(server->gatt,
server->hr_msrmt_handle,
- pdu, len);
+ pdu, len, false);
cur_ee = server->hr_energy_expended;
@@ -831,7 +831,7 @@ static void cmd_notify(struct server *server, char *cmd_str)
conf_cb, NULL, NULL))
printf("Failed to initiate indication\n");
} else if (!bt_gatt_server_send_notification(server->gatt, handle,
- value, length))
+ value, length, false))
printf("Failed to initiate notification\n");
done:
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index d94993b9c..36dd2847c 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -2271,7 +2271,7 @@ static void test_server_notification(struct context *context)
const struct test_step *step = context->data->step;
bt_gatt_server_send_notification(context->server, step->handle,
- step->value, step->length);
+ step->value, step->length, false);
}
static const struct test_step test_notification_server_1 = {
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 11/12] core: Add support for setting the number of GATT bearers
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (9 preceding siblings ...)
2020-02-28 23:46 ` [PATCH v2 10/12] gatt: Add support for Notify Multiple Luiz Augusto von Dentz
@ 2020-02-28 23:47 ` Luiz Augusto von Dentz
2020-02-28 23:47 ` [PATCH v2 12/12] monitor: Add support for decoding EATT Luiz Augusto von Dentz
2020-03-01 2:49 ` [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Marcel Holtmann
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:47 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds option to set the numbers of GATT Channels/Bearers to be
connected in main.conf.
---
src/device.c | 5 +++++
src/gatt-client.c | 8 +++++---
src/gatt-database.c | 12 ++++++++++--
src/hcid.h | 1 +
src/main.c | 14 ++++++++++++++
src/main.conf | 5 +++++
6 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/src/device.c b/src/device.c
index c86e9a64d..7e3c2b215 100644
--- a/src/device.c
+++ b/src/device.c
@@ -5049,6 +5049,11 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
}
if (dev->att) {
+ if (main_opts.gatt_channels == bt_att_get_channels(dev->att)) {
+ DBG("EATT channel limit reached");
+ return false;
+ }
+
if (!bt_att_attach_fd(dev->att, g_io_channel_unix_get_fd(io))) {
DBG("EATT channel connected");
g_io_channel_set_close_on_unref(io, FALSE);
diff --git a/src/gatt-client.c b/src/gatt-client.c
index aa77661ad..958ce5f9b 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -59,8 +59,6 @@
#define GATT_CHARACTERISTIC_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"
-#define EATT_MAX_BEARERS 2
-
struct btd_gatt_client {
struct btd_device *device;
uint8_t features;
@@ -2171,6 +2169,7 @@ static void eatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
static void eatt_connect(struct btd_gatt_client *client)
{
+ struct bt_att *att = bt_gatt_client_get_att(client->gatt);
struct btd_device *dev = client->device;
struct btd_adapter *adapter = device_get_adapter(dev);
GIOChannel *io;
@@ -2178,11 +2177,14 @@ static void eatt_connect(struct btd_gatt_client *client)
char addr[18];
int i;
+ if (bt_att_get_channels(att) == main_opts.gatt_channels)
+ return;
+
ba2str(device_get_address(dev), addr);
DBG("Connection attempt to: %s", addr);
- for (i = 0; i < EATT_MAX_BEARERS; i++) {
+ for (i = bt_att_get_channels(att); i < main_opts.gatt_channels; i++) {
io = bt_io_connect(eatt_connect_cb, client, NULL, NULL,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(adapter),
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 1b514aa4f..d0ab4da99 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -1215,10 +1215,13 @@ static void populate_gatt_service(struct btd_gatt_database *database)
&uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
db_hash_read_cb, NULL, database);
- bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
- database->eatt = gatt_db_service_add_characteristic(service,
+ /* Only enable EATT if there is a socket listening */
+ if (database->eatt_io) {
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
+ database->eatt = gatt_db_service_add_characteristic(service,
&uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
server_feat_read_cb, NULL, database);
+ }
gatt_db_service_set_active(service, true);
@@ -3564,6 +3567,10 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
goto fail;
}
+ /* If just just 1 channel is enabled EATT is not required */
+ if (main_opts.gatt_channels == 1)
+ goto bredr;
+
/* EATT socket */
database->eatt_io = bt_io_listen(connect_cb, NULL, NULL, NULL, NULL,
BT_IO_OPT_SOURCE_BDADDR, addr,
@@ -3591,6 +3598,7 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
}
}
+bredr:
/* BR/EDR socket */
database->bredr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, addr,
diff --git a/src/hcid.h b/src/hcid.h
index ca405fde4..1bf93784d 100644
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -62,6 +62,7 @@ struct main_opts {
bt_mode_t mode;
bt_gatt_cache_t gatt_cache;
uint16_t gatt_mtu;
+ uint8_t gatt_channels;
uint8_t key_size;
diff --git a/src/main.c b/src/main.c
index fc8c869fc..7b927ac79 100644
--- a/src/main.c
+++ b/src/main.c
@@ -109,6 +109,7 @@ static const char *gatt_options[] = {
"Cache",
"KeySize",
"ExchangeMTU",
+ "EATTChannels",
NULL
};
@@ -471,6 +472,18 @@ static void parse_config(GKeyFile *config)
DBG("ExchangeMTU=%d", val);
main_opts.gatt_mtu = val;
}
+
+ val = g_key_file_get_integer(config, "GATT", "Channels", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("Channels=%d", val);
+ /* Ensure the channels is within a valid range. */
+ val = MIN(val, 5);
+ val = MAX(val, 1);
+ main_opts.gatt_channels = val;
+ }
}
static void init_defaults(void)
@@ -497,6 +510,7 @@ static void init_defaults(void)
main_opts.gatt_cache = BT_GATT_CACHE_ALWAYS;
main_opts.gatt_mtu = BT_ATT_MAX_LE_MTU;
+ main_opts.gatt_channels = 3;
}
static void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
diff --git a/src/main.conf b/src/main.conf
index bb5ff5b15..16701ebe4 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -99,6 +99,11 @@
# Defaults to 517
#ExchangeMTU = 517
+# Number of ATT channels
+# Possible values: 1-5 (1 disables EATT)
+# Default to 3
+#Channels = 3
+
[Policy]
#
# The ReconnectUUIDs defines the set of remote services that should try
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 12/12] monitor: Add support for decoding EATT
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (10 preceding siblings ...)
2020-02-28 23:47 ` [PATCH v2 11/12] core: Add support for setting the number of GATT bearers Luiz Augusto von Dentz
@ 2020-02-28 23:47 ` Luiz Augusto von Dentz
2020-03-01 2:49 ` [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Marcel Holtmann
12 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-02-28 23:47 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This decodes packets received over EATT PSM.
---
monitor/l2cap.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/monitor/l2cap.c b/monitor/l2cap.c
index 3b2b25f24..9409604c3 100644
--- a/monitor/l2cap.c
+++ b/monitor/l2cap.c
@@ -2573,6 +2573,36 @@ static void att_handle_value_conf(const struct l2cap_frame *frame)
{
}
+static void att_multiple_vl_rsp(const struct l2cap_frame *frame)
+{
+ struct l2cap_frame *f = (void *) frame;
+
+ while (frame->size) {
+ uint16_t handle;
+ uint16_t len;
+
+ if (!l2cap_frame_get_le16(f, &handle))
+ return;
+
+ print_field("Handle: 0x%4.4x", handle);
+
+ if (!l2cap_frame_get_le16(f, &len))
+ return;
+
+ print_field("Length: 0x%4.4x", len);
+
+ print_hex_field(" Data", f->data,
+ len < f->size ? len : f->size);
+
+ if (len > f->size) {
+ print_text(COLOR_ERROR, "invalid size");
+ return;
+ }
+
+ l2cap_frame_pull(f, f, len);
+ }
+}
+
static void att_write_command(const struct l2cap_frame *frame)
{
print_field("Handle: 0x%4.4x", get_le16(frame->data));
@@ -2645,6 +2675,12 @@ static const struct att_opcode_data att_opcode_table[] = {
att_handle_value_ind, 2, false },
{ 0x1e, "Handle Value Confirmation",
att_handle_value_conf, 0, true },
+ { 0x20, "Read Multiple Request Variable Length",
+ att_read_multiple_req, 4, false },
+ { 0x21, "Read Multiple Response Variable Length",
+ att_multiple_vl_rsp, 4, false },
+ { 0x23, "Handle Multiple Value Notification",
+ att_multiple_vl_rsp, 4, false },
{ 0x52, "Write Command",
att_write_command, 2, false },
{ 0xd2, "Signed Write Command", att_signed_write_command, 14, false },
@@ -3287,6 +3323,9 @@ void l2cap_frame(uint16_t index, bool in, uint16_t handle, uint16_t cid,
case 0x001f:
att_packet(index, in, handle, cid, data, size);
break;
+ case 0x0027:
+ att_packet(index, in, handle, cid, data + 2, size - 2);
+ break;
case 0x0017:
case 0x001B:
avctp_packet(&frame);
--
2.21.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
` (11 preceding siblings ...)
2020-02-28 23:47 ` [PATCH v2 12/12] monitor: Add support for decoding EATT Luiz Augusto von Dentz
@ 2020-03-01 2:49 ` Marcel Holtmann
2020-03-02 22:48 ` Luiz Augusto von Dentz
12 siblings, 1 reply; 15+ messages in thread
From: Marcel Holtmann @ 2020-03-01 2:49 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Hi Luiz,
> This introduces the initial support for Bluetooth 5.2 features:
>
> * ISO channels:
> + tools/isotest command line tool to run validation tests
>
> * L2CAP Enhanced Credit Based Flow Control Mode
> + tools/l2test has been update to include the new mode
I prefer if we not merge these two just yet. Let this settle a bit first and figure out what the best userspace API is. Otherwise we are stuck with an API that doesn’t work.
> * Enhanced ATT Bearer:
> + Client and Server support
> + Include all new procedures
> + Automaticlly detects and enables channels
> + Number of channels configurable via main.conf
This looks ok to be merged.
Regards
Marcel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support
2020-03-01 2:49 ` [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Marcel Holtmann
@ 2020-03-02 22:48 ` Luiz Augusto von Dentz
0 siblings, 0 replies; 15+ messages in thread
From: Luiz Augusto von Dentz @ 2020-03-02 22:48 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth
Hi,
On Sat, Feb 29, 2020 at 6:49 PM Marcel Holtmann <marcel@holtmann.org> wrote:
>
> Hi Luiz,
>
> > This introduces the initial support for Bluetooth 5.2 features:
> >
> > * ISO channels:
> > + tools/isotest command line tool to run validation tests
> >
> > * L2CAP Enhanced Credit Based Flow Control Mode
> > + tools/l2test has been update to include the new mode
>
> I prefer if we not merge these two just yet. Let this settle a bit first and figure out what the best userspace API is. Otherwise we are stuck with an API that doesn’t work.
>
> > * Enhanced ATT Bearer:
> > + Client and Server support
> > + Include all new procedures
> > + Automaticlly detects and enables channels
> > + Number of channels configurable via main.conf
>
> This looks ok to be merged.
>
> Regards
>
> Marcel
Applied, L2CAP changes were excluded since we still need to settle on
the userspace API.
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2020-03-02 22:48 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-28 23:46 [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 01/12] lib: Add definitions for Enhanced Credits Based Mode Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 02/12] btio: Add mode to for Enhanced Credit Mode Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 03/12] l2test: Add support for L2CAP_EXT_FLOWCTL_MODE Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 04/12] share/att: Add EATT support Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 05/12] shared/gatt-client: Add support for EATT features Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 06/12] gatt: Enable EATT bearer support Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 07/12] shared/gatt-server: Add support for Read Multiple Variable Length Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 08/12] shared/gatt-client: " Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 09/12] shared/gatt: Add support for Handle Value Multiple Notifications Luiz Augusto von Dentz
2020-02-28 23:46 ` [PATCH v2 10/12] gatt: Add support for Notify Multiple Luiz Augusto von Dentz
2020-02-28 23:47 ` [PATCH v2 11/12] core: Add support for setting the number of GATT bearers Luiz Augusto von Dentz
2020-02-28 23:47 ` [PATCH v2 12/12] monitor: Add support for decoding EATT Luiz Augusto von Dentz
2020-03-01 2:49 ` [PATCH v2 00/12] Userspace Bluetooth 5.2 initial support Marcel Holtmann
2020-03-02 22:48 ` Luiz Augusto von Dentz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).