linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support
@ 2020-01-08  0:33 Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 01/22] monitor: Add support for decoding ISO related commands Luiz Augusto von Dentz
                   ` (22 more replies)
  0 siblings, 23 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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

* Monitor:
	+ HCI/LL Bluetooth 5.2 command/event decoding
	+ ISO packets decoding
	+ L2CAP Enhanced Credit Based Flow Control Mode decoding
	+ Enhanced ATT packets decoding

* Emulator:
	+ CIS/Unicast emulation support
	- BIS/Broadcast emulation not yet supported

Kernel changes:
https://lore.kernel.org/linux-bluetooth/20200107074056.25453-1-luiz.dentz@gmail.com/T/#t

Luiz Augusto von Dentz (22):
  monitor: Add support for decoding ISO related commands
  monitor: Add decoding of ISO related Link Layer PDUs
  lib: Add definitions for ISO socket
  tools: Add isotest tool
  emulator: Add initial support for BT 5.2
  monitor: Add support for ISO packets
  tools/btproxy: Add support for ISO packets
  monitor: Fix decoding of CIS estabilished event
  emulator/btdev: Add parameter to CIS Estabilished
  lib: Add definitions for Enhanced Credits Based Mode
  btio: Add mode to for Enhanced Credit Mode
  monitor: Add decoding for L2CAP Enhanced Credit Based PDUs
  l2test: Add support for L2CAP_ECRED_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

 Makefile.tools           |    5 +-
 attrib/gattrib.c         |    5 +-
 btio/btio.c              |    2 +-
 btio/btio.h              |    3 +-
 emulator/btdev.c         |  293 +++++++++--
 emulator/btdev.h         |    1 +
 emulator/hciemu.c        |    3 +
 emulator/hciemu.h        |    1 +
 emulator/vhci.c          |    3 +-
 lib/bluetooth.h          |   14 +
 lib/iso.h                |   45 ++
 lib/l2cap.h              |    1 +
 lib/uuid.h               |    3 +
 monitor/bt.h             |  385 ++++++++++++++
 monitor/l2cap.c          |  192 +++++++
 monitor/ll.c             |  100 ++++
 monitor/packet.c         |  714 ++++++++++++++++++++++++++
 monitor/packet.h         |    2 +
 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/btsnoop.h     |    2 +
 src/shared/gatt-client.c |  279 +++++++++--
 src/shared/gatt-client.h |    5 +-
 src/shared/gatt-server.c |  388 ++++++++++-----
 src/shared/gatt-server.h |    2 +-
 tools/btgatt-client.c    |    2 +-
 tools/btgatt-server.c    |    4 +-
 tools/btproxy.c          |   20 +-
 tools/isotest.c          | 1019 ++++++++++++++++++++++++++++++++++++++
 tools/l2test.c           |    5 +-
 unit/test-gatt.c         |    4 +-
 39 files changed, 4046 insertions(+), 529 deletions(-)
 create mode 100644 lib/iso.h
 create mode 100644 tools/isotest.c

-- 
2.21.0


^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 01/22] monitor: Add support for decoding ISO related commands
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 02/22] monitor: Add decoding of ISO related Link Layer PDUs Luiz Augusto von Dentz
                   ` (21 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds parsing of ISO related commands.
---
 monitor/bt.h     | 300 ++++++++++++++++++++++
 monitor/packet.c | 652 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 952 insertions(+)

diff --git a/monitor/bt.h b/monitor/bt.h
index 8edc895e8..04ad34ea9 100644
--- a/monitor/bt.h
+++ b/monitor/bt.h
@@ -24,6 +24,12 @@
 
 #include <stdint.h>
 
+#define BT_HCI_CMD_5_2		0x2060
+#define BT_HCI_BIT_5_2		(8 * 41) + 5
+#define BT_HCI_SUBEVT_5_2	0x19
+
+#define BT_LL_CMD_MILAN		0x1f
+
 struct bt_ll_hdr {
 	uint8_t  preamble;
 	uint32_t access_addr;
@@ -443,6 +449,7 @@ struct bt_lmp_power_control_res {
 #define BT_H4_ACL_PKT	0x02
 #define BT_H4_SCO_PKT	0x03
 #define BT_H4_EVT_PKT	0x04
+#define BT_H4_ISO_PKT	0x05
 
 struct bt_hci_cmd_hdr {
 	uint16_t opcode;
@@ -459,6 +466,16 @@ struct bt_hci_sco_hdr {
 	uint8_t  dlen;
 } __attribute__ ((packed));
 
+struct bt_hci_iso_hdr {
+	uint16_t handle;
+	uint8_t  dlen;
+} __attribute__ ((packed));
+
+struct bt_hci_iso_data_start {
+	uint16_t sn;
+	uint16_t slen;
+} __attribute__ ((packed));
+
 struct bt_hci_evt_hdr {
 	uint8_t  evt;
 	uint8_t  plen;
@@ -2461,6 +2478,231 @@ struct bt_hci_cmd_le_tx_test_v3 {
 	uint8_t  antenna_ids[0];
 } __attribute__ ((packed));
 
+#define BT_HCI_CMD_LE_READ_BUFFER_SIZE_V2	BT_HCI_CMD_5_2
+#define BT_HCI_BIT_LE_READ_BUFFER_SIZE_V2	BT_HCI_BIT_5_2
+struct bt_hci_rsp_le_read_buffer_size_v2 {
+	uint8_t  status;
+	uint16_t acl_mtu;
+	uint8_t  acl_max_pkt;
+	uint16_t iso_mtu;
+	uint8_t  iso_max_pkt;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_ISO_TX_SYNC		BT_HCI_CMD_5_2 + 1
+#define BT_HCI_BIT_LE_READ_ISO_TX_SYNC		BT_HCI_BIT_5_2 + 1
+struct bt_hci_cmd_le_read_iso_tx_sync {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+struct bt_hci_rsp_le_read_iso_tx_sync {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t seq;
+	uint32_t timestamp;
+	uint8_t  offset[3];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_CIG_PARAMS		BT_HCI_CMD_5_2 + 2
+#define BT_HCI_BIT_LE_SET_CIG_PARAMS		BT_HCI_BIT_5_2 + 2
+struct bt_hci_cis_params {
+	uint8_t  cis_id;
+	uint16_t m_sdu;
+	uint16_t s_sdu;
+	uint8_t  m_phy;
+	uint8_t  s_phy;
+	uint8_t  m_rtn;
+	uint8_t  s_rtn;
+} __attribute__ ((packed));
+
+struct bt_hci_cmd_le_set_cig_params {
+	uint8_t  cig_id;
+	uint8_t  m_interval[3];
+	uint8_t  s_interval[3];
+	uint8_t  sca;
+	uint8_t  packing;
+	uint8_t  framing;
+	uint16_t m_latency;
+	uint16_t s_latency;
+	uint8_t  num_cis;
+	struct bt_hci_cis_params cis[0];
+} __attribute__ ((packed));
+
+struct bt_hci_rsp_le_set_cig_params {
+	uint8_t  status;
+	uint8_t  cig_id;
+	uint8_t  num_handles;
+	uint16_t handle[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_CIG_PARAMS_TEST	BT_HCI_CMD_5_2 + 3
+#define BT_HCI_BIT_LE_SET_CIG_PARAMS_TEST	BT_HCI_BIT_5_2 + 3
+struct bt_hci_cis_params_test {
+	uint8_t  cis_id;
+	uint8_t  nse;
+	uint16_t m_sdu;
+	uint16_t s_sdu;
+	uint8_t  m_pdu;
+	uint8_t  s_pdu;
+	uint8_t  m_phy;
+	uint8_t  s_phy;
+	uint8_t  m_bn;
+	uint8_t  s_bn;
+} __attribute__ ((packed));
+
+struct bt_hci_cmd_le_set_cig_params_test {
+	uint8_t  cig_id;
+	uint8_t  m_interval[3];
+	uint8_t  s_interval[3];
+	uint8_t  m_ft;
+	uint8_t  s_ft;
+	uint16_t iso_interval;
+	uint8_t  sca;
+	uint8_t  packing;
+	uint8_t  framing;
+	uint8_t  num_cis;
+	struct bt_hci_cis_params_test cis[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CREATE_CIS		BT_HCI_CMD_5_2 + 4
+#define BT_HCI_BIT_LE_CREATE_CIS		BT_HCI_BIT_5_2 + 4
+struct bt_hci_cis {
+	uint16_t  cis_handle;
+	uint16_t  acl_handle;
+} __attribute__ ((packed));
+
+struct bt_hci_cmd_le_create_cis {
+	uint8_t  num_cis;
+	struct bt_hci_cis cis[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REMOVE_CIG		BT_HCI_CMD_5_2 + 5
+#define BT_HCI_BIT_LE_REMOVE_CIG		BT_HCI_BIT_5_2 + 5
+struct bt_hci_cmd_le_remove_cig {
+	uint8_t  cig_id;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_ACCEPT_CIS		BT_HCI_CMD_5_2 + 6
+#define BT_HCI_BIT_LE_ACCEPT_CIS		BT_HCI_BIT_5_2 + 6
+struct bt_hci_cmd_le_accept_cis {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REJECT_CIS		BT_HCI_CMD_5_2 + 7
+#define BT_HCI_BIT_LE_REJECT_CIS		BT_HCI_BIT_5_2 + 7
+struct bt_hci_cmd_le_reject_cis {
+	uint16_t handle;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CREATE_BIG		BT_HCI_CMD_5_2 + 8
+#define BT_HCI_BIT_LE_CREATE_BIG		BT_HCI_BIT_5_2 + 8
+struct bt_hci_bis {
+	uint8_t  sdu_interval[3];
+	uint16_t sdu;
+	uint16_t latency;
+	uint8_t  rtn;
+	uint8_t  phy;
+	uint8_t  packing;
+	uint8_t  framing;
+	uint8_t  encryption;
+	uint8_t  bcode[16];
+} __attribute__ ((packed));
+
+struct bt_hci_cmd_le_create_big {
+	uint8_t  big_id;
+	uint8_t  adv_handle;
+	uint8_t  num_bis;
+	struct bt_hci_bis bis[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CREATE_BIG_TEST		BT_HCI_CMD_5_2 + 9
+#define BT_HCI_BIT_LE_CREATE_BIG_TEST		BT_HCI_BIT_5_2 + 9
+struct bt_hci_bis_test {
+	uint8_t  sdu_interval[3];
+	uint16_t iso_interval;
+	uint8_t  nse;
+	uint16_t sdu;
+	uint8_t  pdu;
+	uint8_t  phy;
+	uint8_t  packing;
+	uint8_t  framing;
+	uint8_t  bn;
+	uint8_t  irc;
+	uint8_t  pto;
+	uint8_t  adv_handle;
+	uint8_t  encryption;
+	uint8_t  bcode[16];
+} __attribute__ ((packed));
+
+struct bt_hci_cmd_le_create_big_test {
+	uint8_t  big_id;
+	uint8_t  adv_handle;
+	uint8_t  num_bis;
+	struct bt_hci_bis_test bis[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_TERM_BIG			BT_HCI_CMD_5_2 + 10
+#define BT_HCI_BIT_LE_TERM_BIG			BT_HCI_BIT_5_2 + 10
+struct bt_hci_cmd_le_term_big {
+	uint8_t  big_id;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_BIG_CREATE_SYNC		BT_HCI_CMD_5_2 + 11
+#define BT_HCI_BIT_LE_BIG_CREATE_SYNC		BT_HCI_BIT_5_2 + 11
+struct bt_hci_bis_sync {
+} __attribute__ ((packed));
+
+struct bt_hci_cmd_le_big_create_sync {
+	uint8_t  big_id;
+	uint16_t sync_handle;
+	uint8_t  num_bis;
+	uint8_t  encryption;
+	uint8_t  bcode[16];
+	uint8_t  mse;
+	uint16_t timeout;
+	struct bt_hci_bis_sync bis[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_BIG_TERM_SYNC		BT_HCI_CMD_5_2 + 12
+#define BT_HCI_BIT_LE_BIG_TERM_SYNC		BT_HCI_BIT_5_2 + 12
+struct bt_hci_cmd_le_big_term_sync {
+	uint8_t  big_id;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REQ_PEER_SCA		BT_HCI_CMD_5_2 + 13
+#define BT_HCI_BIT_LE_REQ_PEER_SCA		BT_HCI_BIT_5_2 + 13
+struct bt_hci_cmd_le_req_peer_sca {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SETUP_ISO_PATH		BT_HCI_CMD_5_2 + 14
+#define BT_HCI_BIT_LE_SETUP_ISO_PATH		BT_HCI_BIT_5_2 + 14
+struct bt_hci_cmd_le_setup_iso_path {
+	uint16_t handle;
+	uint8_t  input_path;
+	uint8_t  output_path;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REMOVE_ISO_PATH		BT_HCI_CMD_5_2 + 15
+#define BT_HCI_BIT_LE_REMOVE_ISO_PATH		BT_HCI_BIT_5_2 + 15
+struct bt_hci_cmd_le_remove_iso_path {
+	uint16_t handle;
+	uint8_t  path_dir;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_ISO_TX_TEST		BT_HCI_CMD_5_2 + 16
+#define BT_HCI_BIT_LE_ISO_TX_TEST		BT_HCI_BIT_5_2 + 16
+
+#define BT_HCI_CMD_LE_ISO_RX_TEST		BT_HCI_CMD_5_2 + 17
+#define BT_HCI_BIT_LE_ISO_RX_TEST		BT_HCI_BIT_5_2 + 17
+
+#define BT_HCI_CMD_LE_ISO_READ_TEST_COUNTER	BT_HCI_CMD_5_2 + 18
+#define BT_HCI_BIT_LE_ISO_READ_TEST_COUNTER	BT_HCI_BIT_5_2 + 18
+
+#define BT_HCI_CMD_LE_ISO_TEST_END		BT_HCI_CMD_5_2 + 19
+#define BT_HCI_BIT_LE_ISO_TEST_END		BT_HCI_BIT_5_2 + 19
+
 #define BT_HCI_EVT_INQUIRY_COMPLETE		0x01
 struct bt_hci_evt_inquiry_complete {
 	uint8_t  status;
@@ -3140,6 +3382,64 @@ struct bt_hci_evt_le_chan_select_alg {
 	uint8_t  algorithm;
 } __attribute__ ((packed));
 
+#define BT_HCI_EVT_LE_CIS_ESTABLISHED		BT_HCI_SUBEVT_5_2
+struct bt_hci_evt_le_cis_established {
+	uint8_t  status;
+	uint16_t conn_handle;
+	uint8_t  cig_sync_delay[3];
+	uint8_t  cis_sync_delay[3];
+	uint8_t  latency[3];
+	uint8_t  m_phy;
+	uint8_t  s_phy;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_CIS_REQ			BT_HCI_SUBEVT_5_2 + 1
+struct bt_hci_evt_le_cis_req {
+	uint16_t acl_handle;
+	uint16_t cis_handle;
+	uint8_t  cig_id;
+	uint8_t  cis_id;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_BIG_COMPLETE		BT_HCI_SUBEVT_5_2 + 2
+struct bt_hci_evt_le_big_complete {
+	uint8_t  status;
+	uint8_t  big_id;
+	uint8_t  sync_delay[3];
+	uint8_t  latency[3];
+	uint8_t  phy;
+	uint8_t  num_bis;
+	uint16_t handle[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_BIG_TERMINATE		BT_HCI_SUBEVT_5_2 + 3
+struct bt_hci_evt_le_big_terminate {
+	uint8_t  reason;
+	uint8_t  big_id;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED	BT_HCI_SUBEVT_5_2 + 4
+struct bt_hci_evt_le_big_sync_estabilished {
+	uint8_t  status;
+	uint8_t  big_id;
+	uint8_t  latency[3];
+	uint8_t  num_bis;
+	uint16_t handle[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_BIG_SYNC_LOST		BT_HCI_SUBEVT_5_2 + 5
+struct bt_hci_evt_le_big_sync_lost {
+	uint8_t  big_id;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_REQ_PEER_SCA_COMPLETE	BT_HCI_SUBEVT_5_2 + 6
+struct bt_hci_evt_le_req_peer_sca_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  sca;
+} __attribute__ ((packed));
+
 #define BT_HCI_ERR_SUCCESS			0x00
 #define BT_HCI_ERR_UNKNOWN_COMMAND		0x01
 #define BT_HCI_ERR_UNKNOWN_CONN_ID		0x02
diff --git a/monitor/packet.c b/monitor/packet.c
index ab8bbdee5..8987ed703 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -2617,6 +2617,11 @@ static const struct bitfield_data features_le[] = {
 	{ 25, "Periodic Advertising Sync Transfer - Recipient"	},
 	{ 26, "Sleep Clock Accuracy Updates"			},
 	{ 27, "Remote Public Key Validation"			},
+	{ 28, "Connected Isochronous Stream - Master"		},
+	{ 29, "Connected Isochronous Stream - Slave"		},
+	{ 30, "Isochronous Broadcaster"				},
+	{ 31, "Synchronized Receiver"				},
+	{ 32, "Isochronous Channels (Host Support)"		},
 	{ }
 };
 
@@ -2977,6 +2982,12 @@ static const struct bitfield_data events_le_table[] = {
 	{ 17, "LE Extended Advertising Set Terminated"	},
 	{ 18, "LE Scan Request Received"		},
 	{ 19, "LE Channel Selection Algorithm"		},
+	{ 24, "LE CIS Established"			},
+	{ 25, "LE CIS Request"				},
+	{ 26, "LE Create BIG Complete"			},
+	{ 27, "LE Terminate BIG Complete"		},
+	{ 28, "LE BIG Sync Estabilished Complete"	},
+	{ 29, "LE BIG Sync Lost"			},
 	{ }
 };
 
@@ -7538,6 +7549,437 @@ static void le_tx_test_cmd_v3(const void *data, uint8_t size)
 		print_field("  Antenna ID: %u", cmd->antenna_ids[i]);
 }
 
+static void print_sca(uint8_t sca)
+{
+	switch (sca) {
+	case 0x00:
+		print_field("SCA: 201 - 500 ppm (0x%2.2x)", sca);
+		return;
+	case 0x01:
+		print_field("SCA: 151 - 200 ppm (0x%2.2x)", sca);
+		return;
+	case 0x02:
+		print_field("SCA: 101 - 150 ppm (0x%2.2x)", sca);
+		return;
+	case 0x03:
+		print_field("SCA: 76 - 100 ppm (0x%2.2x)", sca);
+		return;
+	case 0x04:
+		print_field("SCA: 51 - 75 ppm (0x%2.2x)", sca);
+		return;
+	case 0x05:
+		print_field("SCA: 31 - 50 ppm (0x%2.2x)", sca);
+		return;
+	case 0x06:
+		print_field("SCA: 21 - 30 ppm (0x%2.2x)", sca);
+		return;
+	case 0x07:
+		print_field("SCA: 0 - 20 ppm (0x%2.2x)", sca);
+		return;
+	default:
+		print_field("SCA: Reserved (0x%2.2x)", sca);
+	}
+}
+
+static void print_packing(uint8_t value)
+{
+	switch (value) {
+	case 0x00:
+		print_field("Packing: Sequential (0x%2.2x)", value);
+		return;
+	case 0x01:
+		print_field("Packing: Interleaved (0x%2.2x)", value);
+		return;
+	default:
+		print_field("Packing: Reserved (0x%2.2x)", value);
+	}
+}
+
+static void print_framing(uint8_t value)
+{
+	switch (value) {
+	case 0x00:
+		print_field("Framing: Unframed (0x%2.2x)", value);
+		return;
+	case 0x01:
+		print_field("Framing: Framed (0x%2.2x)", value);
+		return;
+	default:
+		print_field("Packing: Reserved (0x%2.2x)", value);
+	}
+}
+
+static void le_read_buffer_size_v2_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_buffer_size_v2 *rsp = data;
+
+	print_status(rsp->status);
+
+	if (size == 1)
+		return;
+
+	print_field("ACL MTU: %d", le16_to_cpu(rsp->acl_mtu));
+	print_field("ACL max packet: %d", rsp->acl_max_pkt);
+	print_field("ISO MTU: %d", le16_to_cpu(rsp->iso_mtu));
+	print_field("ISO max packet: %d", rsp->iso_max_pkt);
+}
+
+static void le_read_iso_tx_sync_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_read_iso_tx_sync *cmd = data;
+
+	print_field("Handle: %d", le16_to_cpu(cmd->handle));
+}
+
+static void le_read_iso_tx_sync_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_iso_tx_sync *rsp = data;
+	uint32_t offset = 0;
+
+	print_status(rsp->status);
+
+	if (size == 1)
+		return;
+
+	print_field("Handle: %d", le16_to_cpu(rsp->handle));
+	print_field("Sequence Number: %d", le16_to_cpu(rsp->seq));
+	print_field("Timestamp: %d", le32_to_cpu(rsp->timestamp));
+
+	memcpy(&offset, rsp->offset, sizeof(rsp->offset));
+
+	print_field("Offset: %d", le32_to_cpu(offset));
+}
+
+static void print_cis_params(const void *data)
+{
+	const struct bt_hci_cis_params *cis = data;
+
+	print_field("CIS ID: 0x%2.2x", cis->cis_id);
+	print_field("Master to Slave Maximum SDU Size: %u",
+						le16_to_cpu(cis->m_sdu));
+	print_field("Slave to Master Maximum SDU Size: %u",
+						le16_to_cpu(cis->s_sdu));
+	print_le_phy("Master to Slave PHY", cis->m_phy);
+	print_le_phy("Slave to Master PHY", cis->s_phy);
+	print_field("Master to Slave Retransmission attempts: 0x%2.2x",
+							cis->m_rtn);
+	print_field("Slave to Master Retransmission attempts: 0x%2.2x",
+							cis->s_rtn);
+}
+
+static void print_list(const void *data, uint8_t size, int num_items,
+			size_t item_size, void (*callback)(const void *data))
+{
+	while (size >= item_size && num_items) {
+		callback(data);
+		data += item_size;
+		size -= item_size;
+		num_items--;
+	}
+
+	if (num_items)
+		print_hex_field("", data, size);
+}
+
+static void print_usec_interval(const char *prefix, const uint8_t interval[3])
+{
+	uint32_t u24 = 0;
+
+	memcpy(&u24, interval, 3);
+	print_field("%s: %u us (0x%6.6x)", prefix, le32_to_cpu(u24),
+						le32_to_cpu(u24));
+}
+
+static void le_set_cig_params_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_cig_params *cmd = data;
+
+	print_field("CIG ID: 0x%2.2x", cmd->cig_id);
+	print_usec_interval("Master to Slave SDU Interval", cmd->m_interval);
+	print_usec_interval("Slave to Master SDU Interval", cmd->s_interval);
+	print_sca(cmd->sca);
+	print_packing(cmd->packing);
+	print_framing(cmd->framing);
+	print_field("Master to Slave Maximum Latency: %d ms (0x%4.4x)",
+		le16_to_cpu(cmd->m_latency), le16_to_cpu(cmd->m_latency));
+	print_field("Slave to Master Maximum Latency: %d ms (0x%4.4x)",
+		le16_to_cpu(cmd->s_latency), le16_to_cpu(cmd->s_latency));
+	print_field("Number of CIS: %u", cmd->num_cis);
+
+	size -= sizeof(*cmd);
+
+	print_list(cmd->cis, size, cmd->num_cis, sizeof(*cmd->cis),
+						print_cis_params);
+}
+
+static void print_cis_params_test(const void *data)
+{
+	const struct bt_hci_cis_params_test *cis = data;
+
+	print_field("CIS ID: 0x%2.2x", cis->cis_id);
+	print_field("NSE: 0x%2.2x", cis->nse);
+	print_field("Master to Slave Maximum SDU: 0x%4.4x", cis->m_sdu);
+	print_field("Slave to Master Maximum SDU: 0x%4.4x",
+						le16_to_cpu(cis->s_sdu));
+	print_field("Master to Slave Maximum PDU: 0x%2.2x",
+						le16_to_cpu(cis->m_pdu));
+	print_field("Slave to Master Maximum PDU: 0x%2.2x", cis->s_pdu);
+	print_le_phy("Master to Slave PHY", cis->m_phy);
+	print_le_phy("Slave to Master PHY", cis->s_phy);
+	print_field("Master to Slave Burst Number: 0x%2.2x", cis->m_bn);
+	print_field("Slave to Master Burst Number: 0x%2.2x", cis->s_bn);
+}
+
+static void le_set_cig_params_test_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_cig_params_test *cmd = data;
+
+	print_field("CIG ID: 0x%2.2x", cmd->cig_id);
+	print_usec_interval("Master to Slave SDU Interval", cmd->m_interval);
+	print_usec_interval("Master to Slave SDU Interval", cmd->s_interval);
+	print_field("Master to Slave Flush Timeout: 0x%2.2x", cmd->m_ft);
+	print_field("Slave to Master Flush Timeout: 0x%2.2x", cmd->s_ft);
+	print_field("ISO Interval: %.2f ms (0x%4.4x)",
+				le16_to_cpu(cmd->iso_interval) * 1.25,
+				le16_to_cpu(cmd->iso_interval));
+	print_sca(cmd->sca);
+	print_packing(cmd->packing);
+	print_framing(cmd->framing);
+	print_field("Number of CIS: %u", cmd->num_cis);
+
+	size -= sizeof(*cmd);
+
+	print_list(cmd->cis, size, cmd->num_cis, sizeof(*cmd->cis),
+						print_cis_params_test);
+}
+
+static void print_cig_handle(const void *data)
+{
+	const uint16_t *handle = data;
+
+	print_field("Connection Handle: %d", le16_to_cpu(*handle));
+}
+
+static void le_set_cig_params_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_set_cig_params *rsp = data;
+
+	print_status(rsp->status);
+
+	if (size == 1)
+		return;
+
+	print_field("CIG ID: 0x%2.2x", rsp->cig_id);
+	print_field("Number of Handles: %u", rsp->num_handles);
+
+	size -= sizeof(*rsp);
+
+	print_list(rsp->handle, size, rsp->num_handles, sizeof(*rsp->handle),
+						print_cig_handle);
+}
+
+static void print_cis(const void *data)
+{
+	const struct bt_hci_cis *cis = data;
+
+	print_field("CIS Handle: %d", cis->cis_handle);
+	print_field("ACL Handle: %d", cis->acl_handle);
+}
+
+static void le_create_cis_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_create_cis *cmd = data;
+
+	print_field("Number of CIS: %u", cmd->num_cis);
+
+	size -= sizeof(*cmd);
+
+	print_list(cmd->cis, size, cmd->num_cis, sizeof(*cmd->cis), print_cis);
+}
+
+static void le_remove_cig_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_remove_cig *cmd = data;
+
+	print_field("CIG ID: 0x%02x", cmd->cig_id);
+}
+
+static void le_accept_cis_req_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_accept_cis *cmd = data;
+
+	print_field("CIS Handle: %d", le16_to_cpu(cmd->handle));
+}
+
+static void le_reject_cis_req_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_reject_cis *cmd = data;
+
+	print_field("CIS Handle: %d", le16_to_cpu(cmd->handle));
+	print_reason(cmd->reason);
+}
+
+static void print_bis(const void *data)
+{
+	const struct bt_hci_bis *bis = data;
+
+	print_usec_interval("SDU Interval", bis->sdu_interval);
+	print_field("Maximum SDU size: %u", le16_to_cpu(bis->sdu));
+	print_field("Maximum Latency: %u ms (0x%4.4x)",
+			le16_to_cpu(bis->latency), le16_to_cpu(bis->latency));
+	print_field("RTN: 0x%2.2x", bis->rtn);
+	print_le_phy("PHY", bis->phy);
+	print_packing(bis->packing);
+	print_framing(bis->framing);
+	print_field("Encryption: 0x%2.2x", bis->encryption);
+	print_hex_field("Broadcast Code", bis->bcode, 16);
+}
+
+static void le_create_big_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_create_big *cmd = data;
+
+	print_field("BIG ID: 0x%2.2x", cmd->big_id);
+	print_field("Advertising Handle: 0x%2.2x", cmd->adv_handle);
+	print_field("Number of BIS: %u", cmd->num_bis);
+
+	size -= sizeof(*cmd);
+
+	print_list(cmd->bis, size, cmd->num_bis, sizeof(*cmd->bis), print_bis);
+}
+
+static void print_bis_test(const void *data)
+{
+	const struct bt_hci_bis_test *bis = data;
+
+	print_usec_interval("SDU Interval", bis->sdu_interval);
+	print_field("ISO Interval: %.2f ms (0x%4.4x)",
+				le16_to_cpu(bis->iso_interval) * 1.25,
+				le16_to_cpu(bis->iso_interval));
+	print_field("Number of Subevents: %u", bis->nse);
+	print_field("Maximum SDU: %u", bis->sdu);
+	print_field("Maximum PDU: %u", bis->pdu);
+	print_packing(bis->packing);
+	print_framing(bis->framing);
+	print_le_phy("PHY", bis->phy);
+	print_field("Burst Number: %u", bis->bn);
+	print_field("Immediate Repetition Count: %u", bis->irc);
+	print_field("Pre Transmission Offset: 0x%2.2x", bis->pto);
+	print_field("Encryption: 0x%2.2x", bis->encryption);
+	print_hex_field("Broadcast Code", bis->bcode, 16);
+}
+
+static void le_create_big_cmd_test_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_create_big_test *cmd = data;
+
+	print_field("BIG ID: 0x%2.2x", cmd->big_id);
+	print_field("Advertising Handle: 0x%2.2x", cmd->adv_handle);
+	print_field("Number of BIS: %u", cmd->num_bis);
+
+	size -= sizeof(*cmd);
+
+	print_list(cmd->bis, size, cmd->num_bis, sizeof(*cmd->bis),
+						print_bis_test);
+}
+
+static void le_terminate_big_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_term_big *cmd = data;
+
+	print_field("BIG ID: 0x%2.2x", cmd->big_id);
+	print_reason(cmd->reason);
+}
+
+static void print_bis_sync(const void *data)
+{
+	const uint8_t *bis_id = data;
+
+	print_field("BIS ID: 0x%2.2x", *bis_id);
+}
+
+static void le_big_create_sync_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_big_create_sync *cmd = data;
+
+	print_field("BIG ID: 0x%2.2x", cmd->big_id);
+	print_field("Number of BIS: %u", cmd->num_bis);
+	print_field("Encryption: 0x%2.2x", cmd->encryption);
+	print_hex_field("Broadcast Code", cmd->bcode, 16);
+	print_field("Number Subevents: 0x%2.2x", cmd->mse);
+	print_field("Timeout: %d ms (0x%4.4x)", le16_to_cpu(cmd->timeout) * 10,
+						le16_to_cpu(cmd->timeout));
+
+	size -= sizeof(*cmd);
+
+	print_list(cmd->bis, size, cmd->num_bis, sizeof(*cmd->bis),
+						print_bis_sync);
+}
+
+static void le_big_term_sync_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_big_term_sync *cmd = data;
+
+	print_field("BIG ID: 0x%2.2x", cmd->big_id);
+}
+
+static void print_iso_path(const char *prefix, uint8_t path)
+{
+	switch (path) {
+	case 0x00:
+		print_field("%s Data Path: Disabled (0x%2.2x)", prefix, path);
+		return;
+	case 0x01:
+		print_field("%s Data Path: HCI (0x%2.2x)", prefix, path);
+		return;
+	case 0xff:
+		print_field("%s Data Path: Test Mode (0x%2.2x)", prefix, path);
+		return;
+	default:
+		print_field("%s Data Path: Logical Channel Number (0x%2.2x)",
+							prefix, path);
+	}
+}
+
+static void le_setup_iso_path_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_setup_iso_path *cmd = data;
+
+	print_field("Handle: %d", le16_to_cpu(cmd->handle));
+	print_iso_path("Input", cmd->input_path);
+	print_iso_path("Output", cmd->output_path);
+}
+
+static void print_iso_dir(uint8_t path_dir)
+{
+	switch (path_dir) {
+	case 0x00:
+		print_field("Data Path Direction: Input (0x%2.2x)", path_dir);
+		return;
+	case 0x01:
+		print_field("Data Path Direction: Output (0x%2.2x)", path_dir);
+		return;
+	default:
+		print_field("Data Path Direction: Reserved (0x%2.2x)",
+							path_dir);
+	}
+}
+
+static void le_remove_iso_path_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_remove_iso_path *cmd = data;
+
+	print_field("Connection Handle: %d", le16_to_cpu(cmd->handle));
+	print_iso_dir(cmd->path_dir);
+}
+
+static void le_req_peer_sca_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_req_peer_sca *cmd = data;
+
+	print_field("Connection Handle: %d", le16_to_cpu(cmd->handle));
+}
+
 struct opcode_data {
 	uint16_t opcode;
 	int bit;
@@ -8331,6 +8773,107 @@ static const struct opcode_data opcode_table[] = {
 	{ 0x2050, 316, "LE Transmitter Test command [v3]",
 				le_tx_test_cmd_v3, 9, false,
 				status_rsp, 1, true },
+	{ BT_HCI_CMD_LE_READ_BUFFER_SIZE_V2,
+				BT_HCI_BIT_LE_READ_BUFFER_SIZE_V2,
+				"LE Read Buffer v2",
+				null_cmd, 0, true,
+				le_read_buffer_size_v2_rsp,
+				sizeof(
+				struct bt_hci_rsp_le_read_buffer_size_v2),
+				true },
+	{ BT_HCI_CMD_LE_READ_ISO_TX_SYNC,
+				BT_HCI_BIT_LE_READ_ISO_TX_SYNC,
+				"LE Read ISO TX Sync",
+				le_read_iso_tx_sync_cmd,
+				sizeof(struct bt_hci_cmd_le_read_iso_tx_sync),
+				true,
+				le_read_iso_tx_sync_rsp,
+				sizeof(struct bt_hci_rsp_le_read_iso_tx_sync),
+				true },
+	{ BT_HCI_CMD_LE_SET_CIG_PARAMS, BT_HCI_BIT_LE_SET_CIG_PARAMS,
+				"LE Set Connected Isochronous Group Parameters",
+				le_set_cig_params_cmd,
+				sizeof(struct bt_hci_cmd_le_set_cig_params),
+				false,
+				le_set_cig_params_rsp,
+				sizeof(struct bt_hci_rsp_le_set_cig_params),
+				false },
+	{ BT_HCI_CMD_LE_SET_CIG_PARAMS_TEST, BT_HCI_BIT_LE_SET_CIG_PARAMS_TEST,
+				"LE Set Connected Isochronous Group Parameters"
+				" Test", le_set_cig_params_test_cmd,
+				sizeof(
+				struct bt_hci_cmd_le_set_cig_params_test),
+				false,
+				le_set_cig_params_rsp,
+				sizeof(struct bt_hci_rsp_le_set_cig_params),
+				false },
+	{ BT_HCI_CMD_LE_CREATE_CIS, BT_HCI_BIT_LE_CREATE_CIS,
+				"LE Create Connected Isochronous Stream",
+				le_create_cis_cmd,
+				sizeof(struct bt_hci_cmd_le_create_cis),
+				false },
+	{ BT_HCI_CMD_LE_REMOVE_CIG, BT_HCI_BIT_LE_REMOVE_CIG,
+				"LE Remove Connected Isochronous Group",
+				le_remove_cig_cmd,
+				sizeof(struct bt_hci_cmd_le_remove_cig), false,
+				status_rsp, 1, true },
+	{ BT_HCI_CMD_LE_ACCEPT_CIS, BT_HCI_BIT_LE_ACCEPT_CIS,
+				"LE Accept Connected Isochronous Stream Request",
+				le_accept_cis_req_cmd,
+				sizeof(struct bt_hci_cmd_le_accept_cis), true },
+	{ BT_HCI_CMD_LE_REJECT_CIS, BT_HCI_BIT_LE_REJECT_CIS,
+				"LE Reject Connected Isochronous Stream Request",
+				le_reject_cis_req_cmd,
+				sizeof(struct bt_hci_cmd_le_reject_cis), true,
+				status_rsp, 1, true },
+	{ BT_HCI_CMD_LE_CREATE_BIG, BT_HCI_BIT_LE_CREATE_BIG,
+				"LE Create Broadcast Isochronous Group",
+				le_create_big_cmd },
+	{ BT_HCI_CMD_LE_CREATE_BIG_TEST, BT_HCI_BIT_LE_CREATE_BIG_TEST,
+				"LE Create Broadcast Isochronous Group Test",
+				le_create_big_cmd_test_cmd },
+	{ BT_HCI_CMD_LE_TERM_BIG, BT_HCI_BIT_LE_TERM_BIG,
+				"LE Terminate Broadcast Isochronous Group",
+				le_terminate_big_cmd,
+				sizeof(struct bt_hci_cmd_le_term_big), true,
+				status_rsp, 1, true},
+	{ BT_HCI_CMD_LE_BIG_CREATE_SYNC, BT_HCI_BIT_LE_BIG_CREATE_SYNC,
+				"LE Broadcast Isochronous Group Create Sync",
+				le_big_create_sync_cmd,
+				sizeof(struct bt_hci_cmd_le_big_create_sync),
+				true },
+	{ BT_HCI_CMD_LE_BIG_TERM_SYNC, BT_HCI_BIT_LE_BIG_TERM_SYNC,
+				"LE Broadcast Isochronous Group Terminate Sync",
+				le_big_term_sync_cmd,
+				sizeof(struct bt_hci_cmd_le_big_term_sync),
+				true },
+	{ BT_HCI_CMD_LE_REQ_PEER_SCA, BT_HCI_BIT_LE_REQ_PEER_SCA,
+				"LE Request Peer SCA", le_req_peer_sca_cmd,
+				sizeof(struct bt_hci_cmd_le_req_peer_sca),
+				true },
+	{ BT_HCI_CMD_LE_SETUP_ISO_PATH, BT_HCI_BIT_LE_SETUP_ISO_PATH,
+				"LE Setup Isochronous Data Path",
+				le_setup_iso_path_cmd,
+				sizeof(struct bt_hci_cmd_le_setup_iso_path),
+				true, status_rsp, 1, true },
+	{ BT_HCI_CMD_LE_REMOVE_ISO_PATH, BT_HCI_BIT_LE_REMOVE_ISO_PATH,
+				"LE Remove Isochronous Data Path",
+				le_remove_iso_path_cmd,
+				sizeof(struct bt_hci_cmd_le_remove_iso_path),
+				true, status_rsp, 1, true },
+	{ BT_HCI_CMD_LE_ISO_TX_TEST, BT_HCI_BIT_LE_ISO_TX_TEST,
+				"LE Isochronous Transmit Test", NULL, 0,
+				false },
+	{ BT_HCI_CMD_LE_ISO_RX_TEST, BT_HCI_BIT_LE_ISO_RX_TEST,
+				"LE Isochronous Receive Test", NULL, 0,
+				false },
+	{ BT_HCI_CMD_LE_ISO_READ_TEST_COUNTER,
+				BT_HCI_BIT_LE_ISO_READ_TEST_COUNTER,
+				"LE Isochronous Read Test Counters", NULL, 0,
+				false },
+	{ BT_HCI_CMD_LE_ISO_TEST_END, BT_HCI_BIT_LE_ISO_TEST_END,
+				"LE Isochronous Read Test Counters", NULL, 0,
+				false },
 	{ }
 };
 
@@ -9724,6 +10267,85 @@ static void le_chan_select_alg_evt(const void *data, uint8_t size)
 	print_field("Algorithm: %s (0x%2.2x)", str, evt->algorithm);
 }
 
+static void le_cis_established_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_cis_established *evt = data;
+
+	print_status(evt->status);
+	print_field("Connection Handle: %d", le16_to_cpu(evt->status));
+	print_usec_interval("CIG Synchronization Delay", evt->cig_sync_delay);
+	print_usec_interval("CIS Synchronization Delay", evt->cis_sync_delay);
+	print_usec_interval("Latency", evt->latency);
+	print_le_phy("Master to Slave PHY", evt->m_phy);
+	print_le_phy("Slave to Master PHY", evt->m_phy);
+}
+
+static void le_req_cis_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_cis_req *evt = data;
+
+	print_field("ACL Handle: %d", le16_to_cpu(evt->acl_handle));
+	print_field("CIS Handle: %d", le16_to_cpu(evt->cis_handle));
+	print_field("CIG ID: 0x%2.2x", evt->cig_id);
+	print_field("CIS ID: 0x%2.2x", evt->cis_id);
+}
+
+static void print_bis_handle(const void *data)
+{
+	const uint16_t *handle = data;
+
+	print_field("Connection Handle: %d", le16_to_cpu(*handle));
+}
+
+static void le_big_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_big_complete *evt = data;
+
+	print_status(evt->status);
+	print_field("BIG ID: 0x%2.2x", evt->big_id);
+	print_usec_interval("BIG Synchronization Delay", evt->sync_delay);
+	print_usec_interval("Transport Latency", evt->latency);
+	print_le_phy("PHY", evt->phy);
+	print_list(evt->handle, size, evt->num_bis, sizeof(*evt->handle),
+						print_bis_handle);
+}
+
+static void le_big_terminate_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_big_terminate *evt = data;
+
+	print_reason(evt->reason);
+	print_field("BIG ID: 0x%2.2x", evt->big_id);
+}
+
+static void le_big_sync_estabilished_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_big_sync_estabilished *evt = data;
+
+	print_status(evt->status);
+	print_field("BIG ID: 0x%2.2x", evt->big_id);
+	print_usec_interval("Transport Latency", evt->latency);
+	print_list(evt->handle, size, evt->num_bis, sizeof(*evt->handle),
+						print_bis_handle);
+}
+
+static void le_big_sync_lost_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_big_sync_lost *evt = data;
+
+	print_field("BIG ID: 0x%2.2x", evt->big_id);
+	print_reason(evt->reason);
+}
+
+static void le_req_sca_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_req_peer_sca_complete *evt = data;
+
+	print_status(evt->status);
+	print_field("Connection Handle: %d", le16_to_cpu(evt->handle));
+	print_sca(evt->sca);
+}
+
 struct subevent_data {
 	uint8_t subevent;
 	const char *str;
@@ -9807,6 +10429,36 @@ static const struct subevent_data le_meta_event_table[] = {
 				le_scan_req_received_evt, 8, true},
 	{ 0x14, "LE Channel Selection Algorithm",
 				le_chan_select_alg_evt, 3, true},
+	{ BT_HCI_EVT_LE_CIS_ESTABLISHED,
+				"LE Connected Isochronous Stream Established",
+				le_cis_established_evt,
+				sizeof(struct bt_hci_evt_le_cis_established),
+				true },
+	{ BT_HCI_EVT_LE_CIS_REQ, "LE Connected Isochronous Stream Request",
+				le_req_cis_evt,
+				sizeof(struct bt_hci_evt_le_cis_req),
+				true },
+	{ BT_HCI_EVT_LE_BIG_COMPLETE,
+				"LE Broadcast Isochronous Group Complete",
+				le_big_complete_evt,
+				sizeof(struct bt_hci_evt_le_big_complete) },
+	{ BT_HCI_EVT_LE_BIG_TERMINATE,
+				"LE Broadcast Isochronous Group Terminate",
+				le_big_terminate_evt,
+				sizeof(struct bt_hci_evt_le_big_terminate) },
+	{ BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED,
+				"LE Broadcast Isochronous Group Sync "
+				"Estabilished", le_big_sync_estabilished_evt,
+				sizeof(struct bt_hci_evt_le_big_sync_lost) },
+	{ BT_HCI_EVT_LE_BIG_SYNC_LOST,
+				"LE Broadcast Isochronous Group Sync Lost",
+				le_big_sync_lost_evt,
+				sizeof(struct bt_hci_evt_le_big_sync_lost) },
+	{ BT_HCI_EVT_LE_REQ_PEER_SCA_COMPLETE,
+				"LE Request Peer SCA Complete",
+				le_req_sca_complete_evt,
+				sizeof(
+				struct bt_hci_evt_le_req_peer_sca_complete)},
 	{ }
 };
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 02/22] monitor: Add decoding of ISO related Link Layer PDUs
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 01/22] monitor: Add support for decoding ISO related commands Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 03/22] lib: Add definitions for ISO socket Luiz Augusto von Dentz
                   ` (20 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds the decoding of ISO related Link Layer PDUs
---
 monitor/bt.h |  48 ++++++++++++++++++++++++-
 monitor/ll.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 147 insertions(+), 1 deletion(-)

diff --git a/monitor/bt.h b/monitor/bt.h
index 04ad34ea9..0a88aff97 100644
--- a/monitor/bt.h
+++ b/monitor/bt.h
@@ -28,7 +28,7 @@
 #define BT_HCI_BIT_5_2		(8 * 41) + 5
 #define BT_HCI_SUBEVT_5_2	0x19
 
-#define BT_LL_CMD_MILAN		0x1f
+#define BT_LL_CMD_5_2		0x1f
 
 struct bt_ll_hdr {
 	uint8_t  preamble;
@@ -181,6 +181,52 @@ struct bt_ll_clock_acc {
 
 #define BT_LL_CLOCK_ACCURACY_RSP 0x1e
 
+#define BT_LL_CIS_REQ		BT_LL_CMD_5_2
+struct bt_ll_cis_req {
+	uint8_t  cig;
+	uint8_t  cis;
+	uint8_t  m_phy;
+	uint8_t  s_phy;
+	uint16_t m_sdu;
+	uint16_t s_sdu;
+	uint8_t  m_interval[3];
+	uint8_t  s_interval[3];
+	uint8_t  m_pdu;
+	uint8_t  s_pdu;
+	uint8_t  nse;
+	uint8_t  sub_interval[3];
+	uint8_t  bn;
+	uint8_t  m_ft;
+	uint8_t  s_ft;
+	uint16_t iso_interval;
+	uint8_t  offset_min[3];
+	uint8_t  offset_max[3];
+	uint16_t conn_event_count;
+} __attribute__ ((packed));
+
+#define BT_LL_CIS_RSP		BT_LL_CMD_5_2 + 1
+struct bt_ll_cis_rsp {
+	uint8_t  offset_min[3];
+	uint8_t  offset_max[3];
+	uint16_t conn_event_count;
+} __attribute__ ((packed));
+
+#define BT_LL_CIS_IND		BT_LL_CMD_5_2 + 2
+struct bt_ll_cis_ind {
+	uint32_t addr;
+	uint8_t  cis_offset[3];
+	uint8_t  cig_sync_delay[3];
+	uint8_t  cis_sync_delay[3];
+	uint16_t conn_event_count;
+} __attribute__ ((packed));
+
+#define BT_LL_CIS_TERMINATE_IND	BT_LL_CMD_5_2 + 3
+struct bt_ll_cis_term_ind {
+	uint8_t  cig;
+	uint8_t  cis;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
 #define LMP_ESC4(x) ((127 << 8) | (x))
 
 #define BT_LMP_NAME_REQ			1
diff --git a/monitor/ll.c b/monitor/ll.c
index 5141f2be6..4b3f567bd 100644
--- a/monitor/ll.c
+++ b/monitor/ll.c
@@ -601,6 +601,97 @@ static void clock_acc_req_rsp(const void *data, uint8_t size)
 	print_field("SCA: 0x%2.2x", pdu->sca);
 }
 
+static void cis_req(const void *data, uint8_t size)
+{
+	const struct bt_ll_cis_req *cmd = data;
+	uint32_t interval;
+	uint8_t mask;
+
+	print_field("CIG ID: 0x%2.2x", cmd->cig);
+	print_field("CIS ID: 0x%2.2x", cmd->cis);
+	print_field("Master to Slave PHY: 0x%2.2x", cmd->m_phy);
+
+	mask = print_bitfield(2, cmd->m_phy, le_phys);
+	if (mask)
+		print_text(COLOR_UNKNOWN_OPTIONS_BIT, "  Reserved"
+							" (0x%2.2x)", mask);
+
+	print_field("Slave To Master PHY: 0x%2.2x", cmd->s_phy);
+
+	mask = print_bitfield(2, cmd->s_phy, le_phys);
+	if (mask)
+		print_text(COLOR_UNKNOWN_OPTIONS_BIT, "  Reserved"
+							" (0x%2.2x)", mask);
+
+	print_field("Master to Slave Maximum SDU: %u", cmd->m_sdu);
+	print_field("Slave to Master Maximum SDU: %u", cmd->s_sdu);
+
+	memcpy(&interval, cmd->m_interval, sizeof(cmd->m_interval));
+	print_field("Master to Slave Interval: 0x%6.6x", le32_to_cpu(interval));
+	memcpy(&interval, cmd->s_interval, sizeof(cmd->s_interval));
+	print_field("Slave to Master Interval: 0x%6.6x", le32_to_cpu(interval));
+
+	print_field("Master to Slave Maximum PDU: %u", cmd->m_pdu);
+	print_field("Slave to Master Maximum PDU: %u", cmd->s_pdu);
+
+	print_field("Burst Number: %u us", cmd->bn);
+
+	memcpy(&interval, cmd->sub_interval, sizeof(cmd->sub_interval));
+	print_field("Sub-Interval: 0x%6.6x", le32_to_cpu(interval));
+
+	print_field("Master to Slave Flush Timeout: %u", cmd->m_ft);
+	print_field("Slave to Master Flush Timeout: %u", cmd->s_ft);
+
+	print_field("ISO Interval: 0x%4.4x", le16_to_cpu(cmd->iso_interval));
+
+	memcpy(&interval, cmd->offset_min, sizeof(cmd->offset_min));
+	print_field("CIS Offset Minimum: 0x%6.6x", le32_to_cpu(interval));
+	memcpy(&interval, cmd->offset_max, sizeof(cmd->offset_max));
+	print_field("CIS Offset Maximum: 0x%6.6x", le32_to_cpu(interval));
+
+	print_field("Connection Event Count: %u", cmd->conn_event_count);
+}
+
+static void cis_rsp(const void *data, uint8_t size)
+{
+	const struct bt_ll_cis_rsp *rsp = data;
+	uint32_t interval;
+
+	memcpy(&interval, rsp->offset_min, sizeof(rsp->offset_min));
+	print_field("CIS Offset Minimum: 0x%6.6x", le32_to_cpu(interval));
+	memcpy(&interval, rsp->offset_max, sizeof(rsp->offset_max));
+	print_field("CIS Offset Maximum: 0x%6.6x", le32_to_cpu(interval));
+
+	print_field("Connection Event Count: %u", rsp->conn_event_count);
+}
+
+static void cis_ind(const void *data, uint8_t size)
+{
+	const struct bt_ll_cis_ind *ind = data;
+	uint32_t interval;
+
+	print_field("CIS Access Address: 0x%4.4x", le32_to_cpu(ind->addr));
+	memcpy(&interval, ind->cis_offset, sizeof(ind->cis_offset));
+	print_field("CIS Offset: 0x%6.6x", le32_to_cpu(interval));
+
+	memcpy(&interval, ind->cig_sync_delay, sizeof(ind->cig_sync_delay));
+	print_field("CIG Synchronization Delay: 0x%6.6x",
+					le32_to_cpu(interval));
+	memcpy(&interval, ind->cis_sync_delay, sizeof(ind->cis_sync_delay));
+	print_field("CIS Synchronization Delay: %u us",
+					le32_to_cpu(interval));
+	print_field("Connection Event Count: %u", ind->conn_event_count);
+}
+
+static void cis_term_ind(const void *data, uint8_t size)
+{
+	const struct bt_ll_cis_term_ind *ind = data;
+
+	print_field("CIG ID: 0x%2.2x", ind->cig);
+	print_field("CIS ID: 0x%2.2x", ind->cis);
+	packet_print_error("Reason", ind->reason);
+}
+
 struct llcp_data {
 	uint8_t opcode;
 	const char *str;
@@ -641,6 +732,15 @@ static const struct llcp_data llcp_table[] = {
 	{ 0x1c, "LL_PERIODIC_SYNC_IND",     periodic_sync_ind, 34, true },
 	{ 0x1d, "LL_CLOCK_ACCURACY_REQ",    clock_acc_req_rsp,  1, true },
 	{ 0x1e, "LL_CLOCK_ACCURACY_RSP",    clock_acc_req_rsp,  1, true },
+	{ BT_LL_CIS_REQ, "LL_CIS_REQ",      cis_req,
+					sizeof(struct bt_ll_cis_req), true },
+	{ BT_LL_CIS_RSP, "LL_CIS_RSP",      cis_rsp,
+					sizeof(struct bt_ll_cis_rsp), true },
+	{ BT_LL_CIS_IND, "LL_CIS_IND",      cis_ind,
+					sizeof(struct bt_ll_cis_ind), true },
+	{ BT_LL_CIS_TERMINATE_IND, "LL_CIS_TERMINATE_IND", cis_term_ind,
+					sizeof(struct bt_ll_cis_term_ind),
+					true },
 	{ }
 };
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 03/22] lib: Add definitions for ISO socket
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 01/22] monitor: Add support for decoding ISO related commands Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 02/22] monitor: Add decoding of ISO related Link Layer PDUs Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 04/22] tools: Add isotest tool Luiz Augusto von Dentz
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

---
 lib/bluetooth.h | 14 ++++++++++++++
 lib/iso.h       | 45 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)
 create mode 100644 lib/iso.h

diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 756dce164..ec110f757 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -50,6 +50,7 @@ extern "C" {
 #define BTPROTO_CMTP	5
 #define BTPROTO_HIDP	6
 #define BTPROTO_AVDTP	7
+#define BTPROTO_ISO	8
 
 #define SOL_HCI		0
 #define SOL_L2CAP	6
@@ -120,6 +121,19 @@ struct bt_voice {
 #define BT_SNDMTU		12
 #define BT_RCVMTU		13
 
+#define BT_ISO_IN_QOS		15
+#define BT_ISO_OUT_QOS		16
+struct bt_iso_qos {
+	uint8_t  sca;
+	uint8_t  packing;
+	uint8_t  framing;
+	uint32_t interval;
+	uint16_t latency;
+	uint16_t mtu;
+	uint8_t  phy;
+	uint8_t  rtn;
+};
+
 #define BT_VOICE_TRANSPARENT			0x0003
 #define BT_VOICE_CVSD_16BIT			0x0060
 
diff --git a/lib/iso.h b/lib/iso.h
new file mode 100644
index 000000000..2d427b417
--- /dev/null
+++ b/lib/iso.h
@@ -0,0 +1,45 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISO_H
+#define __ISO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ISO defaults */
+#define ISO_DEFAULT_MTU		251
+
+/* ISO socket address */
+struct sockaddr_iso {
+	sa_family_t	iso_family;
+	bdaddr_t	iso_bdaddr;
+	uint8_t		iso_bdaddr_type;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ISO_H */
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 04/22] tools: Add isotest tool
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 03/22] lib: Add definitions for ISO socket Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 05/22] emulator: Add initial support for BT 5.2 Luiz Augusto von Dentz
                   ` (18 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds isotest tool which can be used to test ISO sockets.
---
 Makefile.tools  |    5 +-
 tools/isotest.c | 1019 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1023 insertions(+), 1 deletion(-)
 create mode 100644 tools/isotest.c

diff --git a/Makefile.tools b/Makefile.tools
index 006554cf7..9628b83e3 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -190,7 +190,8 @@ noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
 			tools/eddystone tools/ibeacon \
 			tools/btgatt-client tools/btgatt-server \
 			tools/test-runner tools/check-selftest \
-			tools/gatt-service profiles/iap/iapd
+			tools/gatt-service profiles/iap/iapd \
+			tools/isotest
 
 tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
 tools_bdaddr_LDADD = lib/libbluetooth-internal.la $(UDEV_LIBS)
@@ -296,6 +297,8 @@ tools_gatt_service_LDADD = $(GLIB_LIBS) $(DBUS_LIBS) gdbus/libgdbus-internal.la
 profiles_iap_iapd_SOURCES = profiles/iap/main.c
 profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la $(GLIB_LIBS) $(DBUS_LIBS)
 
+tools_isotest_LDADD = lib/libbluetooth-internal.la
+
 dist_man_MANS += tools/rctest.1 tools/l2ping.1 tools/bccmd.1 tools/btattach.1
 
 EXTRA_DIST += tools/bdaddr.1
diff --git a/tools/isotest.c b/tools/isotest.c
new file mode 100644
index 000000000..f86ac243a
--- /dev/null
+++ b/tools/isotest.c
@@ -0,0 +1,1019 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/iso.h"
+
+#include "src/shared/util.h"
+
+/* Test modes */
+enum {
+	SEND,
+	RECV,
+	RECONNECT,
+	MULTY,
+	DUMP,
+	CONNECT
+};
+
+static unsigned char *buf;
+
+/* Default data size */
+static long data_size = 251;
+
+static bdaddr_t bdaddr;
+static int bdaddr_type = BDADDR_LE_PUBLIC;
+
+static int defer_setup = 0;
+
+struct bt_iso_qos *in_qos = NULL;
+struct bt_iso_qos *out_qos = NULL;
+static bool inout;
+
+struct lookup_table {
+	const char *name;
+	int flag;
+};
+
+static struct lookup_table bdaddr_types[] = {
+	{ "le_public",	BDADDR_LE_PUBLIC	},
+	{ "le_random",	BDADDR_LE_RANDOM	},
+	{ NULL,		0			},
+};
+
+static int get_lookup_flag(struct lookup_table *table, char *name)
+{
+	int i;
+
+	for (i = 0; table[i].name; i++)
+		if (!strcasecmp(table[i].name, name))
+			return table[i].flag;
+
+	return -1;
+}
+
+static void print_lookup_values(struct lookup_table *table, char *header)
+{
+	int i;
+
+	printf("%s\n", header);
+
+	for (i = 0; table[i].name; i++)
+		printf("\t%s\n", table[i].name);
+}
+
+static float tv2fl(struct timeval tv)
+{
+	return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static int do_connect(char *peer)
+{
+	struct sockaddr_iso addr;
+	struct bt_iso_qos qos[2];
+	socklen_t len;
+	int sk;
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't create socket: %s (%d)",
+							strerror(errno), errno);
+		return -1;
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.iso_family = AF_BLUETOOTH;
+	bacpy(&addr.iso_bdaddr, &bdaddr);
+	addr.iso_bdaddr_type = bdaddr_type;
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Set QoS if available */
+	if (out_qos) {
+		if (setsockopt(sk, SOL_BLUETOOTH, BT_ISO_OUT_QOS,
+					out_qos, sizeof(*out_qos)) < 0) {
+			syslog(LOG_ERR, "Can't set Output QoS socket option: "
+					"%s (%d)", strerror(errno), errno);
+		}
+	}
+
+	if (in_qos) {
+		if (!inout) {
+			in_qos->phy = 0x00;
+			in_qos->mtu = 0;
+		}
+
+		if (setsockopt(sk, SOL_BLUETOOTH, BT_ISO_IN_QOS,
+					in_qos, sizeof(*in_qos)) < 0) {
+			syslog(LOG_ERR, "Can't set Input QoS socket option: "
+					"%s (%d)", strerror(errno), errno);
+		}
+	}
+
+	/* Connect to remote device */
+	memset(&addr, 0, sizeof(addr));
+	addr.iso_family = AF_BLUETOOTH;
+	str2ba(peer, &addr.iso_bdaddr);
+	addr.iso_bdaddr_type = bdaddr_type;
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't connect: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Read Out QOS */
+	memset(&qos, 0, sizeof(qos));
+	len = sizeof(qos[0]);
+
+	if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_IN_QOS, &qos[0], &len) < 0) {
+		syslog(LOG_ERR, "Can't get Input QoS socket option: %s (%d)",
+				strerror(errno), errno);
+		goto error;
+	}
+
+	if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_OUT_QOS, &qos[1], &len) < 0) {
+		syslog(LOG_ERR, "Can't get Output QoS socket option: %s (%d)",
+				strerror(errno), errno);
+		goto error;
+	}
+
+	syslog(LOG_INFO, "Connected [%s]", peer);
+	syslog(LOG_INFO, "Input QoS [Interval %u us Framing 0x%02x Latency %u "
+		"ms MTU %u PHY 0x%02x RTN %u]", qos[0].interval, qos[0].framing,
+		qos[0].latency, qos[0].mtu, qos[0].phy, qos[0].rtn);
+	syslog(LOG_INFO, "Output QoS [Interval %u us Framing 0x%02x Latency %u "
+		"ms MTU %u PHY 0x%02x RTN %u]", qos[1].interval, qos[1].framing,
+		qos[1].latency, qos[1].mtu, qos[1].phy, qos[1].rtn);
+
+	return sk;
+
+error:
+	close(sk);
+	return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+	struct sockaddr_iso addr;
+	socklen_t optlen;
+	int sk, nsk;
+	char ba[18];
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't create socket: %s (%d)",
+							strerror(errno), errno);
+		exit(1);
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.iso_family = AF_BLUETOOTH;
+	bacpy(&addr.iso_bdaddr, &bdaddr);
+	addr.iso_bdaddr_type = bdaddr_type;
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Enable deferred setup */
+	if (defer_setup && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP,
+				&defer_setup, sizeof(defer_setup)) < 0) {
+		syslog(LOG_ERR, "Can't enable deferred setup : %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Listen for connections */
+	if (listen(sk, 10)) {
+		syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	syslog(LOG_INFO,"Waiting for connection ...");
+
+	while (1) {
+		memset(&addr, 0, sizeof(addr));
+		optlen = sizeof(addr);
+
+		nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+		if (nsk < 0) {
+			syslog(LOG_ERR,"Accept failed: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+		if (fork()) {
+			/* Parent */
+			close(nsk);
+			continue;
+		}
+		/* Child */
+		close(sk);
+
+		ba2str(&addr.iso_bdaddr, ba);
+		syslog(LOG_INFO, "Connect from %s", ba);
+
+		/* Handle deferred setup */
+		if (defer_setup) {
+			syslog(LOG_INFO, "Waiting for %d seconds",
+							abs(defer_setup) - 1);
+			sleep(abs(defer_setup) - 1);
+
+			if (defer_setup < 0) {
+				close(nsk);
+				exit(1);
+			}
+		}
+
+		handler(nsk);
+
+		syslog(LOG_INFO, "Disconnect");
+		exit(0);
+	}
+
+error:
+	close(sk);
+	exit(1);
+}
+
+static void dump_mode(int sk)
+{
+	int len;
+
+	if (defer_setup) {
+		len = read(sk, buf, data_size);
+		if (len < 0)
+			syslog(LOG_ERR, "Initial read error: %s (%d)",
+						strerror(errno), errno);
+		else
+			syslog(LOG_INFO, "Initial bytes %d", len);
+	}
+
+	syslog(LOG_INFO,"Receiving ...");
+	while ((len = read(sk, buf, data_size)) > 0)
+		syslog(LOG_INFO, "Received %d bytes", len);
+}
+
+static void recv_mode(int sk)
+{
+	struct timeval tv_beg,tv_end,tv_diff;
+	long total;
+	int len;
+
+	if (defer_setup) {
+		len = read(sk, buf, data_size);
+		if (len < 0)
+			syslog(LOG_ERR, "Initial read error: %s (%d)",
+						strerror(errno), errno);
+		else
+			syslog(LOG_INFO, "Initial bytes %d", len);
+	}
+
+	syslog(LOG_INFO, "Receiving ...");
+
+	while (1) {
+		gettimeofday(&tv_beg, NULL);
+		total = 0;
+		while (total < data_size) {
+			int r;
+
+			r = recv(sk, buf, data_size, 0);
+			if (r <= 0) {
+				if (r < 0)
+					syslog(LOG_ERR, "Read failed: %s (%d)",
+							strerror(errno), errno);
+				if (errno != ENOTCONN)
+					return;
+				r = 0;
+			}
+			total += r;
+		}
+		gettimeofday(&tv_end, NULL);
+
+		timersub(&tv_end, &tv_beg, &tv_diff);
+
+		syslog(LOG_INFO,"%ld bytes in %.2fm speed %.2f kb", total,
+			tv2fl(tv_diff) / 60.0,
+			(float)( total / tv2fl(tv_diff) ) / 1024.0 );
+	}
+}
+
+static void send_mode(char *peer)
+{
+	struct bt_iso_qos qos;
+	socklen_t len;
+	uint32_t seq;
+	int i, sk;
+
+	sk = do_connect(peer);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+							strerror(errno), errno);
+		exit(1);
+	}
+
+	syslog(LOG_INFO,"Sending ...");
+
+	/* Read Out QOS */
+	memset(&qos, 0, sizeof(qos));
+	len = sizeof(qos);
+	if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_OUT_QOS, &qos, &len) < 0) {
+		syslog(LOG_ERR, "Can't get Output QoS socket option: %s (%d)",
+				strerror(errno), errno);
+		qos.mtu = ISO_DEFAULT_MTU;
+	}
+
+	for (i = 6; i < qos.mtu; i++)
+		buf[i] = 0x7f;
+
+	seq = 0;
+	while (1) {
+		put_le32(seq, buf);
+		put_le16(data_size, buf + 4);
+
+		seq++;
+
+		if (send(sk, buf, qos.mtu, 0) <= 0) {
+			syslog(LOG_ERR, "Send failed: %s (%d)",
+							strerror(errno), errno);
+			exit(1);
+		}
+
+		usleep(qos.interval);
+	}
+}
+
+static void reconnect_mode(char *peer)
+{
+	while (1) {
+		int sk;
+
+		sk = do_connect(peer);
+		if (sk < 0) {
+			syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+							strerror(errno), errno);
+			exit(1);
+		}
+
+		close(sk);
+
+		sleep(5);
+	}
+}
+
+static void multy_connect_mode(char *peer)
+{
+	while (1) {
+		int i, sk;
+
+		for (i = 0; i < 10; i++){
+			if (fork())
+				continue;
+
+			/* Child */
+			sk = do_connect(peer);
+			if (sk < 0) {
+				syslog(LOG_ERR, "Can't connect to the server: "
+					"%s (%d)", strerror(errno), errno);
+			}
+			close(sk);
+			exit(0);
+		}
+
+		sleep(19);
+	}
+}
+
+static void usage(void)
+{
+	printf("isotest - ISO testing\n"
+		"Usage:\n");
+	printf("\tisotest <mode> [options] [bdaddr]\n");
+	printf("Modes:\n"
+		"\t-d dump (server)\n"
+		"\t-c reconnect (client)\n"
+		"\t-m multiple connects (client)\n"
+		"\t-r receive (server)\n"
+		"\t-s connect and send (client)\n"
+		"\t-n connect and be silent (client)\n"
+		"Options:\n"
+		"\t[-b bytes]\n"
+		"\t[-i device]\n"
+		"\t[-W seconds] enable deferred setup\n"
+		"\t[-M mtu]\n"
+		"\t[-S sca]\n"
+		"\t[-P packing]\n"
+		"\t[-F framing]\n"
+		"\t[-I interval]\n"
+		"\t[-L latency]\n"
+		"\t[-Y phy]\n"
+		"\t[-R rtn]\n"
+		"\t[-B preset]\n");
+}
+
+static struct qos_preset {
+	const char *name;
+	bool inout;
+	struct bt_iso_qos qos;
+} presets[] = {
+	{
+		.name = "1.1",
+		.inout = true,
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 8,
+			.mtu = 30,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "1.2",
+		.inout = true,
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 10,
+			.mtu = 40,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "1.3",
+		.inout = true,
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 8,
+			.mtu = 45,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "1.4",
+		.inout = true,
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 10,
+			.mtu = 60,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "1.5",
+		.inout = true,
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 8,
+			.mtu = 60,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "1.6",
+		.inout = true,
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 10,
+			.mtu = 80,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "2.1",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 60,
+			.mtu = 45,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "2.2",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 80,
+			.mtu = 60,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "3.0",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 8,
+			.mtu = 75,
+			.phy = 0x02,
+			.rtn = 3,
+		}
+	},
+	{
+		.name = "3.1",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 10,
+			.mtu = 100,
+			.phy = 0x02,
+			.rtn = 3,
+		}
+	},
+	{
+		.name = "3.2",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 8,
+			.mtu = 90,
+			.phy = 0x02,
+			.rtn = 3,
+		}
+	},
+	{
+		.name = "3.3",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 10,
+			.mtu = 120,
+			.phy = 0x02,
+			.rtn = 3,
+		}
+	},
+	{
+		.name = "3.4",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 8,
+			.mtu = 117,
+			.phy = 0x02,
+			.rtn = 3,
+		}
+	},
+	{
+		.name = "3.5",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 10,
+			.mtu = 155,
+			.phy = 0x02,
+			.rtn = 3,
+		}
+	},
+	{
+		.name = "3.6",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10880,
+			.latency = 11,
+			.mtu = 120,
+			.phy = 0x02,
+			.rtn = 3,
+		}
+	},
+	{
+		.name = "4.0",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 60,
+			.mtu = 75,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.1",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 80,
+			.mtu = 100,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.2",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 60,
+			.mtu = 90,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.3",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 60,
+			.mtu = 180,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.4",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 80,
+			.mtu = 120,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.5",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 80,
+			.mtu = 240,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.6",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 60,
+			.mtu = 117,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.7",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 7500,
+			.latency = 60,
+			.mtu = 234,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.8",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 80,
+			.mtu = 155,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+	{
+		.name = "4.9",
+		.qos = {
+			.sca = 0x07,
+			.packing = 0x00,
+			.framing = 0x00,
+			.interval = 10000,
+			.latency = 88,
+			.mtu = 120,
+			.phy = 0x02,
+			.rtn = 2,
+		}
+	},
+};
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+int main(int argc ,char *argv[])
+{
+	struct sigaction sa;
+	int opt, sk, mode = RECV;
+	unsigned int i;
+
+	while ((opt = getopt(argc, argv, "rdscmnb:i:V:W:M:S:P:F:I:L:Y:R:B:")) !=
+									EOF) {
+		switch(opt) {
+		case 'r':
+			mode = RECV;
+			break;
+
+		case 's':
+			mode = SEND;
+			break;
+
+		case 'd':
+			mode = DUMP;
+			break;
+
+		case 'c':
+			mode = RECONNECT;
+			break;
+
+		case 'm':
+			mode = MULTY;
+			break;
+
+		case 'n':
+			mode = CONNECT;
+			break;
+
+		case 'b':
+			data_size = atoi(optarg);
+			break;
+
+		case 'i':
+			if (!strncasecmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &bdaddr);
+			else
+				str2ba(optarg, &bdaddr);
+			break;
+
+		case 'V':
+			bdaddr_type = get_lookup_flag(bdaddr_types, optarg);
+
+			if (bdaddr_type == -1) {
+				print_lookup_values(bdaddr_types,
+						"List Address types:");
+				exit(1);
+			}
+
+			break;
+
+		case 'W':
+			defer_setup = atoi(optarg);
+			break;
+
+		case 'M':
+			if (!out_qos) {
+				out_qos = malloc(sizeof(*out_qos));
+				memset(out_qos, 0, sizeof(*out_qos));
+			}
+
+			out_qos->mtu = atoi(optarg);
+
+			break;
+
+		case 'S':
+			if (!out_qos) {
+				out_qos = malloc(sizeof(*out_qos));
+				memset(out_qos, 0, sizeof(*out_qos));
+			}
+
+			out_qos->sca = atoi(optarg);
+
+			break;
+
+
+		case 'P':
+			if (!out_qos) {
+				out_qos = malloc(sizeof(*out_qos));
+				memset(out_qos, 0, sizeof(*out_qos));
+			}
+
+			out_qos->packing = atoi(optarg);
+
+			break;
+
+		case 'F':
+			if (!out_qos) {
+				out_qos = malloc(sizeof(*out_qos));
+				memset(out_qos, 0, sizeof(*out_qos));
+			}
+
+			out_qos->framing = atoi(optarg);
+
+			break;
+
+		case 'I':
+			if (!out_qos) {
+				out_qos = malloc(sizeof(*out_qos));
+				memset(out_qos, 0, sizeof(*out_qos));
+			}
+
+			out_qos->interval = atoi(optarg);
+
+			break;
+
+		case 'L':
+			if (!out_qos) {
+				out_qos = malloc(sizeof(*out_qos));
+				memset(out_qos, 0, sizeof(*out_qos));
+			}
+
+			out_qos->latency = atoi(optarg);
+
+			break;
+
+		case 'Y':
+			if (!out_qos) {
+				out_qos = malloc(sizeof(*out_qos));
+				memset(out_qos, 0, sizeof(*out_qos));
+			}
+
+			out_qos->phy = atoi(optarg);
+
+			break;
+
+		case 'R':
+			if (!out_qos) {
+				out_qos = malloc(sizeof(*out_qos));
+				memset(out_qos, 0, sizeof(*out_qos));
+			}
+
+			out_qos->rtn = atoi(optarg);
+
+			break;
+
+		case 'B':
+			for (i = 0; i < ARRAY_SIZE(presets); i++) {
+				if (!strcmp(presets[i].name, optarg)) {
+					out_qos = &presets[i].qos;
+					inout = presets[i].inout;
+					break;
+				}
+			}
+
+			if (out_qos)
+				break;
+
+		/* Fallthrough */
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	if (!(argc - optind) && (mode != RECV && mode != DUMP)) {
+		usage();
+		exit(1);
+	}
+
+	if (out_qos && !in_qos)
+		in_qos = out_qos;
+
+	buf = malloc(data_size);
+	if (!buf) {
+		perror("Can't allocate data buffer");
+		exit(1);
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = SIG_IGN;
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sigaction(SIGCHLD, &sa, NULL);
+
+	openlog("isotest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+	switch( mode ){
+	case RECV:
+		do_listen(recv_mode);
+		break;
+
+	case DUMP:
+		do_listen(dump_mode);
+		break;
+
+	case SEND:
+		send_mode(argv[optind]);
+		break;
+
+	case RECONNECT:
+		reconnect_mode(argv[optind]);
+		break;
+
+	case MULTY:
+		multy_connect_mode(argv[optind]);
+		break;
+
+	case CONNECT:
+		sk = do_connect(argv[optind]);
+		if (sk < 0)
+			exit(1);
+		dump_mode(sk);
+		break;
+	}
+
+	syslog(LOG_INFO, "Exit");
+
+	closelog();
+
+	return 0;
+}
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 05/22] emulator: Add initial support for BT 5.2
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 04/22] tools: Add isotest tool Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 06/22] monitor: Add support for ISO packets Luiz Augusto von Dentz
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds the initial command/event decoding for 5.2.
---
 emulator/btdev.c  | 271 +++++++++++++++++++++++++++++++++++++++-------
 emulator/btdev.h  |   1 +
 emulator/hciemu.c |   3 +
 emulator/hciemu.h |   1 +
 emulator/vhci.c   |   3 +-
 5 files changed, 236 insertions(+), 43 deletions(-)

diff --git a/emulator/btdev.c b/emulator/btdev.c
index 38d5b3b1f..02f408dd5 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -47,6 +47,9 @@
 #define has_bredr(btdev)	(!((btdev)->features[4] & 0x20))
 #define has_le(btdev)		(!!((btdev)->features[4] & 0x40))
 
+#define ACL_HANDLE 42
+#define ISO_HANDLE 44
+
 struct hook {
 	btdev_hook_func handler;
 	void *user_data;
@@ -92,6 +95,8 @@ struct btdev {
 	uint8_t  feat_page_2[8];
 	uint16_t acl_mtu;
 	uint16_t acl_max_pkt;
+	uint16_t iso_mtu;
+	uint16_t iso_max_pkt;
 	uint8_t  country_code;
 	uint8_t  bdaddr[6];
 	uint8_t  random_addr[6];
@@ -438,6 +443,30 @@ static void set_le_50_commands(struct btdev *btdev)
 	btdev->commands[38] |= 0x40;	/* LE Read Periodic Adv List Size */
 }
 
+static void set_le_60_commands(struct btdev *btdev)
+{
+	btdev->commands[41] |= 0x20;	/* LE Read Buffer Size v2 */
+	btdev->commands[41] |= 0x40;	/* LE Read ISO TX Sync */
+	btdev->commands[41] |= 0x80;	/* LE Set CIG Parameters */
+	btdev->commands[42] |= 0x01;	/* LE Set CIG Parameters Test */
+	btdev->commands[42] |= 0x02;	/* LE Create CIS */
+	btdev->commands[42] |= 0x04;	/* LE Remove CIG */
+	btdev->commands[42] |= 0x08;	/* LE Accept CIS */
+	btdev->commands[42] |= 0x10;	/* LE Reject CIS */
+	btdev->commands[42] |= 0x20;	/* LE Create BIG */
+	btdev->commands[42] |= 0x40;	/* LE Create BIG Test */
+	btdev->commands[42] |= 0x80;	/* LE Terminate BIG */
+	btdev->commands[43] |= 0x01;	/* LE BIG Create Sync */
+	btdev->commands[43] |= 0x02;	/* LE BIG Terminate Sync */
+	btdev->commands[43] |= 0x04;	/* LE Request Peer SCA */
+	btdev->commands[43] |= 0x08;	/* LE Setup ISO Path */
+	btdev->commands[43] |= 0x10;	/* LE Remove ISO Path */
+	btdev->commands[43] |= 0x20;	/* LE ISO TX Test */
+	btdev->commands[43] |= 0x40;	/* LE ISO RX Test */
+	btdev->commands[43] |= 0x80;	/* LE ISO Read Test Counter */
+	btdev->commands[44] |= 0x01;	/* LE ISO Test End */
+}
+
 static void set_le_commands(struct btdev *btdev)
 {
 	set_common_commands_all(btdev);
@@ -482,6 +511,10 @@ static void set_le_commands(struct btdev *btdev)
 	/* Extra LE commands for >= 5.0 adapters */
 	if (btdev->type >= BTDEV_TYPE_BREDRLE50)
 		set_le_50_commands(btdev);
+
+	/* Extra LE commands for >= 6.0 adapters */
+	if (btdev->type >= BTDEV_TYPE_BREDRLE60)
+		set_le_60_commands(btdev);
 }
 
 static void set_bredrle_commands(struct btdev *btdev)
@@ -550,6 +583,14 @@ static void set_bredrle_features(struct btdev *btdev)
 		btdev->le_features[1] |= 0x10;  /* LE EXT ADV */
 	}
 
+	if (btdev->type >= BTDEV_TYPE_BREDRLE60) {
+		btdev->le_features[3] |= 0x10;  /* LE CIS Master */
+		btdev->le_features[3] |= 0x20;  /* LE CIS Slave */
+		btdev->le_features[3] |= 0x40;  /* LE ISO Broadcaster */
+		btdev->le_features[3] |= 0x80;  /* LE Synchronized Receiver */
+		btdev->le_features[4] |= 0x01;  /* LE ISO channels */
+	}
+
 	btdev->feat_page_2[0] |= 0x01;	/* CSB - Master Operation */
 	btdev->feat_page_2[0] |= 0x02;	/* CSB - Slave Operation */
 	btdev->feat_page_2[0] |= 0x04;	/* Synchronization Train */
@@ -644,8 +685,9 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
 
 	memset(btdev, 0, sizeof(*btdev));
 
-	if (type == BTDEV_TYPE_BREDRLE || type == BTDEV_TYPE_LE
-				|| type == BTDEV_TYPE_BREDRLE50) {
+	if (type == BTDEV_TYPE_BREDRLE || type == BTDEV_TYPE_LE ||
+			type == BTDEV_TYPE_BREDRLE50 ||
+			type == BTDEV_TYPE_BREDRLE60) {
 		btdev->crypto = bt_crypto_new();
 		if (!btdev->crypto) {
 			free(btdev);
@@ -661,6 +703,7 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
 	switch (btdev->type) {
 	case BTDEV_TYPE_BREDRLE:
 	case BTDEV_TYPE_BREDRLE50:
+	case BTDEV_TYPE_BREDRLE60:
 		btdev->version = 0x09;
 		set_bredrle_features(btdev);
 		set_bredrle_commands(btdev);
@@ -700,6 +743,9 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
 	btdev->acl_mtu = 192;
 	btdev->acl_max_pkt = 1;
 
+	btdev->iso_mtu = 251;
+	btdev->iso_max_pkt = 1;
+
 	btdev->country_code = 0x00;
 
 	index = add_btdev(btdev);
@@ -893,13 +939,13 @@ static void le_meta_event(struct btdev *btdev, uint8_t event,
 	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, pkt_data, 1 + len);
 }
 
-static void num_completed_packets(struct btdev *btdev)
+static void num_completed_packets(struct btdev *btdev, uint16_t handle)
 {
 	if (btdev->conn) {
 		struct bt_hci_evt_num_completed_packets ncp;
 
 		ncp.num_handles = 1;
-		ncp.handle = cpu_to_le16(42);
+		ncp.handle = cpu_to_le16(handle);
 		ncp.count = cpu_to_le16(1);
 
 		send_event(btdev, BT_HCI_EVT_NUM_COMPLETED_PACKETS,
@@ -1106,12 +1152,12 @@ static void conn_complete(struct btdev *btdev,
 		memcpy(cc.bdaddr, btdev->bdaddr, 6);
 		cc.encr_mode = 0x00;
 
-		cc.handle = cpu_to_le16(42);
+		cc.handle = cpu_to_le16(ACL_HANDLE);
 		cc.link_type = 0x01;
 
 		send_event(remote, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
 
-		cc.handle = cpu_to_le16(42);
+		cc.handle = cpu_to_le16(ACL_HANDLE);
 		cc.link_type = 0x01;
 	} else {
 		cc.handle = cpu_to_le16(0x0000);
@@ -1208,7 +1254,7 @@ static void le_conn_complete(struct btdev *btdev,
 			memcpy(cc->peer_addr, btdev->bdaddr, 6);
 
 		cc->role = 0x01;
-		cc->handle = cpu_to_le16(42);
+		cc->handle = cpu_to_le16(ACL_HANDLE);
 		cc->interval = lecc->max_interval;
 		cc->latency = lecc->latency;
 		cc->supv_timeout = lecc->supv_timeout;
@@ -1255,7 +1301,7 @@ static void le_ext_conn_complete(struct btdev *btdev,
 			memcpy(cc->peer_addr, btdev->bdaddr, 6);
 
 		cc->role = 0x01;
-		cc->handle = cpu_to_le16(42);
+		cc->handle = cpu_to_le16(ACL_HANDLE);
 		cc->interval = lecc->max_interval;
 		cc->latency = lecc->latency;
 		cc->supv_timeout = lecc->supv_timeout;
@@ -1356,6 +1402,46 @@ static void le_ext_conn_request(struct btdev *btdev,
 					BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH);
 }
 
+static void le_cis_estabilished(struct btdev *dev, uint8_t status)
+{
+	struct bt_hci_evt_le_cis_established evt;
+
+	memset(&evt, 0, sizeof(evt));
+
+	evt.status = status;
+
+	if (!evt.status) {
+		evt.conn_handle = cpu_to_le16(ISO_HANDLE);
+		evt.m_phy = 0x01;
+		evt.m_phy = 0x01;
+	}
+
+	le_meta_event(dev, BT_HCI_EVT_LE_CIS_ESTABLISHED, &evt, sizeof(evt));
+
+	if (dev->conn)
+		le_meta_event(dev->conn, BT_HCI_EVT_LE_CIS_ESTABLISHED, &evt,
+							sizeof(evt));
+}
+
+static void le_cis_request(struct btdev *dev,
+				const struct bt_hci_cmd_le_create_cis *leccis)
+{
+	struct btdev *remote = dev->conn;
+
+	if (remote) {
+		struct bt_hci_evt_le_cis_req evt;
+
+		evt.acl_handle = cpu_to_le16(ACL_HANDLE);
+		evt.cis_handle = cpu_to_le16(ISO_HANDLE);
+		evt.cis_id = 0x00;
+		evt.cis_id = 0x00;
+
+		le_meta_event(remote, BT_HCI_EVT_LE_CIS_REQ, &evt, sizeof(evt));
+	} else {
+		le_cis_estabilished(dev, BT_HCI_ERR_UNKNOWN_CONN_ID);
+	}
+}
+
 static void conn_request(struct btdev *btdev, const uint8_t *bdaddr)
 {
 	struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
@@ -1464,8 +1550,10 @@ static void disconnect_complete(struct btdev *btdev, uint16_t handle,
 	dc.handle = cpu_to_le16(handle);
 	dc.reason = reason;
 
-	btdev->conn = NULL;
-	remote->conn = NULL;
+	if (dc.handle == ACL_HANDLE) {
+		btdev->conn = NULL;
+		remote->conn = NULL;
+	}
 
 	send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
 	send_event(remote, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
@@ -1492,7 +1580,7 @@ static void link_key_req_reply_complete(struct btdev *btdev,
 		return;
 	}
 
-	ev.handle = cpu_to_le16(42);
+	ev.handle = cpu_to_le16(ACL_HANDLE);
 
 	if (!memcmp(btdev->link_key, remote->link_key, 16))
 		ev.status = BT_HCI_ERR_SUCCESS;
@@ -1587,7 +1675,7 @@ static void encrypt_change(struct btdev *btdev, uint8_t mode, uint8_t status)
 	struct bt_hci_evt_encrypt_change ev;
 
 	ev.status = status;
-	ev.handle = cpu_to_le16(42);
+	ev.handle = cpu_to_le16(ACL_HANDLE);
 	ev.encr_mode = mode;
 
 	send_event(btdev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
@@ -1628,7 +1716,7 @@ static void pin_code_req_reply_complete(struct btdev *btdev,
 	}
 
 	if (remote->conn) {
-		ev.handle = cpu_to_le16(42);
+		ev.handle = cpu_to_le16(ACL_HANDLE);
 		send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
 	} else {
 		conn_complete(remote, btdev->bdaddr, ev.status);
@@ -1651,7 +1739,7 @@ static void pin_code_req_neg_reply_complete(struct btdev *btdev,
 	}
 
 	ev.status = BT_HCI_ERR_PIN_OR_KEY_MISSING;
-	ev.handle = cpu_to_le16(42);
+	ev.handle = cpu_to_le16(ACL_HANDLE);
 
 	if (btdev->conn)
 		send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
@@ -1944,7 +2032,7 @@ static void ssp_complete(struct btdev *btdev, const uint8_t *bdaddr,
 	}
 
 	auth.status = status;
-	auth.handle = cpu_to_le16(42);
+	auth.handle = cpu_to_le16(ACL_HANDLE);
 	send_event(init, BT_HCI_EVT_AUTH_COMPLETE, &auth, sizeof(auth));
 }
 
@@ -2207,7 +2295,7 @@ static void le_read_remote_features_complete(struct btdev *btdev)
 	memset(buf, 0, sizeof(buf));
 	buf[0] = BT_HCI_EVT_LE_REMOTE_FEATURES_COMPLETE;
 	ev->status = BT_HCI_ERR_SUCCESS;
-	ev->handle = cpu_to_le16(42);
+	ev->handle = cpu_to_le16(ACL_HANDLE);
 	memcpy(ev->features, remote->le_features, 8);
 
 	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
@@ -2230,7 +2318,7 @@ static void le_start_encrypt_complete(struct btdev *btdev, uint16_t ediv,
 
 	memset(buf, 0, sizeof(buf));
 	buf[0] = BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST;
-	ev->handle = cpu_to_le16(42);
+	ev->handle = cpu_to_le16(ACL_HANDLE);
 	ev->ediv = ediv;
 	ev->rand = rand;
 
@@ -2244,7 +2332,7 @@ static void le_encrypt_complete(struct btdev *btdev)
 	struct btdev *remote = btdev->conn;
 
 	memset(&rp, 0, sizeof(rp));
-	rp.handle = cpu_to_le16(42);
+	rp.handle = cpu_to_le16(ACL_HANDLE);
 
 	if (!remote) {
 		rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
@@ -2266,7 +2354,7 @@ static void le_encrypt_complete(struct btdev *btdev)
 		ev.encr_mode = 0x01;
 	}
 
-	ev.handle = cpu_to_le16(42);
+	ev.handle = cpu_to_le16(ACL_HANDLE);
 
 	send_event(btdev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
 	send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
@@ -2279,7 +2367,7 @@ static void ltk_neg_reply_complete(struct btdev *btdev)
 	struct btdev *remote = btdev->conn;
 
 	memset(&rp, 0, sizeof(rp));
-	rp.handle = cpu_to_le16(42);
+	rp.handle = cpu_to_le16(ACL_HANDLE);
 
 	if (!remote) {
 		rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
@@ -2293,7 +2381,7 @@ static void ltk_neg_reply_complete(struct btdev *btdev)
 
 	memset(&ev, 0, sizeof(ev));
 	ev.status = BT_HCI_ERR_PIN_OR_KEY_MISSING;
-	ev.handle = cpu_to_le16(42);
+	ev.handle = cpu_to_le16(ACL_HANDLE);
 
 	send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
 }
@@ -2364,6 +2452,7 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 	const struct bt_hci_cmd_le_set_ext_scan_params *lsesp;
 	const struct bt_hci_le_scan_phy *lsp;
 	const struct bt_hci_cmd_le_set_ext_scan_enable *lsese;
+	const struct bt_hci_cmd_le_reject_cis *lrcis;
 	struct bt_hci_rsp_read_default_link_policy rdlp;
 	struct bt_hci_rsp_read_stored_link_key rslk;
 	struct bt_hci_rsp_write_stored_link_key wslk;
@@ -2425,6 +2514,11 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 	struct bt_hci_evt_le_generate_dhkey_complete dh_evt;
 	struct bt_hci_rsp_le_read_num_supported_adv_sets rlrnsas;
 	struct bt_hci_rsp_le_set_ext_adv_params rlseap;
+	struct bt_hci_rsp_le_read_buffer_size_v2 lrbsv2;
+	struct lescp {
+		struct bt_hci_rsp_le_set_cig_params params;
+		uint16_t handle;
+	} __attribute__ ((packed)) lscp;
 	uint8_t status, page;
 
 	switch (opcode) {
@@ -2960,7 +3054,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 
 	case BT_HCI_CMD_READ_LE_HOST_SUPPORTED:
 		if (btdev->type != BTDEV_TYPE_BREDRLE &&
-				btdev->type != BTDEV_TYPE_BREDRLE50)
+				btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 		rlhs.status = BT_HCI_ERR_SUCCESS;
 		rlhs.supported = btdev->le_supported;
@@ -2971,7 +3066,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 	case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED:
 		if (btdev->type != BTDEV_TYPE_BREDRLE &&
 				btdev->type != BTDEV_TYPE_LE &&
-				btdev->type != BTDEV_TYPE_BREDRLE50)
+				btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 		wlhs = data;
 		btdev->le_supported = wlhs->supported;
@@ -2982,7 +3078,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 
 	case BT_HCI_CMD_READ_SECURE_CONN_SUPPORT:
 		if (btdev->type != BTDEV_TYPE_BREDRLE &&
-				btdev->type != BTDEV_TYPE_BREDRLE50)
+				btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 		rscs.status = BT_HCI_ERR_SUCCESS;
 		rscs.support = btdev->secure_conn_support;
@@ -2991,7 +3088,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 
 	case BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT:
 		if (btdev->type != BTDEV_TYPE_BREDRLE &&
-				btdev->type != BTDEV_TYPE_BREDRLE50)
+				btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 		wscs = data;
 		btdev->secure_conn_support = wscs->support;
@@ -3001,7 +3099,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 
 	case BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA:
 		if (btdev->type != BTDEV_TYPE_BREDRLE &&
-				btdev->type != BTDEV_TYPE_BREDRLE50)
+				btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 		rloed.status = BT_HCI_ERR_SUCCESS;
 		cmd_complete(btdev, opcode, &rloed, sizeof(rloed));
@@ -3009,7 +3108,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 
 	case BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS:
 		if (btdev->type != BTDEV_TYPE_BREDRLE &&
-				btdev->type != BTDEV_TYPE_BREDRLE50)
+				btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 		rstp.status = BT_HCI_ERR_SUCCESS;
 		rstp.interval = cpu_to_le16(btdev->sync_train_interval);
@@ -3160,7 +3260,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 	case BT_HCI_CMD_READ_ENCRYPT_KEY_SIZE:
 		if (btdev->type != BTDEV_TYPE_BREDRLE &&
 					btdev->type != BTDEV_TYPE_BREDR &&
-					btdev->type != BTDEV_TYPE_BREDRLE50)
+					btdev->type != BTDEV_TYPE_BREDRLE50 &&
+					btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 		reks = data;
 		read_enc_key_size_complete(btdev, le16_to_cpu(reks->handle));
@@ -3535,7 +3636,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &lcprnr_rsp, sizeof(lcprnr_rsp));
 		break;
 	case BT_HCI_CMD_LE_READ_NUM_SUPPORTED_ADV_SETS:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		rlrnsas.status = BT_HCI_ERR_SUCCESS;
@@ -3544,7 +3646,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &rlrnsas, sizeof(rlrnsas));
 		break;
 	case BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		lsasra = data;
@@ -3553,7 +3656,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &status, sizeof(status));
 		break;
 	case BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		if (btdev->le_adv_enable) {
@@ -3573,7 +3677,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &rlseap, sizeof(rlseap));
 		break;
 	case BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		lseae = data;
@@ -3588,7 +3693,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 			le_set_ext_adv_enable_complete(btdev);
 		break;
 	case BT_HCI_CMD_LE_SET_EXT_ADV_DATA:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		lsead = data;
@@ -3598,7 +3704,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &status, sizeof(status));
 		break;
 	case BT_HCI_CMD_LE_SET_EXT_SCAN_RSP_DATA:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		lsesrd = data;
@@ -3608,14 +3715,16 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &status, sizeof(status));
 		break;
 	case BT_HCI_CMD_LE_REMOVE_ADV_SET:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		status = BT_HCI_ERR_SUCCESS;
 		cmd_complete(btdev, opcode, &status, sizeof(status));
 		break;
 	case BT_HCI_CMD_LE_CLEAR_ADV_SETS:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		status = BT_HCI_ERR_SUCCESS;
@@ -3636,7 +3745,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &status, sizeof(status));
 		break;
 	case BT_HCI_CMD_LE_SET_EXT_SCAN_PARAMS:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		lsesp = data;
@@ -3658,7 +3768,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &status, sizeof(status));
 		break;
 	case BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		lsese = data;
@@ -3672,11 +3783,62 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_complete(btdev, opcode, &status, sizeof(status));
 		break;
 	case BT_HCI_CMD_LE_EXT_CREATE_CONN:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
+			goto unsupported;
+
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_LE_READ_BUFFER_SIZE_V2:
+		if (btdev->type != BTDEV_TYPE_BREDRLE60)
+			goto unsupported;
+		lrbsv2.status = BT_HCI_ERR_SUCCESS;
+		lrbsv2.acl_mtu = cpu_to_le16(btdev->acl_mtu);
+		lrbsv2.acl_max_pkt = btdev->acl_max_pkt;
+		lrbsv2.iso_mtu = cpu_to_le16(btdev->iso_mtu);
+		lrbsv2.iso_max_pkt = btdev->iso_max_pkt;
+		cmd_complete(btdev, opcode, &lrbsv2, sizeof(lrbsv2));
+		break;
+
+	case BT_HCI_CMD_LE_SET_CIG_PARAMS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE60)
+			goto unsupported;
+		lscp.params.status = BT_HCI_ERR_SUCCESS;
+		lscp.params.cig_id = 0x00;
+		lscp.params.num_handles = 1;
+		lscp.handle = cpu_to_le16(ISO_HANDLE);
+		cmd_complete(btdev, opcode, &lscp, sizeof(lscp));
+		break;
+
+	case BT_HCI_CMD_LE_CREATE_CIS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE60)
+			goto unsupported;
+
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+
+		break;
+
+	case BT_HCI_CMD_LE_ACCEPT_CIS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
 
 		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		le_cis_estabilished(btdev, BT_HCI_ERR_SUCCESS);
+
 		break;
+
+	case BT_HCI_CMD_LE_REJECT_CIS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE60)
+			goto unsupported;
+
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+
+		lrcis = data;
+		le_cis_estabilished(btdev, lrcis->reason);
+
+		break;
+
 	default:
 		goto unsupported;
 	}
@@ -3716,6 +3878,7 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
 	const struct bt_hci_cmd_le_set_scan_enable *lsse;
 	const struct bt_hci_cmd_le_set_ext_scan_enable *lsese;
 	const struct bt_hci_cmd_le_ext_create_conn *leecc;
+	const struct bt_hci_cmd_le_create_cis *leccis;
 
 	switch (opcode) {
 	case BT_HCI_CMD_INQUIRY:
@@ -3912,19 +4075,28 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
 			le_set_scan_enable_complete(btdev);
 		break;
 	case BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			return;
 		lsese = data;
 		if (btdev->le_scan_enable && lsese->enable)
 			le_set_ext_scan_enable_complete(btdev);
 		break;
 	case BT_HCI_CMD_LE_EXT_CREATE_CONN:
-		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
 			return;
 		leecc = data;
 		btdev->le_scan_own_addr_type = leecc->own_addr_type;
 		le_ext_conn_request(btdev, leecc);
 		break;
+	case BT_HCI_CMD_LE_CREATE_CIS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50 &&
+				btdev->type != BTDEV_TYPE_BREDRLE60)
+			return;
+		leccis = data;
+		le_cis_request(btdev, leccis);
+		break;
 	}
 }
 
@@ -4033,6 +4205,16 @@ static void send_acl(struct btdev *conn, const void *data, uint16_t len)
 	send_packet(conn, iov, 3);
 }
 
+static void send_iso(struct btdev *conn, const void *data, uint16_t len)
+{
+	struct iovec iov;
+
+	iov.iov_base = (void *) (data);
+	iov.iov_len = len;
+
+	send_packet(conn, &iov, 1);
+}
+
 void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len)
 {
 	uint8_t pkt_type;
@@ -4052,7 +4234,12 @@ void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len)
 	case BT_H4_ACL_PKT:
 		if (btdev->conn)
 			send_acl(btdev->conn, data, len);
-		num_completed_packets(btdev);
+		num_completed_packets(btdev, ACL_HANDLE);
+		break;
+	case BT_H4_ISO_PKT:
+		num_completed_packets(btdev, ISO_HANDLE);
+		if (btdev->conn)
+			send_iso(btdev->conn, data, len);
 		break;
 	default:
 		printf("Unsupported packet 0x%2.2x\n", pkt_type);
diff --git a/emulator/btdev.h b/emulator/btdev.h
index 362d1e7a2..b535930de 100644
--- a/emulator/btdev.h
+++ b/emulator/btdev.h
@@ -64,6 +64,7 @@ enum btdev_type {
 	BTDEV_TYPE_AMP,
 	BTDEV_TYPE_BREDR20,
 	BTDEV_TYPE_BREDRLE50,
+	BTDEV_TYPE_BREDRLE60,
 };
 
 enum btdev_hook_type {
diff --git a/emulator/hciemu.c b/emulator/hciemu.c
index 1045043f1..23891a2ee 100644
--- a/emulator/hciemu.c
+++ b/emulator/hciemu.c
@@ -335,6 +335,9 @@ struct hciemu *hciemu_new(enum hciemu_type type)
 	case HCIEMU_TYPE_BREDRLE50:
 		hciemu->btdev_type = BTDEV_TYPE_BREDRLE50;
 		break;
+	case HCIEMU_TYPE_BREDRLE60:
+		hciemu->btdev_type = BTDEV_TYPE_BREDRLE60;
+		break;
 	default:
 		return NULL;
 	}
diff --git a/emulator/hciemu.h b/emulator/hciemu.h
index e37c069e1..6f651cb98 100644
--- a/emulator/hciemu.h
+++ b/emulator/hciemu.h
@@ -32,6 +32,7 @@ enum hciemu_type {
 	HCIEMU_TYPE_LE,
 	HCIEMU_TYPE_LEGACY,
 	HCIEMU_TYPE_BREDRLE50,
+	HCIEMU_TYPE_BREDRLE60,
 };
 
 enum hciemu_hook_type {
diff --git a/emulator/vhci.c b/emulator/vhci.c
index 8dec20a08..7a69b484e 100644
--- a/emulator/vhci.c
+++ b/emulator/vhci.c
@@ -89,6 +89,7 @@ static void vhci_read_callback(int fd, uint32_t events, void *user_data)
 	case BT_H4_CMD_PKT:
 	case BT_H4_ACL_PKT:
 	case BT_H4_SCO_PKT:
+	case BT_H4_ISO_PKT:
 		btdev_receive_h4(vhci->btdev, buf, len);
 		break;
 	}
@@ -104,7 +105,7 @@ struct vhci *vhci_open(enum vhci_type type)
 
 	switch (type) {
 	case VHCI_TYPE_BREDRLE:
-		btdev_type = BTDEV_TYPE_BREDRLE;
+		btdev_type = BTDEV_TYPE_BREDRLE60;
 		ctrl_type = HCI_PRIMARY;
 		break;
 	case VHCI_TYPE_BREDR:
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 06/22] monitor: Add support for ISO packets
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 05/22] emulator: Add initial support for BT 5.2 Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 07/22] tools/btproxy: " Luiz Augusto von Dentz
                   ` (16 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This enables decoding of ISO packets.
---
 monitor/bt.h         | 12 +++++++--
 monitor/packet.c     | 63 +++++++++++++++++++++++++++++++++++++++++++-
 monitor/packet.h     |  2 ++
 src/shared/btsnoop.h |  2 ++
 4 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/monitor/bt.h b/monitor/bt.h
index 0a88aff97..b08f145ab 100644
--- a/monitor/bt.h
+++ b/monitor/bt.h
@@ -514,7 +514,7 @@ struct bt_hci_sco_hdr {
 
 struct bt_hci_iso_hdr {
 	uint16_t handle;
-	uint8_t  dlen;
+	uint16_t dlen;
 } __attribute__ ((packed));
 
 struct bt_hci_iso_data_start {
@@ -3434,9 +3434,17 @@ struct bt_hci_evt_le_cis_established {
 	uint16_t conn_handle;
 	uint8_t  cig_sync_delay[3];
 	uint8_t  cis_sync_delay[3];
-	uint8_t  latency[3];
+	uint8_t  m_latency[3];
+	uint8_t  s_latency[3];
 	uint8_t  m_phy;
 	uint8_t  s_phy;
+	uint8_t  nse;
+	uint8_t  m_bn;
+	uint8_t  s_bn;
+	uint8_t  m_ft;
+	uint8_t  s_ft;
+	uint16_t m_mtu;
+	uint16_t s_mtu;
 } __attribute__ ((packed));
 
 #define BT_HCI_EVT_LE_CIS_REQ			BT_HCI_SUBEVT_5_2 + 1
diff --git a/monitor/packet.c b/monitor/packet.c
index 8987ed703..0ef8a5b86 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -3934,6 +3934,12 @@ void packet_monitor(struct timeval *tv, struct ucred *cred,
 	case BTSNOOP_OPCODE_SCO_RX_PKT:
 		packet_hci_scodata(tv, cred, index, true, data, size);
 		break;
+	case BTSNOOP_OPCODE_ISO_TX_PKT:
+		packet_hci_isodata(tv, cred, index, false, data, size);
+		break;
+	case BTSNOOP_OPCODE_ISO_RX_PKT:
+		packet_hci_isodata(tv, cred, index, true, data, size);
+		break;
 	case BTSNOOP_OPCODE_OPEN_INDEX:
 		if (index < MAX_INDEX)
 			addr2str(index_list[index].bdaddr, str);
@@ -10275,9 +10281,17 @@ static void le_cis_established_evt(const void *data, uint8_t size)
 	print_field("Connection Handle: %d", le16_to_cpu(evt->status));
 	print_usec_interval("CIG Synchronization Delay", evt->cig_sync_delay);
 	print_usec_interval("CIS Synchronization Delay", evt->cis_sync_delay);
-	print_usec_interval("Latency", evt->latency);
+	print_usec_interval("Master to Slave Latency", evt->m_latency);
+	print_usec_interval("Slave to Master Latency", evt->s_latency);
 	print_le_phy("Master to Slave PHY", evt->m_phy);
 	print_le_phy("Slave to Master PHY", evt->m_phy);
+	print_field("Number of Subevents: %u", evt->nse);
+	print_field("Master to Slave Burst Number: %u", evt->m_bn);
+	print_field("Slave to Master Burst Number: %u", evt->s_bn);
+	print_field("Master to Slave Flush Timeout: %u", evt->m_ft);
+	print_field("Slave to Master Flush Timeout: %u", evt->s_ft);
+	print_field("Master to Slave MTU: %u", evt->m_ft);
+	print_field("Slave to Master MTU: %u", evt->s_ft);
 }
 
 static void le_req_cis_evt(const void *data, uint8_t size)
@@ -11134,6 +11148,53 @@ void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index,
 		packet_hexdump(data, size);
 }
 
+void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index,
+				bool in, const void *data, uint16_t size)
+{
+	const struct bt_hci_iso_hdr *hdr = data;
+	uint16_t handle = le16_to_cpu(hdr->handle);
+	uint8_t flags = acl_flags(handle);
+	char handle_str[16], extra_str[32];
+
+	if (index > MAX_INDEX) {
+		print_field("Invalid index (%d).", index);
+		return;
+	}
+
+	index_list[index].frame++;
+
+	if (size < sizeof(*hdr)) {
+		if (in)
+			print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+				"Malformed ISO Data RX packet", NULL, NULL);
+		else
+			print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+				"Malformed ISO Data TX packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	sprintf(handle_str, "Handle %d", acl_handle(handle));
+	sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, hdr->dlen);
+
+	print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_SCODATA,
+				in ? "ISO Data RX" : "ISO Data TX",
+						handle_str, extra_str);
+
+	if (size != hdr->dlen) {
+		print_text(COLOR_ERROR, "invalid packet size (%d != %d)",
+							size, hdr->dlen);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (filter_mask & PACKET_FILTER_SHOW_SCO_DATA)
+		packet_hexdump(data, size);
+}
+
 void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
 					const void *data, uint16_t size)
 {
diff --git a/monitor/packet.h b/monitor/packet.h
index 199e15e58..19ea04c68 100644
--- a/monitor/packet.h
+++ b/monitor/packet.h
@@ -94,6 +94,8 @@ void packet_hci_acldata(struct timeval *tv, struct ucred *cred, uint16_t index,
 				bool in, const void *data, uint16_t size);
 void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index,
 				bool in, const void *data, uint16_t size);
+void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index,
+				bool in, const void *data, uint16_t size);
 
 void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
 					const void *data, uint16_t size);
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index 3043d33e2..5fb084aa2 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -53,6 +53,8 @@
 #define BTSNOOP_OPCODE_CTRL_CLOSE	15
 #define BTSNOOP_OPCODE_CTRL_COMMAND	16
 #define BTSNOOP_OPCODE_CTRL_EVENT	17
+#define BTSNOOP_OPCODE_ISO_TX_PKT	18
+#define BTSNOOP_OPCODE_ISO_RX_PKT	19
 
 #define BTSNOOP_MAX_PACKET_SIZE		(1486 + 4)
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 07/22] tools/btproxy: Add support for ISO packets
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (5 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 06/22] monitor: Add support for ISO packets Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 08/22] monitor: Fix decoding of CIS estabilished event Luiz Augusto von Dentz
                   ` (15 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This enables proxing ISO packets.
---
 tools/btproxy.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/tools/btproxy.c b/tools/btproxy.c
index fb67a5735..a98bbb73f 100644
--- a/tools/btproxy.c
+++ b/tools/btproxy.c
@@ -73,14 +73,14 @@ static void hexdump_print(const char *str, void *user_data)
 }
 
 struct proxy {
-	/* Receive commands, ACL and SCO data */
+	/* Receive commands, ACL, SCO and ISO data */
 	int host_fd;
 	uint8_t host_buf[4096];
 	uint16_t host_len;
 	bool host_shutdown;
 	bool host_skip_first_zero;
 
-	/* Receive events, ACL and SCO data */
+	/* Receive events, ACL, SCO and ISO data */
 	int dev_fd;
 	uint8_t dev_buf[4096];
 	uint16_t dev_len;
@@ -296,6 +296,7 @@ static void host_read_callback(int fd, uint32_t events, void *user_data)
 	struct bt_hci_cmd_hdr *cmd_hdr;
 	struct bt_hci_acl_hdr *acl_hdr;
 	struct bt_hci_sco_hdr *sco_hdr;
+	struct bt_hci_iso_hdr *iso_hdr;
 	ssize_t len;
 	uint16_t pktlen;
 
@@ -364,6 +365,13 @@ process_packet:
 		sco_hdr = (void *) (proxy->host_buf + 1);
 		pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen;
 		break;
+	case BT_H4_ISO_PKT:
+		if (proxy->host_len < 1 + sizeof(*iso_hdr))
+			return;
+
+		iso_hdr = (void *) (proxy->host_buf + 1);
+		pktlen = 1 + sizeof(*iso_hdr) + cpu_to_le16(iso_hdr->dlen);
+		break;
 	case 0xff:
 		/* Notification packet from /dev/vhci - ignore */
 		proxy->host_len = 0;
@@ -418,6 +426,7 @@ static void dev_read_callback(int fd, uint32_t events, void *user_data)
 	struct bt_hci_evt_hdr *evt_hdr;
 	struct bt_hci_acl_hdr *acl_hdr;
 	struct bt_hci_sco_hdr *sco_hdr;
+	struct bt_hci_iso_hdr *iso_hdr;
 	ssize_t len;
 	uint16_t pktlen;
 
@@ -476,6 +485,13 @@ process_packet:
 		sco_hdr = (void *) (proxy->dev_buf + 1);
 		pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen;
 		break;
+	case BT_H4_ISO_PKT:
+		if (proxy->dev_len < 1 + sizeof(*iso_hdr))
+			return;
+
+		iso_hdr = (void *) (proxy->dev_buf + 1);
+		pktlen = 1 + sizeof(*iso_hdr) + cpu_to_le16(iso_hdr->dlen);
+		break;
 	default:
 		fprintf(stderr, "Received unknown device packet type 0x%02x\n",
 							proxy->dev_buf[0]);
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 08/22] monitor: Fix decoding of CIS estabilished event
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (6 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 07/22] tools/btproxy: " Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 09/22] emulator/btdev: Add parameter to CIS Estabilished Luiz Augusto von Dentz
                   ` (14 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

The event has been changed in r06.
---
 monitor/bt.h     | 1 +
 monitor/packet.c | 9 +++++----
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/monitor/bt.h b/monitor/bt.h
index b08f145ab..c90338096 100644
--- a/monitor/bt.h
+++ b/monitor/bt.h
@@ -3445,6 +3445,7 @@ struct bt_hci_evt_le_cis_established {
 	uint8_t  s_ft;
 	uint16_t m_mtu;
 	uint16_t s_mtu;
+	uint16_t interval;
 } __attribute__ ((packed));
 
 #define BT_HCI_EVT_LE_CIS_REQ			BT_HCI_SUBEVT_5_2 + 1
diff --git a/monitor/packet.c b/monitor/packet.c
index 0ef8a5b86..377944284 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -10278,20 +10278,21 @@ static void le_cis_established_evt(const void *data, uint8_t size)
 	const struct bt_hci_evt_le_cis_established *evt = data;
 
 	print_status(evt->status);
-	print_field("Connection Handle: %d", le16_to_cpu(evt->status));
+	print_field("Connection Handle: %d", le16_to_cpu(evt->conn_handle));
 	print_usec_interval("CIG Synchronization Delay", evt->cig_sync_delay);
 	print_usec_interval("CIS Synchronization Delay", evt->cis_sync_delay);
 	print_usec_interval("Master to Slave Latency", evt->m_latency);
 	print_usec_interval("Slave to Master Latency", evt->s_latency);
 	print_le_phy("Master to Slave PHY", evt->m_phy);
-	print_le_phy("Slave to Master PHY", evt->m_phy);
+	print_le_phy("Slave to Master PHY", evt->s_phy);
 	print_field("Number of Subevents: %u", evt->nse);
 	print_field("Master to Slave Burst Number: %u", evt->m_bn);
 	print_field("Slave to Master Burst Number: %u", evt->s_bn);
 	print_field("Master to Slave Flush Timeout: %u", evt->m_ft);
 	print_field("Slave to Master Flush Timeout: %u", evt->s_ft);
-	print_field("Master to Slave MTU: %u", evt->m_ft);
-	print_field("Slave to Master MTU: %u", evt->s_ft);
+	print_field("Master to Slave MTU: %u", le16_to_cpu(evt->m_mtu));
+	print_field("Slave to Master MTU: %u", le16_to_cpu(evt->s_mtu));
+	print_field("ISO Interval: %u", le16_to_cpu(evt->interval));
 }
 
 static void le_req_cis_evt(const void *data, uint8_t size)
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 09/22] emulator/btdev: Add parameter to CIS Estabilished
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (7 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 08/22] monitor: Fix decoding of CIS estabilished event Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 10/22] lib: Add definitions for Enhanced Credits Based Mode Luiz Augusto von Dentz
                   ` (13 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Use Set CIG params to fill CIS Estabilished.
---
 emulator/btdev.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/emulator/btdev.c b/emulator/btdev.c
index 02f408dd5..0de273e53 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -144,6 +144,10 @@ struct btdev {
 	uint8_t  le_filter_dup;
 	uint8_t  le_adv_enable;
 	uint8_t  le_ltk[16];
+	struct {
+		struct bt_hci_cmd_le_set_cig_params params;
+		struct bt_hci_cis_params cis;
+	} __attribute__ ((packed)) le_cig;
 
 	uint8_t le_local_sk256[32];
 
@@ -1412,8 +1416,25 @@ static void le_cis_estabilished(struct btdev *dev, uint8_t status)
 
 	if (!evt.status) {
 		evt.conn_handle = cpu_to_le16(ISO_HANDLE);
-		evt.m_phy = 0x01;
-		evt.m_phy = 0x01;
+		/* TODO: Figure out if these values makes sense */
+		memcpy(evt.cig_sync_delay, dev->le_cig.params.m_interval,
+				sizeof(dev->le_cig.params.m_interval));
+		memcpy(evt.cis_sync_delay, dev->le_cig.params.s_interval,
+				sizeof(dev->le_cig.params.s_interval));
+		memcpy(evt.m_latency, &dev->le_cig.params.m_latency,
+				sizeof(dev->le_cig.params.m_latency));
+		memcpy(evt.s_latency, &dev->le_cig.params.s_latency,
+				sizeof(dev->le_cig.params.s_latency));
+		evt.m_phy = dev->le_cig.cis.m_phy;
+		evt.s_phy = dev->le_cig.cis.s_phy;
+		evt.nse = 0x01;
+		evt.m_bn = 0x01;
+		evt.s_bn = 0x01;
+		evt.m_ft = 0x01;
+		evt.s_ft = 0x01;
+		evt.m_mtu = dev->iso_mtu;
+		evt.s_mtu = dev->iso_mtu;
+		evt.interval = dev->le_cig.params.m_latency;
 	}
 
 	le_meta_event(dev, BT_HCI_EVT_LE_CIS_ESTABLISHED, &evt, sizeof(evt));
@@ -3804,6 +3825,7 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 	case BT_HCI_CMD_LE_SET_CIG_PARAMS:
 		if (btdev->type != BTDEV_TYPE_BREDRLE60)
 			goto unsupported;
+		memcpy(&btdev->le_cig, data, len);
 		lscp.params.status = BT_HCI_ERR_SUCCESS;
 		lscp.params.cig_id = 0x00;
 		lscp.params.num_handles = 1;
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 10/22] lib: Add definitions for Enhanced Credits Based Mode
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (8 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 09/22] emulator/btdev: Add parameter to CIS Estabilished Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 11/22] btio: Add mode to for Enhanced Credit Mode Luiz Augusto von Dentz
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

---
 lib/l2cap.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/l2cap.h b/lib/l2cap.h
index 5ce94c4ee..bc9d7ed13 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_ECRED	0x81
 
 #define L2CAP_SERVTYPE_NOTRAFFIC	0x00
 #define L2CAP_SERVTYPE_BESTEFFORT	0x01
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 11/22] btio: Add mode to for Enhanced Credit Mode
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (9 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 10/22] lib: Add definitions for Enhanced Credits Based Mode Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 12/22] monitor: Add decoding for L2CAP Enhanced Credit Based PDUs Luiz Augusto von Dentz
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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 | 2 +-
 btio/btio.h | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index af2276db9..bd076b4c8 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -638,7 +638,7 @@ static gboolean l2cap_set(int sock, uint8_t src_type, int sec_level,
 	if (imtu || omtu || mode) {
 		gboolean ret;
 
-		if (src_type == BDADDR_BREDR)
+		if (src_type == BDADDR_BREDR || mode)
 			ret = set_l2opts(sock, imtu, omtu, mode, err);
 		else
 			ret = set_le_imtu(sock, imtu, err);
diff --git a/btio/btio.h b/btio/btio.h
index 2dce9f0c1..91398f72d 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -70,7 +70,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_ECRED = 0x81
 } BtIOMode;
 
 typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 12/22] monitor: Add decoding for L2CAP Enhanced Credit Based PDUs
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (10 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 11/22] btio: Add mode to for Enhanced Credit Mode Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 13/22] l2test: Add support for L2CAP_ECRED_MODE Luiz Augusto von Dentz
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Enhanced Credit Based Flow Control Mode is used for L2CAP
connection-oriented channels on LE and BR/EDR with flow control using
a credit-based scheme for L2CAP data.
---
 monitor/bt.h    |  30 ++++++++++
 monitor/l2cap.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 183 insertions(+)

diff --git a/monitor/bt.h b/monitor/bt.h
index c90338096..42e5b5d9f 100644
--- a/monitor/bt.h
+++ b/monitor/bt.h
@@ -3655,6 +3655,36 @@ struct bt_l2cap_pdu_le_flowctl_creds {
 	uint16_t credits;
 } __attribute__ ((packed));
 
+#define BT_L2CAP_PDU_ECRED_CONN_REQ	0x17
+struct bt_l2cap_pdu_ecred_conn_req {
+	uint16_t psm;
+	uint16_t mtu;
+	uint16_t mps;
+	uint16_t credits;
+	uint16_t scid[0];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_ECRED_CONN_RSP	0x18
+struct bt_l2cap_pdu_ecred_conn_rsp {
+	uint16_t mtu;
+	uint16_t mps;
+	uint16_t credits;
+	uint16_t result;
+	uint16_t dcid[0];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_ECRED_RECONF_REQ	0x19
+struct bt_l2cap_pdu_ecred_reconf_req {
+	uint16_t mtu;
+	uint16_t mps;
+	uint16_t scid[0];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_ECRED_RECONF_RSP	0x1a
+struct bt_l2cap_pdu_ecred_reconf_rsp {
+	uint16_t result;
+} __attribute__ ((packed));
+
 struct bt_l2cap_hdr_connless {
 	uint16_t psm;
 } __attribute__ ((packed));
diff --git a/monitor/l2cap.c b/monitor/l2cap.c
index d4feca451..3b2b25f24 100644
--- a/monitor/l2cap.c
+++ b/monitor/l2cap.c
@@ -54,6 +54,7 @@
 #define L2CAP_MODE_ERTM			0x03
 #define L2CAP_MODE_STREAMING		0x04
 #define L2CAP_MODE_LE_FLOWCTL		0x80
+#define L2CAP_MODE_ECRED		0x81
 
 /* L2CAP Control Field bit masks */
 #define L2CAP_CTRL_SAR_MASK		0xC000
@@ -121,6 +122,9 @@ static void assign_scid(const struct l2cap_frame *frame, uint16_t scid,
 	int i, n = -1;
 	uint8_t seq_num = 1;
 
+	if (!scid)
+		return;
+
 	for (i = 0; i < MAX_CHAN; i++) {
 		if (n < 0 && chan_list[i].handle == 0x0000) {
 			n = i;
@@ -417,6 +421,8 @@ static char *mode2str(uint8_t mode)
 		return "Streaming";
 	case L2CAP_MODE_LE_FLOWCTL:
 		return "LE Flow Control";
+	case L2CAP_MODE_ECRED:
+		return "Enhanced Credit";
 	default:
 		return "Unknown";
 	}
@@ -1333,6 +1339,132 @@ static void sig_le_flowctl_creds(const struct l2cap_frame *frame)
 	print_field("Credits: %u", le16_to_cpu(pdu->credits));
 }
 
+static void sig_ecred_conn_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_ecred_conn_req *pdu = frame->data;
+	uint16_t scid;
+
+	l2cap_frame_pull((void *)frame, frame, sizeof(pdu));
+
+	print_psm(pdu->psm);
+	print_field("MTU: %u", le16_to_cpu(pdu->mtu));
+	print_field("MPS: %u", le16_to_cpu(pdu->mps));
+	print_field("Credits: %u", le16_to_cpu(pdu->credits));
+
+	while (l2cap_frame_get_le16((void *)frame, &scid)) {
+		print_cid("Source", scid);
+		assign_scid(frame, scid, le16_to_cpu(pdu->psm),
+						L2CAP_MODE_ECRED, 0);
+	}
+}
+
+static void print_ecred_conn_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Connection successful";
+		break;
+	case 0x0002:
+		str = "Connection refused - PSM not supported";
+		break;
+	case 0x0004:
+		str = "Some connections refused – not enough resources "
+			"available";
+		break;
+	case 0x0005:
+		str = "All Connections refused - insufficient authentication";
+		break;
+	case 0x0006:
+		str = "All Connections refused - insufficient authorization";
+		break;
+	case 0x0007:
+		str = "All Connection refused - insufficient encryption key "
+			"size";
+		break;
+	case 0x0008:
+		str = "All Connections refused - insufficient encryption";
+		break;
+	case 0x0009:
+		str = "Some Connections refused - Invalid Source CID";
+		break;
+	case 0x000a:
+		str = "Some Connections refused - Source CID already allocated";
+		break;
+	case 0x000b:
+		str = "All Connections refused - unacceptable parameters";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static void sig_ecred_conn_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_ecred_conn_rsp *pdu = frame->data;
+	uint16_t dcid;
+
+	l2cap_frame_pull((void *)frame, frame, sizeof(pdu));
+
+	print_field("MTU: %u", le16_to_cpu(pdu->mtu));
+	print_field("MPS: %u", le16_to_cpu(pdu->mps));
+	print_field("Credits: %u", le16_to_cpu(pdu->credits));
+	print_ecred_conn_result(pdu->result);
+
+	while (l2cap_frame_get_le16((void *)frame, &dcid)) {
+		print_cid("Destination", dcid);
+		assign_dcid(frame, dcid, 0);
+	}
+}
+
+static void sig_ecred_reconf_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_ecred_reconf_req *pdu = frame->data;
+	uint16_t scid;
+
+	l2cap_frame_pull((void *)frame, frame, sizeof(pdu));
+
+	print_field("MTU: %u", le16_to_cpu(pdu->mtu));
+	print_field("MPS: %u", le16_to_cpu(pdu->mps));
+
+	while (l2cap_frame_get_le16((void *)frame, &scid))
+		print_cid("Source", scid);
+}
+
+static void print_ecred_reconf_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Reconfiguration successful";
+		break;
+	case 0x0001:
+		str = "Reconfiguration failed - reduction in size of MTU not "
+			"allowed";
+		break;
+	case 0x0002:
+		str = "Reconfiguration failed - reduction in size of MPS not "
+			"allowed for more than one channel at a time";
+		break;
+	default:
+		str = "Reserved";
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static void sig_ecred_reconf_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_ecred_reconf_rsp *pdu = frame->data;
+
+	print_ecred_reconf_result(pdu->result);
+}
+
 struct sig_opcode_data {
 	uint8_t opcode;
 	const char *str;
@@ -1341,6 +1473,24 @@ struct sig_opcode_data {
 	bool fixed;
 };
 
+#define SIG_ECRED \
+	{ BT_L2CAP_PDU_ECRED_CONN_REQ,					\
+	"Enhanced Credit Connection Request",				\
+	sig_ecred_conn_req, sizeof(struct bt_l2cap_pdu_ecred_conn_req), \
+	false },							\
+	{ BT_L2CAP_PDU_ECRED_CONN_RSP,					\
+	"Enhanced Credit Connection Response",				\
+	sig_ecred_conn_rsp, sizeof(struct bt_l2cap_pdu_ecred_conn_rsp), \
+	false },							\
+	{ BT_L2CAP_PDU_ECRED_RECONF_REQ,				\
+	"Enhanced Credit Reconfigure Request",				\
+	sig_ecred_reconf_req, sizeof(struct bt_l2cap_pdu_ecred_reconf_req), \
+	false },							\
+	{ BT_L2CAP_PDU_ECRED_RECONF_RSP,				\
+	"Enhanced Credit Reconfigure Respond",				\
+	sig_ecred_reconf_rsp, sizeof(struct bt_l2cap_pdu_ecred_reconf_rsp), \
+	true },
+
 static const struct sig_opcode_data bredr_sig_opcode_table[] = {
 	{ 0x01, "Command Reject",
 			sig_cmd_reject, 2, false },
@@ -1376,6 +1526,7 @@ static const struct sig_opcode_data bredr_sig_opcode_table[] = {
 			sig_move_chan_cfm, 4, true },
 	{ 0x11, "Move Channel Confirmation Response",
 			sig_move_chan_cfm_rsp, 2, true },
+	SIG_ECRED
 	{ },
 };
 
@@ -1396,6 +1547,7 @@ static const struct sig_opcode_data le_sig_opcode_table[] = {
 			sig_le_conn_rsp, 10, true },
 	{ 0x16, "LE Flow Control Credit",
 			sig_le_flowctl_creds, 4, true },
+	SIG_ECRED
 	{ },
 };
 
@@ -3066,6 +3218,7 @@ void l2cap_frame(uint16_t index, bool in, uint16_t handle, uint16_t cid,
 
 		switch (frame.mode) {
 		case L2CAP_MODE_LE_FLOWCTL:
+		case L2CAP_MODE_ECRED:
 			chan = get_chan(&frame);
 			if (!chan->sdu) {
 				if (!l2cap_frame_get_le16(&frame, &chan->sdu))
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 13/22] l2test: Add support for L2CAP_ECRED_MODE
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (11 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 12/22] monitor: Add decoding for L2CAP Enhanced Credit Based PDUs Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 14/22] share/att: Add EATT support Luiz Augusto von Dentz
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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_ECRED_MODE.
---
 tools/l2test.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/tools/l2test.c b/tools/l2test.c
index e787c2ce2..317515e68 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -149,6 +149,7 @@ static struct lookup_table l2cap_modes[] = {
 	*/
 	{ "ertm",	L2CAP_MODE_ERTM		},
 	{ "streaming",	L2CAP_MODE_STREAMING	},
+	{ "ecred",	L2CAP_MODE_ECRED	},
 	{ 0 }
 };
 
@@ -282,7 +283,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);
 	}
@@ -298,7 +299,7 @@ static int getopts(int sk, struct l2cap_options *opts, bool connected)
 
 static int setopts(int sk, struct l2cap_options *opts)
 {
-	if (bdaddr_type == BDADDR_BREDR)
+	if (bdaddr_type == BDADDR_BREDR || rfcmode)
 		return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts,
 								sizeof(*opts));
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 14/22] share/att: Add EATT support
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (12 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 13/22] l2test: Add support for L2CAP_ECRED_MODE Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 15/22] shared/gatt-client: Add support for EATT features Luiz Augusto von Dentz
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 15/22] shared/gatt-client: Add support for EATT features
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (13 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 14/22] share/att: Add EATT support Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 16/22] gatt: Enable EATT bearer support Luiz Augusto von Dentz
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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 | 164 +++++++++++++++++++++--
 src/shared/gatt-client.h |   5 +-
 src/shared/gatt-server.c | 205 ++++++++++++++++-------------
 tools/btgatt-client.c    |   2 +-
 unit/test-gatt.c         |   2 +-
 11 files changed, 477 insertions(+), 198 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 a4fe10980..dbbc59b15 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..d4758c2de 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,64 @@ 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;
+
+	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 +1527,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 +1904,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 +1948,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 +1982,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 +2047,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 +2143,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 +2159,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 +2217,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 +2248,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 +2260,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 +2287,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 +2405,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 +2421,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..d3f3dd696 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -683,7 +683,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,
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 16/22] gatt: Enable EATT bearer support
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (14 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 15/22] shared/gatt-client: Add support for EATT features Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 17/22] shared/gatt-server: Add support for Read Multiple Variable Length Luiz Augusto von Dentz
                   ` (6 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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 dbbc59b15..f7f0bc789 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..04a7e5319 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_ECRED,
+				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..47d32889d 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_ECRED,
+					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.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 17/22] shared/gatt-server: Add support for Read Multiple Variable Length
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (15 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 16/22] gatt: Enable EATT bearer support Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 18/22] shared/gatt-client: " Luiz Augusto von Dentz
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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 | 112 +++++++++++++++++++++++++++------------
 2 files changed, 80 insertions(+), 34 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..55535abdf 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,40 @@ 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->handles = NULL;
+	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,18 +1158,8 @@ 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);
@@ -1135,7 +1168,9 @@ static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode,
 		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 +1186,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 +1623,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.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 18/22] shared/gatt-client: Add support for Read Multiple Variable Length
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (16 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 17/22] shared/gatt-server: Add support for Read Multiple Variable Length Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 19/22] shared/gatt: Add support for Handle Value Multiple Notifications Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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 | 37 +++++++++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index d4758c2de..aecf0ca5a 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -2639,7 +2639,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)
@@ -2654,8 +2656,31 @@ 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) {
 		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,
@@ -2667,6 +2692,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)
@@ -2696,8 +2722,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_GATT_CHRC_CLI_FEAT_EATT;
+
+	req->att_id = bt_att_send(client->att, opcode, pdu, sizeof(pdu),
 							read_multiple_cb, req,
 							request_unref);
 	if (!req->att_id) {
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 19/22] shared/gatt: Add support for Handle Value Multiple Notifications
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (17 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 18/22] shared/gatt-client: " Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 20/22] gatt: Add support for Notify Multiple Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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 aecf0ca5a..8c563cdcc 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
@@ -2069,9 +2069,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,
@@ -2122,25 +2123,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,
@@ -2148,18 +2142,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);
@@ -2178,7 +2196,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);
 	}
@@ -2236,12 +2255,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 55535abdf..0711f1d75 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -1748,7 +1748,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);
 
@@ -1809,7 +1809,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.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 20/22] gatt: Add support for Notify Multiple
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (18 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 19/22] shared/gatt: Add support for Handle Value Multiple Notifications Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 21/22] core: Add support for setting the number of GATT bearers Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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 47d32889d..d4c892113 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 8c563cdcc..596ab30ca 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1965,6 +1965,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 0711f1d75..7a95fd4ce 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)
@@ -1729,28 +1741,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 d3f3dd696..a0b1d9844 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -2263,7 +2263,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.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 21/22] core: Add support for setting the number of GATT bearers
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (19 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 20/22] gatt: Add support for Notify Multiple Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-08  0:33 ` [PATCH BlueZ 22/22] monitor: Add support for decoding EATT Luiz Augusto von Dentz
  2020-01-16 11:54 ` [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Grzegorz Kołodziejczyk
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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 f7f0bc789..828fe9810 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 04a7e5319..203af709b 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 d4c892113..455201efd 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 adea85ce2..083f70c5b 100644
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -56,6 +56,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 1a6ab36a3..701cd279c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -108,6 +108,7 @@ static const char *gatt_options[] = {
 	"Cache",
 	"KeySize",
 	"ExchangeMTU",
+	"EATTChannels",
 	NULL
 };
 
@@ -444,6 +445,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)
@@ -470,6 +483,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 40687a755..35e4238f2 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -94,6 +94,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.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 22/22] monitor: Add support for decoding EATT
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (20 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 21/22] core: Add support for setting the number of GATT bearers Luiz Augusto von Dentz
@ 2020-01-08  0:33 ` Luiz Augusto von Dentz
  2020-01-16 11:54 ` [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Grzegorz Kołodziejczyk
  22 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-08  0:33 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.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support
  2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
                   ` (21 preceding siblings ...)
  2020-01-08  0:33 ` [PATCH BlueZ 22/22] monitor: Add support for decoding EATT Luiz Augusto von Dentz
@ 2020-01-16 11:54 ` Grzegorz Kołodziejczyk
  2020-01-16 19:51   ` Luiz Augusto von Dentz
  22 siblings, 1 reply; 25+ messages in thread
From: Grzegorz Kołodziejczyk @ 2020-01-16 11:54 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

I wasn't able to test it - it has to be rebased.

Regards,
Grzegorz


śr., 8 sty 2020 o 01:33 Luiz Augusto von Dentz <luiz.dentz@gmail.com>
napisał(a):
>
> 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
>
> * Monitor:
>         + HCI/LL Bluetooth 5.2 command/event decoding
>         + ISO packets decoding
>         + L2CAP Enhanced Credit Based Flow Control Mode decoding
>         + Enhanced ATT packets decoding
>
> * Emulator:
>         + CIS/Unicast emulation support
>         - BIS/Broadcast emulation not yet supported
>
> Kernel changes:
> https://lore.kernel.org/linux-bluetooth/20200107074056.25453-1-luiz.dentz@gmail.com/T/#t
>
> Luiz Augusto von Dentz (22):
>   monitor: Add support for decoding ISO related commands
>   monitor: Add decoding of ISO related Link Layer PDUs
>   lib: Add definitions for ISO socket
>   tools: Add isotest tool
>   emulator: Add initial support for BT 5.2
>   monitor: Add support for ISO packets
>   tools/btproxy: Add support for ISO packets
>   monitor: Fix decoding of CIS estabilished event
>   emulator/btdev: Add parameter to CIS Estabilished
>   lib: Add definitions for Enhanced Credits Based Mode
>   btio: Add mode to for Enhanced Credit Mode
>   monitor: Add decoding for L2CAP Enhanced Credit Based PDUs
>   l2test: Add support for L2CAP_ECRED_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
>
>  Makefile.tools           |    5 +-
>  attrib/gattrib.c         |    5 +-
>  btio/btio.c              |    2 +-
>  btio/btio.h              |    3 +-
>  emulator/btdev.c         |  293 +++++++++--
>  emulator/btdev.h         |    1 +
>  emulator/hciemu.c        |    3 +
>  emulator/hciemu.h        |    1 +
>  emulator/vhci.c          |    3 +-
>  lib/bluetooth.h          |   14 +
>  lib/iso.h                |   45 ++
>  lib/l2cap.h              |    1 +
>  lib/uuid.h               |    3 +
>  monitor/bt.h             |  385 ++++++++++++++
>  monitor/l2cap.c          |  192 +++++++
>  monitor/ll.c             |  100 ++++
>  monitor/packet.c         |  714 ++++++++++++++++++++++++++
>  monitor/packet.h         |    2 +
>  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/btsnoop.h     |    2 +
>  src/shared/gatt-client.c |  279 +++++++++--
>  src/shared/gatt-client.h |    5 +-
>  src/shared/gatt-server.c |  388 ++++++++++-----
>  src/shared/gatt-server.h |    2 +-
>  tools/btgatt-client.c    |    2 +-
>  tools/btgatt-server.c    |    4 +-
>  tools/btproxy.c          |   20 +-
>  tools/isotest.c          | 1019 ++++++++++++++++++++++++++++++++++++++
>  tools/l2test.c           |    5 +-
>  unit/test-gatt.c         |    4 +-
>  39 files changed, 4046 insertions(+), 529 deletions(-)
>  create mode 100644 lib/iso.h
>  create mode 100644 tools/isotest.c
>
> --
> 2.21.0
>

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support
  2020-01-16 11:54 ` [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Grzegorz Kołodziejczyk
@ 2020-01-16 19:51   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 25+ messages in thread
From: Luiz Augusto von Dentz @ 2020-01-16 19:51 UTC (permalink / raw)
  To: Grzegorz Kołodziejczyk; +Cc: linux-bluetooth

Hi Grzegorz,

On Thu, Jan 16, 2020 at 3:54 AM Grzegorz Kołodziejczyk
<grzegorz.kolodziejczyk@codecoup.pl> wrote:
>
> Hi Luiz,
>
> I wasn't able to test it - it has to be rebased.

There are some changes needed with the way the kernel end up being
merged so I will have to update this set anyway.

> Regards,
> Grzegorz
>
>
> śr., 8 sty 2020 o 01:33 Luiz Augusto von Dentz <luiz.dentz@gmail.com>
> napisał(a):
> >
> > 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
> >
> > * Monitor:
> >         + HCI/LL Bluetooth 5.2 command/event decoding
> >         + ISO packets decoding
> >         + L2CAP Enhanced Credit Based Flow Control Mode decoding
> >         + Enhanced ATT packets decoding
> >
> > * Emulator:
> >         + CIS/Unicast emulation support
> >         - BIS/Broadcast emulation not yet supported
> >
> > Kernel changes:
> > https://lore.kernel.org/linux-bluetooth/20200107074056.25453-1-luiz.dentz@gmail.com/T/#t
> >
> > Luiz Augusto von Dentz (22):
> >   monitor: Add support for decoding ISO related commands
> >   monitor: Add decoding of ISO related Link Layer PDUs
> >   lib: Add definitions for ISO socket
> >   tools: Add isotest tool
> >   emulator: Add initial support for BT 5.2
> >   monitor: Add support for ISO packets
> >   tools/btproxy: Add support for ISO packets
> >   monitor: Fix decoding of CIS estabilished event
> >   emulator/btdev: Add parameter to CIS Estabilished
> >   lib: Add definitions for Enhanced Credits Based Mode
> >   btio: Add mode to for Enhanced Credit Mode
> >   monitor: Add decoding for L2CAP Enhanced Credit Based PDUs
> >   l2test: Add support for L2CAP_ECRED_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
> >
> >  Makefile.tools           |    5 +-
> >  attrib/gattrib.c         |    5 +-
> >  btio/btio.c              |    2 +-
> >  btio/btio.h              |    3 +-
> >  emulator/btdev.c         |  293 +++++++++--
> >  emulator/btdev.h         |    1 +
> >  emulator/hciemu.c        |    3 +
> >  emulator/hciemu.h        |    1 +
> >  emulator/vhci.c          |    3 +-
> >  lib/bluetooth.h          |   14 +
> >  lib/iso.h                |   45 ++
> >  lib/l2cap.h              |    1 +
> >  lib/uuid.h               |    3 +
> >  monitor/bt.h             |  385 ++++++++++++++
> >  monitor/l2cap.c          |  192 +++++++
> >  monitor/ll.c             |  100 ++++
> >  monitor/packet.c         |  714 ++++++++++++++++++++++++++
> >  monitor/packet.h         |    2 +
> >  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/btsnoop.h     |    2 +
> >  src/shared/gatt-client.c |  279 +++++++++--
> >  src/shared/gatt-client.h |    5 +-
> >  src/shared/gatt-server.c |  388 ++++++++++-----
> >  src/shared/gatt-server.h |    2 +-
> >  tools/btgatt-client.c    |    2 +-
> >  tools/btgatt-server.c    |    4 +-
> >  tools/btproxy.c          |   20 +-
> >  tools/isotest.c          | 1019 ++++++++++++++++++++++++++++++++++++++
> >  tools/l2test.c           |    5 +-
> >  unit/test-gatt.c         |    4 +-
> >  39 files changed, 4046 insertions(+), 529 deletions(-)
> >  create mode 100644 lib/iso.h
> >  create mode 100644 tools/isotest.c
> >
> > --
> > 2.21.0
> >



-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2020-01-16 19:51 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-08  0:33 [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 01/22] monitor: Add support for decoding ISO related commands Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 02/22] monitor: Add decoding of ISO related Link Layer PDUs Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 03/22] lib: Add definitions for ISO socket Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 04/22] tools: Add isotest tool Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 05/22] emulator: Add initial support for BT 5.2 Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 06/22] monitor: Add support for ISO packets Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 07/22] tools/btproxy: " Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 08/22] monitor: Fix decoding of CIS estabilished event Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 09/22] emulator/btdev: Add parameter to CIS Estabilished Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 10/22] lib: Add definitions for Enhanced Credits Based Mode Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 11/22] btio: Add mode to for Enhanced Credit Mode Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 12/22] monitor: Add decoding for L2CAP Enhanced Credit Based PDUs Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 13/22] l2test: Add support for L2CAP_ECRED_MODE Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 14/22] share/att: Add EATT support Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 15/22] shared/gatt-client: Add support for EATT features Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 16/22] gatt: Enable EATT bearer support Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 17/22] shared/gatt-server: Add support for Read Multiple Variable Length Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 18/22] shared/gatt-client: " Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 19/22] shared/gatt: Add support for Handle Value Multiple Notifications Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 20/22] gatt: Add support for Notify Multiple Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 21/22] core: Add support for setting the number of GATT bearers Luiz Augusto von Dentz
2020-01-08  0:33 ` [PATCH BlueZ 22/22] monitor: Add support for decoding EATT Luiz Augusto von Dentz
2020-01-16 11:54 ` [PATCH BlueZ 00/22] Userspace Bluetooth 5.2 initial support Grzegorz Kołodziejczyk
2020-01-16 19:51   ` 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).