Merge BlueZ 5.41 to master

Merge BlueZ 5.41 from gfiber-internal/upstream to master.

Change-Id: I6efb64b3c2ce89b4c9bdb8088acdccd53acd3c02
diff --git a/ChangeLog b/ChangeLog
index dab8141..920175e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+ver 5.41:
+	Fix issue with service state changes handling.
+	Fix issue with AVRCP and no available player.
+	Fix issue with handling discovery filters.
+	Fix issue with handling temporary addresses.
+	Fix issue with GATT MTU size and BR/EDR links.
+	Fix issue with OBEX and creating directories.
+
 ver 5.40:
 	Fix issue with not storing GATT attributes.
 	Fix issue with optional GATT notifications.
diff --git a/Makefile.am b/Makefile.am
index 5510102..4ce642f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,7 +80,7 @@
 lib_LTLIBRARIES += lib/libbluetooth.la
 
 lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:12:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:13:18
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
diff --git a/client/main.c b/client/main.c
index c75b558..32341ad 100644
--- a/client/main.c
+++ b/client/main.c
@@ -50,7 +50,7 @@
 #define COLORED_DEL	COLOR_RED "DEL" COLOR_OFF
 
 #define PROMPT_ON	COLOR_BLUE "[bluetooth]" COLOR_OFF "# "
-#define PROMPT_OFF	"[bluetooth]# "
+#define PROMPT_OFF	"Waiting to connect to bluetoothd..."
 
 static GMainLoop *main_loop;
 static DBusConnection *dbus_conn;
@@ -82,16 +82,56 @@
 	printf("Leaking proxy %p\n", data);
 }
 
+static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
+							gpointer user_data)
+{
+	if (condition & G_IO_IN) {
+		rl_callback_read_char();
+		return TRUE;
+	}
+
+	if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_main_loop_quit(main_loop);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static guint setup_standard_input(void)
+{
+	GIOChannel *channel;
+	guint source;
+
+	channel = g_io_channel_unix_new(fileno(stdin));
+
+	source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				input_handler, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
 static void connect_handler(DBusConnection *connection, void *user_data)
 {
 	rl_set_prompt(PROMPT_ON);
 	printf("\r");
 	rl_on_new_line();
 	rl_redisplay();
+
+	if (!input)
+		input = setup_standard_input();
 }
 
 static void disconnect_handler(DBusConnection *connection, void *user_data)
 {
+	if (input > 0) {
+		g_source_remove(input);
+		input = 0;
+	}
+
 	rl_set_prompt(PROMPT_OFF);
 	printf("\r");
 	rl_on_new_line();
@@ -932,6 +972,36 @@
 	dbus_message_iter_close_container(iter, &value);
 }
 
+static void append_array_variant(DBusMessageIter *iter, int type, void *val,
+							int n_elements)
+{
+	DBusMessageIter variant, array;
+	char type_sig[2] = { type, '\0' };
+	char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+						array_sig, &variant);
+
+	dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+						type_sig, &array);
+
+	if (dbus_type_is_fixed(type) == TRUE) {
+		dbus_message_iter_append_fixed_array(&array, type, val,
+							n_elements);
+	} else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+		const char ***str_array = val;
+		int i;
+
+		for (i = 0; i < n_elements; i++)
+			dbus_message_iter_append_basic(&array, type,
+							&((*str_array)[i]));
+	}
+
+	dbus_message_iter_close_container(&variant, &array);
+
+	dbus_message_iter_close_container(iter, &variant);
+}
+
 static void dict_append_entry(DBusMessageIter *dict, const char *key,
 							int type, void *val)
 {
@@ -954,13 +1024,37 @@
 	dbus_message_iter_close_container(dict, &entry);
 }
 
+static void dict_append_basic_array(DBusMessageIter *dict, int key_type,
+					const void *key, int type, void *val,
+					int n_elements)
+{
+	DBusMessageIter entry;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+						NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, key_type, key);
+
+	append_array_variant(&entry, type, val, n_elements);
+
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+						void *val, int n_elements)
+{
+	dict_append_basic_array(dict, DBUS_TYPE_STRING, &key, type, val,
+								n_elements);
+}
+
 #define	DISTANCE_VAL_INVALID	0x7FFF
 
 struct set_discovery_filter_args {
 	char *transport;
 	dbus_uint16_t rssi;
 	dbus_int16_t pathloss;
-	GSList *uuids;
+	char **uuids;
+	size_t uuids_len;
 };
 
 static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
@@ -974,37 +1068,8 @@
 				DBUS_TYPE_VARIANT_AS_STRING
 				DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
 
-	if (args->uuids != NULL) {
-		DBusMessageIter entry, value, arrayIter;
-		char *uuids = "UUIDs";
-		GSList *l;
-
-		dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
-							NULL, &entry);
-		/* dict key */
-		dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
-							&uuids);
-
-		dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
-							"as", &value);
-
-		dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, "s",
-							&arrayIter);
-
-		for (l = args->uuids; l != NULL; l = g_slist_next(l))
-			/* list->data contains string representation of uuid */
-			dbus_message_iter_append_basic(&arrayIter,
-							DBUS_TYPE_STRING,
-							&l->data);
-
-		dbus_message_iter_close_container(&value, &arrayIter);
-
-		/* close vararg*/
-		dbus_message_iter_close_container(&entry, &value);
-
-		/* close entry */
-		dbus_message_iter_close_container(&dict, &entry);
-	}
+	dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &args->uuids,
+							args->uuids_len);
 
 	if (args->pathloss != DISTANCE_VAL_INVALID)
 		dict_append_entry(&dict, "Pathloss", DBUS_TYPE_UINT16,
@@ -1037,7 +1102,8 @@
 
 static gint filtered_scan_rssi = DISTANCE_VAL_INVALID;
 static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID;
-static GSList *filtered_scan_uuids;
+static char **filtered_scan_uuids;
+static size_t filtered_scan_uuids_len;
 static char *filtered_scan_transport;
 
 static void cmd_set_scan_filter_commit(void)
@@ -1049,6 +1115,7 @@
 	args.rssi = filtered_scan_rssi;
 	args.transport = filtered_scan_transport;
 	args.uuids = filtered_scan_uuids;
+	args.uuids_len = filtered_scan_uuids_len;
 
 	if (check_default_ctrl() == FALSE)
 		return;
@@ -1063,25 +1130,22 @@
 
 static void cmd_set_scan_filter_uuids(const char *arg)
 {
-	char *uuid_str, *saveptr, *uuids, *uuidstmp;
-
-	g_slist_free_full(filtered_scan_uuids, g_free);
+	g_strfreev(filtered_scan_uuids);
 	filtered_scan_uuids = NULL;
+	filtered_scan_uuids_len = 0;
 
 	if (!arg || !strlen(arg))
-		return;
+		goto commit;
 
-	uuids = g_strdup(arg);
-	for (uuidstmp = uuids; ; uuidstmp = NULL) {
-		uuid_str = strtok_r(uuidstmp, " \t", &saveptr);
-		if (uuid_str == NULL)
-			break;
-		filtered_scan_uuids = g_slist_append(filtered_scan_uuids,
-							strdup(uuid_str));
+	filtered_scan_uuids = g_strsplit(arg, " ", -1);
+	if (!filtered_scan_uuids) {
+		rl_printf("Failed to parse input\n");
+		return;
 	}
 
-	g_free(uuids);
+	filtered_scan_uuids_len = g_strv_length(filtered_scan_uuids);
 
+commit:
 	cmd_set_scan_filter_commit();
 }
 
@@ -1121,17 +1185,35 @@
 	cmd_set_scan_filter_commit();
 }
 
+static void clear_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+				DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+				DBUS_TYPE_STRING_AS_STRING
+				DBUS_TYPE_VARIANT_AS_STRING
+				DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
 static void cmd_set_scan_filter_clear(const char *arg)
 {
 	/* set default values for all options */
 	filtered_scan_rssi = DISTANCE_VAL_INVALID;
 	filtered_scan_pathloss = DISTANCE_VAL_INVALID;
-	g_slist_free_full(filtered_scan_uuids, g_free);
+	g_strfreev(filtered_scan_uuids);
 	filtered_scan_uuids = NULL;
+	filtered_scan_uuids_len = 0;
 	g_free(filtered_scan_transport);
 	filtered_scan_transport = NULL;
 
-	cmd_set_scan_filter_commit();
+	if (g_dbus_proxy_method_call(default_ctrl, "SetDiscoveryFilter",
+		clear_discovery_filter_setup, set_discovery_filter_reply,
+		NULL, NULL) == FALSE) {
+		rl_printf("Failed to clear discovery filter\n");
+	}
 }
 
 static struct GDBusProxy *find_device(const char *arg)
@@ -1898,38 +1980,6 @@
 	free(input);
 }
 
-static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
-							gpointer user_data)
-{
-	if (condition & G_IO_IN) {
-		rl_callback_read_char();
-		return TRUE;
-	}
-
-	if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
-		g_main_loop_quit(main_loop);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static guint setup_standard_input(void)
-{
-	GIOChannel *channel;
-	guint source;
-
-	channel = g_io_channel_unix_new(fileno(stdin));
-
-	source = g_io_add_watch(channel,
-				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-				input_handler, NULL);
-
-	g_io_channel_unref(channel);
-
-	return source;
-}
-
 static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
 							gpointer user_data)
 {
@@ -2040,9 +2090,8 @@
 
 static void client_ready(GDBusClient *client, void *user_data)
 {
-	guint *input = user_data;
-
-	*input = setup_standard_input();
+	if (!input)
+		input = setup_standard_input();
 }
 
 int main(int argc, char *argv[])
@@ -2093,8 +2142,7 @@
 	g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
 							property_changed, NULL);
 
-	input = 0;
-	g_dbus_client_set_ready_watch(client, client_ready, &input);
+	g_dbus_client_set_ready_watch(client, client_ready, NULL);
 
 	g_main_loop_run(main_loop);
 
diff --git a/configure.ac b/configure.ac
index 62c82ea..2f459cc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.40)
+AC_INIT(bluez, 5.41)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
 					tar-pax no-dist-gzip dist-xz])
diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
index 4dbf879..97462a3 100644
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -80,7 +80,11 @@
 
 			When discovery filter is set, Device objects will be
 			created as new devices with matching criteria are
-			discovered. PropertiesChanged signals will be emitted
+			discovered regardless of they are connectable or
+			discoverable which enables listening to
+			non-connectable and non-discoverable devices.
+
+			PropertiesChanged signals will be emitted
 			for already existing Device objects, with updated RSSI
 			value. If one or more discovery filters have been set,
 			the RSSI delta-threshold, that is imposed by
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index 8393bbf..2bc2a88 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -26,7 +26,8 @@
 Linux kernel v4.1	Version 1.9
 Linux kernel v4.2	Version 1.10
 Linux kernel v4.5	Version 1.11
-Linux kernel v4.6	Version 1.12 (not yet released)
+Linux kernel v4.6	Version 1.12
+Linux kernel v4.8	Version 1.13 (not yet released)
 
 Version 1.1 introduces Set Device ID command.
 
@@ -75,6 +76,10 @@
 Version 1.12 introduces a new limited privacy mode (value 0x02 passed to
 the Set Privacy command).
 
+Version 1.13 introduces a new authentication failure reason code for the
+Device Disconnected event.
+
+
 Example
 =======
 
@@ -550,6 +555,7 @@
 				Invalid Parameters
 				Invalid Index
 
+
 Set High Speed Command
 ======================
 
@@ -2454,6 +2460,8 @@
 		0x04	RS232
 		0x05	PCI
 		0x06	SDIO
+		0x07	SPI
+		0x08	I2C
 
 	Controllers marked as RAW only operation are currently not listed
 	by this command.
@@ -3061,6 +3069,7 @@
 		1	Connection timeout
 		2	Connection terminated by local host
 		3	Connection terminated by remote host
+		4	Connection terminated due to authentication failure
 
 	Note that the local/remote distinction just determines which side
 	terminated the low-level connection, regardless of the
diff --git a/doc/supported-features.txt b/doc/supported-features.txt
index 33685d5..f04cf4a 100644
--- a/doc/supported-features.txt
+++ b/doc/supported-features.txt
@@ -14,7 +14,7 @@
 GATT			4.2		Server, Client
 SDAP			1.1		Server, Client
 RFCOMM			1.1		Server, Client
-SPP			1.1		Server, Client
+SPP			1.2		Server, Client
 
 PXP			1.0		Reporter, Monitor
 HOGP			1.0		Host
diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt
index 5612ff0..54e5fe4 100644
--- a/doc/test-coverage.txt
+++ b/doc/test-coverage.txt
@@ -13,7 +13,7 @@
 test-sdp		 133	SDP qualification test cases
 test-uuid		  30	UUID conversion handling
 test-mgmt		   9	Management interface handling
-test-crypto		   4	Cryptographic toolbox helpers
+test-crypto		   5	Cryptographic toolbox helpers
 test-textfile		   4	Old textfile storage format
 test-ringbuf		   3	Ring buffer functionality
 test-queue		   6	Queue handling functionality
@@ -39,7 +39,7 @@
 
 Application		Count	Description
 -------------------------------------------
-mgmt-tester		 305	Kernel management interface testing
+mgmt-tester		 307	Kernel management interface testing
 l2cap-tester		  33	Kernel L2CAP implementation testing
 rfcomm-tester		   9	Kernel RFCOMM implementation testing
 bnep-tester		   1	Kernel BNEP implementation testing
@@ -49,7 +49,7 @@
 hci-tester		  14	Controller hardware testing
 userchan-tester		   3	Kernel HCI User Channel testting
 			-----
-			 382
+			 384
 
 
 Android end-to-end testing
diff --git a/emulator/btdev.c b/emulator/btdev.c
index 38769d8..1e94fc3 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -3098,6 +3098,7 @@
 		}
 		cmd_status(btdev, BT_HCI_ERR_SUCCESS,
 						BT_HCI_CMD_LE_GENERATE_DHKEY);
+		dh_evt.status = BT_HCI_ERR_SUCCESS;
 		le_meta_event(btdev, BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE,
 						&dh_evt, sizeof(dh_evt));
 		break;
diff --git a/emulator/bthost.c b/emulator/bthost.c
index 3638fe4..2bcdc31 100644
--- a/emulator/bthost.c
+++ b/emulator/bthost.c
@@ -2329,6 +2329,11 @@
 							&cmd, sizeof(cmd));
 }
 
+bool bthost_bredr_capable(struct bthost *bthost)
+{
+	return lmp_bredr_capable(bthost);
+}
+
 void bthost_request_auth(struct bthost *bthost, uint16_t handle)
 {
 	struct btconn *conn;
diff --git a/emulator/bthost.h b/emulator/bthost.h
index 7110db8..553865a 100644
--- a/emulator/bthost.h
+++ b/emulator/bthost.h
@@ -108,6 +108,8 @@
 void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject);
 bool bthost_get_reject_user_confirm(struct bthost *bthost);
 
+bool bthost_bredr_capable(struct bthost *bthost);
+
 uint64_t bthost_conn_get_fixed_chan(struct bthost *bthost, uint16_t handle);
 
 typedef void (*bthost_rfcomm_connect_cb) (uint16_t handle, uint16_t cid,
diff --git a/emulator/hciemu.c b/emulator/hciemu.c
index 6a53499..ea1e3cf 100644
--- a/emulator/hciemu.c
+++ b/emulator/hciemu.c
@@ -243,7 +243,8 @@
 	}
 
 	create_req[0] = HCI_VENDOR_PKT;
-	create_req[1] = HCI_BREDR;
+	create_req[1] = HCI_PRIMARY;
+
 	written = write(fd, create_req, sizeof(create_req));
 	if (written < 0) {
 		close(fd);
diff --git a/emulator/le.c b/emulator/le.c
index 82ae573..d7ee297 100644
--- a/emulator/le.c
+++ b/emulator/le.c
@@ -1881,7 +1881,7 @@
 	}
 
 	setup_cmd[0] = HCI_VENDOR_PKT;
-	setup_cmd[1] = HCI_BREDR;
+	setup_cmd[1] = HCI_PRIMARY;
 
 	if (write(hci->vhci_fd, setup_cmd, sizeof(setup_cmd)) < 0) {
 		close(hci->vhci_fd);
diff --git a/emulator/smp.c b/emulator/smp.c
index e941141..40836cf 100644
--- a/emulator/smp.c
+++ b/emulator/smp.c
@@ -68,8 +68,6 @@
 #define DIST_SIGN	0x04
 #define DIST_LINK_KEY	0x08
 
-#define KEY_DIST	(DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN)
-
 #define SC_NO_DIST	(DIST_ENC_KEY | DIST_LINK_KEY)
 
 #define MAX_IO_CAP	0x04
@@ -193,6 +191,14 @@
 	return method;
 }
 
+static uint8_t key_dist(struct bthost *host)
+{
+	if (!bthost_bredr_capable(host))
+		return (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN);
+
+	return (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN | DIST_LINK_KEY);
+}
+
 static void smp_send(struct smp_conn *conn, uint8_t smp_cmd, const void *data,
 								uint8_t len)
 {
@@ -433,8 +439,8 @@
 	}
 
 	rsp.max_key_size	= 0x10;
-	rsp.init_key_dist	= conn->preq[5] & KEY_DIST;
-	rsp.resp_key_dist	= conn->preq[6] & KEY_DIST;
+	rsp.init_key_dist	= conn->preq[5] & key_dist(bthost);
+	rsp.resp_key_dist	= conn->preq[6] & key_dist(bthost);
 
 	conn->prsp[0] = BT_L2CAP_SMP_PAIRING_RESPONSE;
 	memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
@@ -691,8 +697,8 @@
 	req.oob_data		= 0x00;
 	req.auth_req		= auth_req;
 	req.max_key_size	= 0x10;
-	req.init_key_dist	= KEY_DIST;
-	req.resp_key_dist	= KEY_DIST;
+	req.init_key_dist	= key_dist(conn->smp->bthost);
+	req.resp_key_dist	= key_dist(conn->smp->bthost);
 
 	conn->preq[0] = BT_L2CAP_SMP_PAIRING_REQUEST;
 	memcpy(&conn->preq[1], &req, sizeof(req));
@@ -818,8 +824,8 @@
 
 	memset(&req, 0, sizeof(req));
 	req.max_key_size = 0x10;
-	req.init_key_dist = KEY_DIST;
-	req.resp_key_dist = KEY_DIST;
+	req.init_key_dist = key_dist(smp->bthost);
+	req.resp_key_dist = key_dist(smp->bthost);
 
 	smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req));
 }
diff --git a/emulator/vhci.c b/emulator/vhci.c
index 6bba4e2..8dec20a 100644
--- a/emulator/vhci.c
+++ b/emulator/vhci.c
@@ -105,15 +105,15 @@
 	switch (type) {
 	case VHCI_TYPE_BREDRLE:
 		btdev_type = BTDEV_TYPE_BREDRLE;
-		ctrl_type = HCI_BREDR;
+		ctrl_type = HCI_PRIMARY;
 		break;
 	case VHCI_TYPE_BREDR:
 		btdev_type = BTDEV_TYPE_BREDR;
-		ctrl_type = HCI_BREDR;
+		ctrl_type = HCI_PRIMARY;
 		break;
 	case VHCI_TYPE_LE:
 		btdev_type = BTDEV_TYPE_LE;
-		ctrl_type = HCI_BREDR;
+		ctrl_type = HCI_PRIMARY;
 		break;
 	case VHCI_TYPE_AMP:
 		btdev_type = BTDEV_TYPE_AMP;
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index fd0b081..3726b50 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -360,7 +360,7 @@
 	case 48:
 		return "ST Microelectronics";
 	case 49:
-		return "Synopsis";
+		return "Synopsys, Inc.";
 	case 50:
 		return "Red-M (Communications) Ltd";
 	case 51:
@@ -836,7 +836,7 @@
 	case 286:
 		return "Skoda Auto a.s.";
 	case 287:
-		return "Volkswagon AG";
+		return "Volkswagen AG";
 	case 288:
 		return "Porsche AG";
 	case 289:
@@ -2059,6 +2059,90 @@
 		return "Sharp Corporation";
 	case 898:
 		return "Precision Outcomes Ltd";
+	case 899:
+		return "Kronos Incorporated";
+	case 900:
+		return "OCOSMOS Co., Ltd.";
+	case 901:
+		return "Embedded Electronic Solutions Ltd. dba e2Solutions";
+	case 902:
+		return "Aterica Inc.";
+	case 903:
+		return "BluStor PMC, Inc.";
+	case 904:
+		return "Kapsch TrafficCom AB";
+	case 905:
+		return "ActiveBlu Corporation";
+	case 906:
+		return "Kohler Mira Limited";
+	case 907:
+		return "Noke";
+	case 908:
+		return "Appion Inc.";
+	case 909:
+		return "Resmed Ltd";
+	case 910:
+		return "Crownstone B.V.";
+	case 911:
+		return "Xiaomi Inc.";
+	case 912:
+		return "INFOTECH s.r.o.";
+	case 913:
+		return "Thingsquare AB";
+	case 914:
+		return "T&D";
+	case 915:
+		return "LAVAZZA S.p.A.";
+	case 916:
+		return "Netclearance Systems, Inc.";
+	case 917:
+		return "SDATAWAY";
+	case 918:
+		return "BLOKS GmbH";
+	case 919:
+		return "LEGO System A/S";
+	case 920:
+		return "Thetatronics Ltd";
+	case 921:
+		return "Nikon Corporation";
+	case 922:
+		return "NeST";
+	case 923:
+		return "South Silicon Valley Microelectronics";
+	case 924:
+		return "ALE International";
+	case 925:
+		return "CareView Communications, Inc.";
+	case 926:
+		return "SchoolBoard Limited";
+	case 927:
+		return "Molex Corporation";
+	case 928:
+		return "IVT Wireless Limited";
+	case 929:
+		return "Alpine Labs LLC";
+	case 930:
+		return "Candura Instruments";
+	case 931:
+		return "SmartMovt Technology Co., Ltd";
+	case 932:
+		return "Token Zero Ltd";
+	case 933:
+		return "ACE CAD Enterprise Co., Ltd. (ACECAD)";
+	case 934:
+		return "Medela, Inc";
+	case 935:
+		return "AeroScout";
+	case 936:
+		return "Esrille Inc.";
+	case 937:
+		return "THINKERLY SRL";
+	case 938:
+		return "Exon Sp. z o.o.";
+	case 939:
+		return "Meizu Technology Co., Ltd.";
+	case 940:
+		return "Smablo LTD";
 	case 65535:
 		return "internal use";
 	default:
diff --git a/lib/hci.c b/lib/hci.c
index fda1aca..0c85875 100644
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -143,7 +143,7 @@
 {
 	switch (bus) {
 	case HCI_VIRTUAL:
-		return "VIRTUAL";
+		return "Virtual";
 	case HCI_USB:
 		return "USB";
 	case HCI_PCCARD:
@@ -161,7 +161,7 @@
 	case HCI_I2C:
 		return "I2C";
 	default:
-		return "UNKNOWN";
+		return "Unknown";
 	}
 }
 
@@ -173,12 +173,12 @@
 char *hci_typetostr(int type)
 {
 	switch (type) {
-	case HCI_BREDR:
-		return "BR/EDR";
+	case HCI_PRIMARY:
+		return "Primary";
 	case HCI_AMP:
 		return "AMP";
 	default:
-		return "UNKNOWN";
+		return "Unknown";
 	}
 }
 
diff --git a/lib/hci.h b/lib/hci.h
index f76e205..bdd0df0 100644
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -59,8 +59,9 @@
 #define HCI_I2C		8
 
 /* HCI controller types */
-#define HCI_BREDR	0x00
+#define HCI_PRIMARY	0x00
 #define HCI_AMP		0x01
+#define HCI_BREDR	HCI_PRIMARY
 
 /* HCI device flags */
 enum {
diff --git a/monitor/analyze.c b/monitor/analyze.c
index 0f2d19a..4dc2891 100644
--- a/monitor/analyze.c
+++ b/monitor/analyze.c
@@ -29,6 +29,8 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "lib/bluetooth.h"
+
 #include "src/shared/util.h"
 #include "src/shared/queue.h"
 #include "src/shared/btsnoop.h"
@@ -45,6 +47,11 @@
 	unsigned long num_evt;
 	unsigned long num_acl;
 	unsigned long num_sco;
+	unsigned long vendor_diag;
+	unsigned long system_note;
+	unsigned long user_log;
+	unsigned long unknown;
+	uint16_t manufacturer;
 };
 
 static struct queue *dev_list;
@@ -67,13 +74,22 @@
 	}
 
 	printf("Found %s controller with index %u\n", str, dev->index);
-	printf("  BD_ADDR %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+	printf("  BD_ADDR %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
 			dev->bdaddr[5], dev->bdaddr[4], dev->bdaddr[3],
 			dev->bdaddr[2], dev->bdaddr[1], dev->bdaddr[0]);
+	if (dev->manufacturer != 0xffff)
+		printf(" (%s)", bt_compidtostr(dev->manufacturer));
+	printf("\n");
+
+
 	printf("  %lu commands\n", dev->num_cmd);
 	printf("  %lu events\n", dev->num_evt);
 	printf("  %lu ACL packets\n", dev->num_acl);
 	printf("  %lu SCO packets\n", dev->num_sco);
+	printf("  %lu vendor diagnostics\n", dev->vendor_diag);
+	printf("  %lu system notes\n", dev->system_note);
+	printf("  %lu user logs\n", dev->user_log);
+	printf("  %lu unknown opcodes\n", dev->unknown);
 	printf("\n");
 
 	free(dev);
@@ -84,12 +100,9 @@
 	struct hci_dev *dev;
 
 	dev = new0(struct hci_dev, 1);
-	if (!dev) {
-		fprintf(stderr, "Failed to allocate new device entry\n");
-		return NULL;
-	}
 
 	dev->index = index;
+	dev->manufacturer = 0xffff;
 
 	return dev;
 }
@@ -111,8 +124,6 @@
 		fprintf(stderr, "Creating new device for unknown index\n");
 
 		dev = dev_alloc(index);
-		if (!dev)
-			return NULL;
 
 		queue_push_tail(dev_list, dev);
 	}
@@ -127,8 +138,6 @@
 	struct hci_dev *dev;
 
 	dev = dev_alloc(index);
-	if (!dev)
-		return;
 
 	dev->type = ni->type;
 	memcpy(dev->bdaddr, ni->bdaddr, 6);
@@ -251,6 +260,70 @@
 	dev->num_sco++;
 }
 
+static void info_index(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	const struct btsnoop_opcode_index_info *hdr = data;
+	struct hci_dev *dev;
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->manufacturer = hdr->manufacturer;
+}
+
+static void vendor_diag(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	struct hci_dev *dev;
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->vendor_diag++;
+}
+
+static void system_note(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	struct hci_dev *dev;
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->system_note++;
+}
+
+static void user_log(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	struct hci_dev *dev;
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->user_log++;
+}
+
+static void unknown_opcode(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	struct hci_dev *dev;
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->unknown++;
+}
+
 void analyze_trace(const char *path)
 {
 	struct btsnoop *btsnoop_file;
@@ -274,10 +347,6 @@
 	}
 
 	dev_list = queue_new();
-	if (!dev_list) {
-		fprintf(stderr, "Failed to allocate device list\n");
-		goto done;
-	}
 
 	while (1) {
 		unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
@@ -312,9 +381,22 @@
 		case BTSNOOP_OPCODE_OPEN_INDEX:
 		case BTSNOOP_OPCODE_CLOSE_INDEX:
 			break;
+		case BTSNOOP_OPCODE_INDEX_INFO:
+			info_index(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_VENDOR_DIAG:
+			vendor_diag(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_SYSTEM_NOTE:
+			system_note(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_USER_LOGGING:
+			user_log(&tv, index, buf, pktlen);
+			break;
 		default:
-			fprintf(stderr, "Wrong opcode %u\n", opcode);
-			goto done;
+			fprintf(stderr, "Unknown opcode %u\n", opcode);
+			unknown_opcode(&tv, index, buf, pktlen);
+			break;
 		}
 
 		num_packets++;
diff --git a/monitor/l2cap.c b/monitor/l2cap.c
index c1f2bc5..582c853 100644
--- a/monitor/l2cap.c
+++ b/monitor/l2cap.c
@@ -1999,6 +1999,7 @@
 static void print_uuid(const char *label, const void *data, uint16_t size)
 {
 	const char *str;
+	char uuidstr[36];
 
 	switch (size) {
 	case 2:
@@ -2010,12 +2011,12 @@
 		print_field("%s: %s (0x%8.8x)", label, str, get_le32(data));
 		break;
 	case 16:
-		str = uuid128_to_str(data);
-		print_field("%s: %s (%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x)",
-				label, str,
+		sprintf(uuidstr, "%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
 				get_le32(data + 12), get_le16(data + 10),
 				get_le16(data + 8), get_le16(data + 6),
 				get_le32(data + 2), get_le16(data + 0));
+		str = uuidstr_to_str(uuidstr);
+		print_field("%s: %s (%s)", label, str, uuidstr);
 		break;
 	default:
 		packet_hexdump(data, size);
diff --git a/monitor/packet.c b/monitor/packet.c
index 32fbcf7..0947213 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -5542,7 +5542,7 @@
 
 	if (index_current < MAX_INDEX) {
 		switch (index_list[index_current].type) {
-		case HCI_BREDR:
+		case HCI_PRIMARY:
 			print_lmp_version(rsp->lmp_ver, rsp->lmp_subver);
 			break;
 		case HCI_AMP:
diff --git a/monitor/uuid.c b/monitor/uuid.c
index 1ceaa6f..6660bc7 100644
--- a/monitor/uuid.c
+++ b/monitor/uuid.c
@@ -585,11 +585,6 @@
 	return "Unknown";
 }
 
-const char *uuid128_to_str(const unsigned char *uuid)
-{
-	return "Unknown";
-}
-
 const char *uuidstr_to_str(const char *uuid)
 {
 	uint32_t val;
diff --git a/monitor/uuid.h b/monitor/uuid.h
index f467f51..6ffc0ee 100644
--- a/monitor/uuid.h
+++ b/monitor/uuid.h
@@ -26,6 +26,4 @@
 
 const char *uuid16_to_str(uint16_t uuid);
 const char *uuid32_to_str(uint32_t uuid);
-const char *uuid128_to_str(const unsigned char *uuid);
-
 const char *uuidstr_to_str(const char *uuid);
diff --git a/obexd/client/opp.c b/obexd/client/opp.c
index 3c2801a..92785f6 100644
--- a/obexd/client/opp.c
+++ b/obexd/client/opp.c
@@ -117,7 +117,8 @@
 static DBusMessage *opp_exchange_business_cards(DBusConnection *connection,
 					DBusMessage *message, void *user_data)
 {
-	return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", NULL);
+	return g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
+							"Not Implemented");
 }
 
 static const GDBusMethodTable opp_methods[] = {
diff --git a/obexd/plugins/ftp.c b/obexd/plugins/ftp.c
index a906527..3ee18a6 100644
--- a/obexd/plugins/ftp.c
+++ b/obexd/plugins/ftp.c
@@ -278,6 +278,8 @@
 	DBG("Fullname: %s", fullname);
 
 	err = verify_path(fullname);
+	if (err == -ENOENT)
+		goto not_found;
 
 	if (err < 0)
 		goto done;
diff --git a/obexd/plugins/phonebook-dummy.c b/obexd/plugins/phonebook-dummy.c
index eeb078f..29ae889 100644
--- a/obexd/plugins/phonebook-dummy.c
+++ b/obexd/plugins/phonebook-dummy.c
@@ -520,7 +520,6 @@
 	struct dummy_data *dummy;
 	char *filename;
 	int fd;
-	guint ret;
 
 	filename = g_build_filename(root_folder, folder, id, NULL);
 
@@ -538,13 +537,13 @@
 	dummy->apparams = params;
 	dummy->fd = fd;
 
-	ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_entry, dummy,
+	dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_entry, dummy,
 								dummy_free);
 
 	if (err)
 		*err = 0;
 
-	return GINT_TO_POINTER(ret);
+	return dummy;
 }
 
 void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
@@ -553,7 +552,7 @@
 	struct cache_query *query;
 	char *foldername;
 	DIR *dp;
-	guint ret;
+	struct dummy_data *dummy;
 
 	foldername = g_build_filename(root_folder, name, NULL);
 	dp = opendir(foldername);
@@ -572,11 +571,13 @@
 	query->user_data = user_data;
 	query->dp = dp;
 
-	ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, create_cache, query,
-								query_free);
+	dummy = g_new0(struct dummy_data, 1);
+
+	dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, create_cache,
+							query, query_free);
 
 	if (err)
 		*err = 0;
 
-	return GINT_TO_POINTER(ret);
+	return dummy;
 }
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 82de98b..4ec9cca 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1397,6 +1397,7 @@
 		avdtp_send(session, session->in.transaction,
 				AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
 				&rej, sizeof(rej));
+		stream_free(stream);
 		return;
 	}
 
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 6c8ed81..c100149 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -3200,7 +3200,8 @@
 
 	session->controller->player = player;
 	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
-	control_set_player(service, media_player_get_path(player->user_data));
+	control_set_player(service, player ?
+			media_player_get_path(player->user_data) : NULL);
 }
 
 static struct avrcp_player *create_ct_player(struct avrcp *session,
@@ -3330,6 +3331,10 @@
 	struct avrcp_player *player = data;
 	GSList *l;
 
+	/* Don't remove reserved player */
+	if (!player->id)
+		return;
+
 	for (l = player->sessions; l; l = l->next) {
 		struct avrcp *session = l->data;
 		struct avrcp_data *controller = session->controller;
@@ -3393,6 +3398,10 @@
 
 	g_slist_free_full(removed, player_remove);
 
+	/* There should always be an active player */
+	if (!session->controller->player)
+		create_ct_player(session, 0);
+
 	return FALSE;
 }
 
diff --git a/src/adapter.c b/src/adapter.c
index a81a144..3b21afa 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2187,9 +2187,7 @@
 		*transport = SCAN_TYPE_BREDR;
 	else if (!strcmp(transport_str, "le"))
 		*transport = SCAN_TYPE_LE;
-	else if (!strcmp(transport_str, "auto"))
-		*transport = SCAN_TYPE_DUAL;
-	else
+	else if (strcmp(transport_str, "auto"))
 		return false;
 
 	return true;
@@ -2220,8 +2218,9 @@
  * successful, sets *filter to proper value.
  * Returns false on any error, and true on success.
  */
-static bool parse_discovery_filter_dict(struct discovery_filter **filter,
-							DBusMessage *msg)
+static bool parse_discovery_filter_dict(struct btd_adapter *adapter,
+					struct discovery_filter **filter,
+					DBusMessage *msg)
 {
 	DBusMessageIter iter, subiter, dictiter, variantiter;
 	bool is_empty = true;
@@ -2233,7 +2232,7 @@
 	(*filter)->uuids = NULL;
 	(*filter)->pathloss = DISTANCE_VAL_INVALID;
 	(*filter)->rssi = DISTANCE_VAL_INVALID;
-	(*filter)->type = SCAN_TYPE_DUAL;
+	(*filter)->type = get_scan_type(adapter);
 
 	dbus_message_iter_init(msg, &iter);
 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
@@ -2308,7 +2307,7 @@
 		return btd_error_not_supported(msg);
 
 	/* parse parameters */
-	if (!parse_discovery_filter_dict(&discovery_filter, msg))
+	if (!parse_discovery_filter_dict(adapter, &discovery_filter, msg))
 		return btd_error_invalid_args(msg);
 
 	is_discovering = get_discovery_client(adapter, sender, &client);
@@ -5451,7 +5450,7 @@
 	memset(&eir_data, 0, sizeof(eir_data));
 	eir_parse(&eir_data, data, data_len);
 
-	if (bdaddr_type == BDADDR_BREDR)
+	if (bdaddr_type == BDADDR_BREDR || adapter->filtered_discovery)
 		discoverable = true;
 	else
 		discoverable = eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC);
diff --git a/src/device.c b/src/device.c
index b1ff1bb..5198c6d 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2700,6 +2700,9 @@
 	char *str = NULL;
 	int len;
 
+	if (device_address_is_private(device))
+		return NULL;
+
 	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
 
 	key_file = g_key_file_new();
@@ -5186,6 +5189,9 @@
 	if (device->temporary == temporary)
 		return;
 
+	if (device_address_is_private(device))
+		return;
+
 	DBG("temporary %d", temporary);
 
 	device->temporary = temporary;
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 0cbacca..7abb306 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -74,6 +74,17 @@
 	struct queue *chrcs;
 };
 
+typedef bool (*async_dbus_op_complete_t)(void *data);
+
+struct async_dbus_op {
+	int ref_count;
+	unsigned int id;
+	struct queue *msgs;
+	void *data;
+	uint16_t offset;
+	async_dbus_op_complete_t complete;
+};
+
 struct characteristic {
 	struct service *service;
 	struct gatt_db_attribute *attr;
@@ -85,8 +96,8 @@
 	bt_uuid_t uuid;
 	char *path;
 
-	unsigned int read_id;
-	unsigned int write_id;
+	struct async_dbus_op *read_op;
+	struct async_dbus_op *write_op;
 
 	struct queue *descs;
 
@@ -101,8 +112,8 @@
 	bt_uuid_t uuid;
 	char *path;
 
-	unsigned int read_id;
-	unsigned int write_id;
+	struct async_dbus_op *read_op;
+	struct async_dbus_op *write_op;
 };
 
 static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16)
@@ -205,22 +216,11 @@
 	return 0;
 }
 
-typedef bool (*async_dbus_op_complete_t)(void *data);
-
-struct async_dbus_op {
-	int ref_count;
-	DBusMessage *msg;
-	void *data;
-	uint16_t offset;
-	async_dbus_op_complete_t complete;
-};
-
 static void async_dbus_op_free(void *data)
 {
 	struct async_dbus_op *op = data;
 
-	if (op->msg)
-		dbus_message_unref(op->msg);
+	queue_destroy(op->msgs, (void *)dbus_message_unref);
 
 	free(op);
 }
@@ -296,27 +296,44 @@
 					GATT_DESCRIPTOR_IFACE, "Value");
 }
 
+static void async_dbus_op_reply(struct async_dbus_op *op, int err,
+				const uint8_t *value, size_t length)
+{
+	const struct queue_entry *entry;
+	DBusMessage *reply;
+
+	op->id = 0;
+
+	for (entry = queue_get_entries(op->msgs); entry; entry = entry->next) {
+		DBusMessage *msg = entry->data;
+
+		if (err) {
+			reply = err > 0 ? create_gatt_dbus_error(msg, err) :
+				btd_error_failed(msg, strerror(err));
+			goto send_reply;
+		}
+
+		reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+		if (!reply) {
+			error("Failed to allocate D-Bus message reply");
+			return;
+		}
+
+		if (value)
+			message_append_byte_array(reply, value, length);
+
+send_reply:
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+	}
+}
+
 static void read_op_cb(struct gatt_db_attribute *attrib, int err,
 				const uint8_t *value, size_t length,
 				void *user_data)
 {
 	struct async_dbus_op *op = user_data;
-	DBusMessage *reply;
 
-	if (err) {
-		error("Failed to read attribute");
-		return;
-	}
-
-	reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
-	if (!reply) {
-		error("Failed to allocate D-Bus message reply");
-		return;
-	}
-
-	message_append_byte_array(reply, value, length);
-
-	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	async_dbus_op_reply(op, err, value, length);
 }
 
 static void desc_read_cb(bool success, uint8_t att_ecode,
@@ -325,8 +342,6 @@
 {
 	struct async_dbus_op *op = user_data;
 	struct descriptor *desc = op->data;
-	struct service *service = desc->chrc->service;
-	DBusMessage *reply;
 
 	if (!success)
 		goto fail;
@@ -337,42 +352,24 @@
 	if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0,
 					NULL, write_descriptor_cb, desc)) {
 		error("Failed to store attribute");
+		att_ecode = BT_ATT_ERROR_UNLIKELY;
 		goto fail;
 	}
 
-	/*
-	 * If the value length is exactly MTU-1, then we may not have read the
-	 * entire value. Perform a long read to obtain the rest, otherwise,
-	 * we're done.
-	 */
-	if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) {
-		op->offset += length;
-		desc->read_id = bt_gatt_client_read_long_value(
-							service->client->gatt,
-							desc->handle,
-							op->offset,
-							desc_read_cb,
-							async_dbus_op_ref(op),
-							async_dbus_op_unref);
-		if (desc->read_id)
-			return;
-	}
-
 	/* Read the stored data from db */
 	if (!gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op)) {
 		error("Failed to read database");
+		att_ecode = BT_ATT_ERROR_UNLIKELY;
 		goto fail;
 	}
 
-	desc->read_id = 0;
+	desc->read_op = NULL;
 
 	return;
 
 fail:
-	reply = create_gatt_dbus_error(op->msg, att_ecode);
-	desc->read_id = 0;
-	g_dbus_send_message(btd_get_dbus_connection(), reply);
-	return;
+	async_dbus_op_reply(op, att_ecode, NULL, 0);
+	desc->read_op = NULL;
 }
 
 static int parse_options(DBusMessageIter *iter, uint16_t *offset)
@@ -401,24 +398,45 @@
 				return -EINVAL;
 			dbus_message_iter_get_basic(&value, offset);
 		}
+
+		dbus_message_iter_next(&dict);
 	}
 
 	return 0;
 }
 
-static unsigned int read_value(struct bt_gatt_client *gatt, uint16_t handle,
-				bt_gatt_client_read_callback_t callback,
-				struct async_dbus_op *op)
+static struct async_dbus_op *async_dbus_op_new(DBusMessage *msg, void *data)
 {
-	if (op->offset)
-		return bt_gatt_client_read_long_value(gatt, handle, op->offset,
-							callback,
-							async_dbus_op_ref(op),
-							async_dbus_op_unref);
-	else
-		return bt_gatt_client_read_value(gatt, handle, callback,
-							async_dbus_op_ref(op),
-							async_dbus_op_unref);
+	struct async_dbus_op *op;
+
+	op = new0(struct async_dbus_op, 1);
+	op->msgs = queue_new();
+	queue_push_tail(op->msgs, dbus_message_ref(msg));
+	op->data = data;
+
+	return op;
+}
+
+static struct async_dbus_op *read_value(struct bt_gatt_client *gatt,
+					DBusMessage *msg, uint16_t handle,
+					uint16_t offset,
+					bt_gatt_client_read_callback_t callback,
+					void *data)
+{
+	struct async_dbus_op *op;
+
+	op = async_dbus_op_new(msg, data);
+	op->offset = offset;
+
+	op->id = bt_gatt_client_read_long_value(gatt, handle, offset, callback,
+						async_dbus_op_ref(op),
+						async_dbus_op_unref);
+	if (op->id)
+		return op;
+
+	async_dbus_op_free(op);
+
+	return NULL;
 }
 
 static DBusMessage *descriptor_read_value(DBusConnection *conn,
@@ -427,63 +445,51 @@
 	struct descriptor *desc = user_data;
 	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
 	DBusMessageIter iter;
-	struct async_dbus_op *op;
 	uint16_t offset = 0;
 
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
 
-	if (desc->read_id)
-		return btd_error_in_progress(msg);
-
 	dbus_message_iter_init(msg, &iter);
 
 	if (parse_options(&iter, &offset))
 		return btd_error_invalid_args(msg);
 
-	op = new0(struct async_dbus_op, 1);
-	op->msg = dbus_message_ref(msg);
-	op->data = desc;
-	op->offset = offset;
-
-	desc->read_id = read_value(gatt, desc->handle, desc_read_cb, op);
-	if (desc->read_id)
+	if (desc->read_op) {
+		if (desc->read_op->offset != offset)
+			return btd_error_in_progress(msg);
+		queue_push_tail(desc->read_op->msgs, dbus_message_ref(msg));
 		return NULL;
+	}
 
-	async_dbus_op_free(op);
+	desc->read_op = read_value(gatt, msg, desc->handle, offset,
+							desc_read_cb, desc);
+	if (!desc->read_op)
+		return btd_error_failed(msg, "Failed to send read request");
 
-	return btd_error_failed(msg, "Failed to send read request");
+	return NULL;
 }
 
 static void write_result_cb(bool success, bool reliable_error,
 					uint8_t att_ecode, void *user_data)
 {
 	struct async_dbus_op *op = user_data;
-	DBusMessage *reply;
+	int err = 0;
 
 	if (op->complete && !op->complete(op->data)) {
-		reply = btd_error_failed(op->msg, "Operation failed");
+		err = -EFAULT;
 		goto done;
 	}
 
 	if (!success) {
 		if (reliable_error)
-			reply = btd_error_failed(op->msg,
-						"Reliable write failed");
+			err = -EFAULT;
 		else
-			reply = create_gatt_dbus_error(op->msg, att_ecode);
-
-		goto done;
-	}
-
-	reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
-	if (!reply) {
-		error("Failed to allocate D-Bus message reply");
-		return;
+			err = att_ecode;
 	}
 
 done:
-	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	async_dbus_op_reply(op, err, NULL, 0);
 }
 
 static void write_cb(bool success, uint8_t att_ecode, void *user_data)
@@ -491,7 +497,7 @@
 	write_result_cb(success, false, att_ecode, user_data);
 }
 
-static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
+static struct async_dbus_op *start_long_write(DBusMessage *msg, uint16_t handle,
 					struct bt_gatt_client *gatt,
 					bool reliable, const uint8_t *value,
 					size_t value_len, uint16_t offset,
@@ -499,53 +505,52 @@
 					async_dbus_op_complete_t complete)
 {
 	struct async_dbus_op *op;
-	unsigned int id;
 
-	op = new0(struct async_dbus_op, 1);
-	op->msg = dbus_message_ref(msg);
-	op->data = data;
+	op = async_dbus_op_new(msg, data);
 	op->complete = complete;
 	op->offset = offset;
 
-	id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset,
+	op->id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset,
 							value, value_len,
 							write_result_cb, op,
 							async_dbus_op_free);
 
-	if (!id)
+	if (!op->id) {
 		async_dbus_op_free(op);
+		return NULL;
+	}
 
-	return id;
+	return op;
 }
 
-static unsigned int start_write_request(DBusMessage *msg, uint16_t handle,
+static struct async_dbus_op *start_write_request(DBusMessage *msg,
+					uint16_t handle,
 					struct bt_gatt_client *gatt,
 					const uint8_t *value, size_t value_len,
 					void *data,
 					async_dbus_op_complete_t complete)
 {
 	struct async_dbus_op *op;
-	unsigned int id;
 
-	op = new0(struct async_dbus_op, 1);
-	op->msg = dbus_message_ref(msg);
-	op->data = data;
+	op = async_dbus_op_new(msg, data);
 	op->complete = complete;
 
-	id = bt_gatt_client_write_value(gatt, handle, value, value_len,
+	op->id = bt_gatt_client_write_value(gatt, handle, value, value_len,
 							write_cb, op,
 							async_dbus_op_free);
-	if (!id)
+	if (!op->id) {
 		async_dbus_op_free(op);
+		return NULL;
+	}
 
-	return id;
+	return op;
 }
 
 static bool desc_write_complete(void *data)
 {
 	struct descriptor *desc = data;
 
-	desc->write_id = 0;
+	desc->write_op = NULL;
 
 	/*
 	 * The descriptor might have been unregistered during the read. Return
@@ -567,7 +572,7 @@
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
 
-	if (desc->write_id)
+	if (desc->write_op)
 		return btd_error_in_progress(msg);
 
 	dbus_message_iter_init(msg, &iter);
@@ -591,17 +596,17 @@
 	 * write.
 	 */
 	if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset)
-		desc->write_id = start_write_request(msg, desc->handle,
+		desc->write_op = start_write_request(msg, desc->handle,
 							gatt, value,
 							value_len, desc,
 							desc_write_complete);
 	else
-		desc->write_id = start_long_write(msg, desc->handle, gatt,
+		desc->write_op = start_long_write(msg, desc->handle, gatt,
 							false, value,
 							value_len, offset, desc,
 							desc_write_complete);
 
-	if (!desc->write_id)
+	if (!desc->write_op)
 		return btd_error_failed(msg, "Failed to initiate write");
 
 	return NULL;
@@ -679,11 +684,11 @@
 
 	DBG("Removing GATT descriptor: %s", desc->path);
 
-	if (desc->read_id)
-		bt_gatt_client_cancel(gatt, desc->read_id);
+	if (desc->read_op)
+		bt_gatt_client_cancel(gatt, desc->read_op->id);
 
-	if (desc->write_id)
-		bt_gatt_client_cancel(gatt, desc->write_id);
+	if (desc->write_op)
+		bt_gatt_client_cancel(gatt, desc->write_op->id);
 
 	desc->chrc = NULL;
 
@@ -829,8 +834,6 @@
 {
 	struct async_dbus_op *op = user_data;
 	struct characteristic *chrc = op->data;
-	struct service *service = chrc->service;
-	DBusMessage *reply;
 
 	if (!success)
 		goto fail;
@@ -841,41 +844,24 @@
 	if (!gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0,
 					NULL, write_characteristic_cb, chrc)) {
 		error("Failed to store attribute");
+		att_ecode = BT_ATT_ERROR_UNLIKELY;
 		goto fail;
 	}
 
-	/*
-	 * If the value length is exactly MTU-1, then we may not have read the
-	 * entire value. Perform a long read to obtain the rest, otherwise,
-	 * we're done.
-	 */
-	if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) {
-		op->offset += length;
-		chrc->read_id = bt_gatt_client_read_long_value(
-							service->client->gatt,
-							chrc->value_handle,
-							op->offset,
-							chrc_read_cb,
-							async_dbus_op_ref(op),
-							async_dbus_op_unref);
-		if (chrc->read_id)
-			return;
-	}
-
-	chrc->read_id = 0;
-
 	/* Read the stored data from db */
 	if (!gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op)) {
 		error("Failed to read database");
+		att_ecode = BT_ATT_ERROR_UNLIKELY;
 		goto fail;
 	}
 
+	chrc->read_op = NULL;
+
 	return;
 
 fail:
-	reply = create_gatt_dbus_error(op->msg, att_ecode);
-	chrc->read_id = 0;
-	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	async_dbus_op_reply(op, att_ecode, NULL, 0);
+	chrc->read_op = NULL;
 }
 
 static DBusMessage *characteristic_read_value(DBusConnection *conn,
@@ -884,39 +870,36 @@
 	struct characteristic *chrc = user_data;
 	struct bt_gatt_client *gatt = chrc->service->client->gatt;
 	DBusMessageIter iter;
-	struct async_dbus_op *op;
 	uint16_t offset = 0;
 
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
 
-	if (chrc->read_id)
-		return btd_error_in_progress(msg);
-
 	dbus_message_iter_init(msg, &iter);
 
 	if (parse_options(&iter, &offset))
 		return btd_error_invalid_args(msg);
 
-	op = new0(struct async_dbus_op, 1);
-	op->msg = dbus_message_ref(msg);
-	op->data = chrc;
-	op->offset = offset;
-
-	chrc->read_id = read_value(gatt, chrc->value_handle, chrc_read_cb, op);
-	if (chrc->read_id)
+	if (chrc->read_op) {
+		if (chrc->read_op->offset != offset)
+			return btd_error_in_progress(msg);
+		queue_push_tail(chrc->read_op->msgs, dbus_message_ref(msg));
 		return NULL;
+	}
 
-	async_dbus_op_free(op);
+	chrc->read_op = read_value(gatt, msg, chrc->value_handle, offset,
+							chrc_read_cb, chrc);
+	if (!chrc->read_op)
+		return btd_error_failed(msg, "Failed to send read request");
 
-	return btd_error_failed(msg, "Failed to send read request");
+	return NULL;
 }
 
 static bool chrc_write_complete(void *data)
 {
 	struct characteristic *chrc = data;
 
-	chrc->write_id = 0;
+	chrc->write_op = NULL;
 
 	/*
 	 * The characteristic might have been unregistered during the read.
@@ -939,7 +922,7 @@
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
 
-	if (chrc->write_id)
+	if (chrc->write_op)
 		return btd_error_in_progress(msg);
 
 	dbus_message_iter_init(msg, &iter);
@@ -963,10 +946,10 @@
 	 */
 	if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) {
 		supported = true;
-		chrc->write_id = start_long_write(msg, chrc->value_handle, gatt,
+		chrc->write_op = start_long_write(msg, chrc->value_handle, gatt,
 						true, value, value_len, offset,
 						chrc, chrc_write_complete);
-		if (chrc->write_id)
+		if (chrc->write_op)
 			return NULL;
 	}
 
@@ -979,17 +962,17 @@
 			return btd_error_failed(msg, "No ATT transport");
 
 		if (value_len <= mtu - 3 && !offset)
-			chrc->write_id = start_write_request(msg,
+			chrc->write_op = start_write_request(msg,
 						chrc->value_handle,
 						gatt, value, value_len,
 						chrc, chrc_write_complete);
 		else
-			chrc->write_id = start_long_write(msg,
+			chrc->write_op = start_long_write(msg,
 						chrc->value_handle, gatt,
 						false, value, value_len, offset,
 						chrc, chrc_write_complete);
 
-		if (chrc->write_id)
+		if (chrc->write_op)
 			return NULL;
 	}
 
@@ -1138,26 +1121,17 @@
 						write_characteristic_cb, chrc);
 }
 
-static DBusMessage *create_notify_reply(struct async_dbus_op *op,
-						bool success, uint8_t att_ecode)
+static void create_notify_reply(struct async_dbus_op *op, bool success,
+							uint8_t att_ecode)
 {
-	DBusMessage *reply = NULL;
-
-	if (!op->msg)
-		return NULL;
+	int err;
 
 	if (success)
-		reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
-	else if (att_ecode)
-		reply = create_gatt_dbus_error(op->msg, att_ecode);
+		err = 0;
 	else
-		reply = btd_error_failed(op->msg,
-						"Characteristic not available");
+		err = att_ecode ? att_ecode : -ENOENT;
 
-	if (!reply)
-		error("Failed to construct D-Bus message reply");
-
-	return reply;
+	async_dbus_op_reply(op, err, NULL, 0);
 }
 
 static void register_notify_cb(uint16_t att_ecode, void *user_data)
@@ -1165,16 +1139,15 @@
 	struct async_dbus_op *op = user_data;
 	struct notify_client *client = op->data;
 	struct characteristic *chrc = client->chrc;
-	DBusMessage *reply;
 
 	if (att_ecode) {
 		queue_remove(chrc->notify_clients, client);
 		queue_remove(chrc->service->client->all_notify_clients, client);
 		notify_client_free(client);
 
-		reply = create_notify_reply(op, false, att_ecode);
+		create_notify_reply(op, false, att_ecode);
 
-		goto done;
+		return;
 	}
 
 	if (!chrc->notifying) {
@@ -1184,11 +1157,7 @@
 					"Notifying");
 	}
 
-	reply = create_notify_reply(op, true, 0);
-
-done:
-	if (reply)
-		g_dbus_send_message(btd_get_dbus_connection(), reply);
+	create_notify_reply(op, true, 0);
 }
 
 static DBusMessage *characteristic_start_notify(DBusConnection *conn,
@@ -1238,9 +1207,7 @@
 		goto fail;
 	}
 
-	op = new0(struct async_dbus_op, 1);
-	op->data = client;
-	op->msg = dbus_message_ref(msg);
+	op = async_dbus_op_new(msg, client);
 
 	client->notify_id = bt_gatt_client_register_notify(gatt,
 						chrc->value_handle,
@@ -1393,11 +1360,11 @@
 
 	DBG("Removing GATT characteristic: %s", chrc->path);
 
-	if (chrc->read_id)
-		bt_gatt_client_cancel(gatt, chrc->read_id);
+	if (chrc->read_op)
+		bt_gatt_client_cancel(gatt, chrc->read_op->id);
 
-	if (chrc->write_id)
-		bt_gatt_client_cancel(gatt, chrc->write_id);
+	if (chrc->write_op)
+		bt_gatt_client_cancel(gatt, chrc->write_op->id);
 
 	queue_remove_all(chrc->notify_clients, NULL, NULL, remove_client);
 	queue_remove_all(chrc->descs, NULL, NULL, unregister_descriptor);
diff --git a/src/gatt-database.c b/src/gatt-database.c
index e287b98..bf1925b 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -468,8 +468,6 @@
 	uint8_t dst_type;
 	bdaddr_t src, dst;
 
-	DBG("New incoming LE ATT connection");
-
 	if (gerr) {
 		error("%s", gerr->message);
 		return;
@@ -485,6 +483,9 @@
 		return;
 	}
 
+	DBG("New incoming %s ATT connection", dst_type == BDADDR_BREDR ?
+							"BR/EDR" : "LE");
+
 	adapter = adapter_find(&src);
 	if (!adapter)
 		return;
@@ -1168,7 +1169,7 @@
 		} else if (!strcmp("secure-read", flag)) {
 			*props |= BT_GATT_CHRC_PROP_READ;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
-			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_READ_SECURE;
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_SECURE;
 		} else if (!strcmp("secure-write", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
@@ -1210,9 +1211,9 @@
 		else if (!strcmp("encrypt-authenticated-write", flag))
 			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
 		else if (!strcmp("secure-read", flag))
-			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_SECURE;
 		else if (!strcmp("secure-write", flag))
-			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE;
 		else {
 			error("Invalid descriptor flag: %s", flag);
 			return false;
diff --git a/src/service.c b/src/service.c
index f7912f5..0da14ab 100644
--- a/src/service.c
+++ b/src/service.c
@@ -353,7 +353,8 @@
 
 void btd_service_connecting_complete(struct btd_service *service, int err)
 {
-	if (service->state != BTD_SERVICE_STATE_CONNECTING)
+	if (service->state != BTD_SERVICE_STATE_DISCONNECTED &&
+			service->state != BTD_SERVICE_STATE_CONNECTING)
 		return;
 
 	if (err == 0)
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index 4a9b67f..51922d1 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -37,6 +37,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
+
 /* ATT protocol opcodes */
 #define BT_ATT_OP_ERROR_RSP			0x01
 #define BT_ATT_OP_MTU_REQ			0x02
diff --git a/src/shared/att.c b/src/shared/att.c
index abcf3bb..f1e0f59 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -34,6 +34,7 @@
 #include "src/shared/util.h"
 #include "src/shared/timeout.h"
 #include "lib/bluetooth.h"
+#include "lib/l2cap.h"
 #include "lib/uuid.h"
 #include "src/shared/att.h"
 #include "src/shared/crypto.h"
@@ -184,7 +185,7 @@
 	unsigned int id;
 	unsigned int timeout_id;
 	enum att_op_type type;
-	uint16_t opcode;
+	uint8_t opcode;
 	void *pdu;
 	uint16_t len;
 	bt_att_response_func_t callback;
@@ -967,6 +968,18 @@
 	free(att);
 }
 
+static uint16_t get_l2cap_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;
+
+	return l2o.omtu;
+}
+
 struct bt_att *bt_att_new(int fd, bool ext_signed)
 {
 	struct bt_att *att;
@@ -976,10 +989,6 @@
 
 	att = new0(struct bt_att, 1);
 	att->fd = fd;
-	att->mtu = BT_ATT_DEFAULT_LE_MTU;
-	att->buf = malloc(att->mtu);
-	if (!att->buf)
-		goto fail;
 
 	att->io = io_new(fd);
 	if (!att->io)
@@ -1005,6 +1014,18 @@
 	if (!att->io_on_l2cap)
 		att->io_sec_level = BT_ATT_SECURITY_LOW;
 
+	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;
+
+	if (att->mtu < BT_ATT_DEFAULT_LE_MTU)
+		goto fail;
+
+	att->buf = malloc(att->mtu);
+	if (!att->buf)
+		goto fail;
+
 	return bt_att_ref(att);
 
 fail:
@@ -1099,6 +1120,28 @@
 	return true;
 }
 
+uint8_t bt_att_get_link_type(struct bt_att *att)
+{
+	struct sockaddr_l2 src;
+	socklen_t len;
+
+	if (!att)
+		return -EINVAL;
+
+	if (!att->io_on_l2cap)
+		return BT_ATT_LINK_LOCAL;
+
+	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;
+}
+
 bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
 						void *user_data,
 						bt_att_destroy_func_t destroy)
diff --git a/src/shared/att.h b/src/shared/att.h
index 2a7f87e..7bffee7 100644
--- a/src/shared/att.h
+++ b/src/shared/att.h
@@ -53,6 +53,7 @@
 
 uint16_t bt_att_get_mtu(struct bt_att *att);
 bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu);
+uint8_t bt_att_get_link_type(struct bt_att *att);
 
 bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
 						void *user_data,
diff --git a/src/shared/crypto.c b/src/shared/crypto.c
index aa66dac..6de5514 100644
--- a/src/shared/crypto.c
+++ b/src/shared/crypto.c
@@ -568,8 +568,8 @@
 	return bt_crypto_e(crypto, k, res, res);
 }
 
-static bool aes_cmac(struct bt_crypto *crypto, uint8_t key[16], uint8_t *msg,
-					size_t msg_len, uint8_t res[16])
+static bool aes_cmac(struct bt_crypto *crypto, const uint8_t key[16],
+			const uint8_t *msg, size_t msg_len, uint8_t res[16])
 {
 	uint8_t key_msb[16], out[16], msg_msb[CMAC_MSG_MAX];
 	ssize_t len;
@@ -679,3 +679,12 @@
 
 	return true;
 }
+
+bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16],
+				const uint8_t keyid[4], uint8_t res[16])
+{
+	if (!aes_cmac(crypto, w, keyid, 4, res))
+		return false;
+
+	return true;
+}
diff --git a/src/shared/crypto.h b/src/shared/crypto.h
index 9ba5803..84d4992 100644
--- a/src/shared/crypto.h
+++ b/src/shared/crypto.h
@@ -56,6 +56,8 @@
 			uint8_t a1[7], uint8_t a2[7], uint8_t res[16]);
 bool bt_crypto_g2(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
 				uint8_t x[16], uint8_t y[16], uint32_t *val);
+bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16],
+				const uint8_t keyid[4], uint8_t res[16]);
 bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
 				const uint8_t *m, uint16_t m_len,
 				uint32_t sign_cnt, uint8_t signature[12]);
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index adabfe3..8fd8a45 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1622,14 +1622,25 @@
 	if (!op)
 		return false;
 
+	/*
+	 * BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 546:
+	 *
+	 * 4.3.1 Exchange MTU
+	 *
+	 * This sub-procedure shall not be used on a BR/EDR physical link since
+	 * the MTU size is negotiated using L2CAP channel configuration
+	 * procedures.
+	 */
+	if (bt_att_get_link_type(client->att) == BT_ATT_LINK_BREDR)
+		goto discover;
+
 	/* Check if MTU needs to be send */
 	mtu = MAX(BT_ATT_DEFAULT_LE_MTU, mtu);
 	if (mtu == BT_ATT_DEFAULT_LE_MTU)
 		goto discover;
 
 	/* Configure the MTU */
-	client->mtu_req_id = bt_gatt_exchange_mtu(client->att,
-						MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
+	client->mtu_req_id = bt_gatt_exchange_mtu(client->att, mtu,
 						exchange_mtu_cb,
 						discovery_op_ref(op),
 						discovery_op_unref);
diff --git a/test/example-gatt-server b/test/example-gatt-server
index 71aeb1b..84905f3 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -117,7 +117,7 @@
         if interface != GATT_SERVICE_IFACE:
             raise InvalidArgsException()
 
-        return self.get_properties[GATT_SERVICE_IFACE]
+        return self.get_properties()[GATT_SERVICE_IFACE]
 
 
 class Characteristic(dbus.service.Object):
@@ -164,7 +164,7 @@
         if interface != GATT_CHRC_IFACE:
             raise InvalidArgsException()
 
-        return self.get_properties[GATT_CHRC_IFACE]
+        return self.get_properties()[GATT_CHRC_IFACE]
 
     @dbus.service.method(GATT_CHRC_IFACE,
                         in_signature='a{sv}',
@@ -222,7 +222,7 @@
         if interface != GATT_DESC_IFACE:
             raise InvalidArgsException()
 
-        return self.get_properties[GATT_CHRC_IFACE]
+        return self.get_properties()[GATT_CHRC_IFACE]
 
     @dbus.service.method(GATT_DESC_IFACE,
                         in_signature='a{sv}',
diff --git a/test/simple-endpoint b/test/simple-endpoint
index 0164cff..78fb5fd 100755
--- a/test/simple-endpoint
+++ b/test/simple-endpoint
@@ -66,9 +66,9 @@
 			mainloop.quit()
 
 	@dbus.service.method("org.bluez.MediaEndpoint1",
-					in_signature="", out_signature="")
-	def ClearConfiguration(self):
-		print("ClearConfiguration")
+					in_signature="o", out_signature="")
+	def ClearConfiguration(self, transport):
+		print("ClearConfiguration (%s)" % (transport))
 
 	@dbus.service.method("org.bluez.MediaEndpoint1",
 					in_signature="oay", out_signature="")
diff --git a/tools/btinfo.c b/tools/btinfo.c
index fd11ac4..8e36577 100644
--- a/tools/btinfo.c
+++ b/tools/btinfo.c
@@ -79,7 +79,7 @@
 
 #define HCI_UP		(1 << 0)
 
-#define HCI_BREDR	0x00
+#define HCI_PRIMARY	0x00
 #define HCI_AMP		0x01
 
 static struct hci_dev_info hci_info;
@@ -128,7 +128,7 @@
 	printf("HCI revision: %u\n", le16_to_cpu(rsp->hci_rev));
 
 	switch (hci_type) {
-	case HCI_BREDR:
+	case HCI_PRIMARY:
 		printf("LMP version: %u\n", rsp->lmp_ver);
 		printf("LMP subversion: %u\n", le16_to_cpu(rsp->lmp_subver));
 		break;
diff --git a/tools/btproxy.c b/tools/btproxy.c
index 43de037..4bb7fef 100644
--- a/tools/btproxy.c
+++ b/tools/btproxy.c
@@ -49,7 +49,7 @@
 #include "src/shared/ecc.h"
 #include "monitor/bt.h"
 
-#define HCI_BREDR	0x00
+#define HCI_PRIMARY	0x00
 #define HCI_AMP		0x01
 
 #define BTPROTO_HCI	1
@@ -758,7 +758,7 @@
 	const char *unix_path = NULL;
 	unsigned short tcp_port = 0xb1ee;	/* 45550 */
 	bool use_redirect = false;
-	uint8_t type = HCI_BREDR;
+	uint8_t type = HCI_PRIMARY;
 	const char *str;
 	sigset_t mask;
 
diff --git a/tools/cltest.c b/tools/cltest.c
index 95fa7b6..44a17a8 100644
--- a/tools/cltest.c
+++ b/tools/cltest.c
@@ -228,7 +228,7 @@
 		if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0)
 			continue;
 
-		if (((di.type & 0x30) >> 4) != HCI_BREDR)
+		if (((di.type & 0x30) >> 4) != HCI_PRIMARY)
 			continue;
 
 		if (!bacmp(&bdaddr_src, BDADDR_ANY)) {
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 0c78c4d..6bd5576 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -407,6 +407,8 @@
 			dbus_message_iter_get_basic(&value, device);
 			printf("Device: %s\n", *device);
 		}
+
+		dbus_message_iter_next(&dict);
 	}
 
 	return 0;
@@ -513,10 +515,6 @@
 
 	dbus_message_iter_init_append(reply, &iter);
 
-	if (parse_options(&iter, &device))
-		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
-							"Invalid arguments");
-
 	desc_read(desc, &iter);
 
 	return reply;
diff --git a/tools/hciconfig.c b/tools/hciconfig.c
index 93bb8ab..a609008 100644
--- a/tools/hciconfig.c
+++ b/tools/hciconfig.c
@@ -1151,7 +1151,7 @@
 	}
 
 	hciver = hci_vertostr(ver.hci_ver);
-	if (((di.type & 0x30) >> 4) == HCI_BREDR)
+	if (((di.type & 0x30) >> 4) == HCI_PRIMARY)
 		lmpver = lmp_vertostr(ver.lmp_ver);
 	else
 		lmpver = pal_vertostr(ver.lmp_ver);
@@ -1161,7 +1161,7 @@
 		"\t%s Version: %s (0x%x)  Subversion: 0x%x\n"
 		"\tManufacturer: %s (%d)\n",
 		hciver ? hciver : "n/a", ver.hci_ver, ver.hci_rev,
-		(((di.type & 0x30) >> 4) == HCI_BREDR) ? "LMP" : "PAL",
+		(((di.type & 0x30) >> 4) == HCI_PRIMARY) ? "LMP" : "PAL",
 		lmpver ? lmpver : "n/a", ver.lmp_ver, ver.lmp_subver,
 		bt_compidtostr(ver.manufacturer), ver.manufacturer);
 
@@ -1939,7 +1939,7 @@
 	if (all && !hci_test_bit(HCI_RAW, &di->flags)) {
 		print_dev_features(di, 0);
 
-		if (((di->type & 0x30) >> 4) == HCI_BREDR) {
+		if (((di->type & 0x30) >> 4) == HCI_PRIMARY) {
 			print_pkt_type(di);
 			print_link_policy(di);
 			print_link_mode(di);
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index 494d436..b6f4873 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -428,6 +428,7 @@
 	bool just_works;
 	bool client_enable_le;
 	bool client_enable_sc;
+	bool client_enable_adv;
 	bool expect_sc_key;
 	bool force_power_off;
 	bool addr_type_avail;
@@ -3090,6 +3091,96 @@
 	.verify_alt_ev_func = verify_ltk,
 };
 
+static bool lk_is_authenticated(const struct mgmt_link_key_info *lk)
+{
+	switch (lk->type) {
+	case 0x00: /* Combination Key */
+	case 0x01: /* Local Unit Key */
+	case 0x02: /* Remote Unit Key */
+	case 0x03: /* Debug Combination Key */
+		if (lk->pin_len == 16)
+			return true;
+		return false;
+	case 0x05: /* Authenticated Combination Key generated from P-192 */
+	case 0x08: /* Authenticated Combination Key generated from P-256 */
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool lk_is_sc(const struct mgmt_link_key_info *lk)
+{
+	switch (lk->type) {
+	case 0x07: /* Unauthenticated Combination Key generated from P-256 */
+	case 0x08: /* Authenticated Combination Key generated from P-256 */
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool verify_link_key(const void *param, uint16_t length)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const struct mgmt_ev_new_link_key *ev = param;
+
+	if (length != sizeof(struct mgmt_ev_new_link_key)) {
+		tester_warn("Invalid new Link Key length %u != %zu", length,
+				sizeof(struct mgmt_ev_new_link_key));
+		return false;
+	}
+
+	if (test->just_works && lk_is_authenticated(&ev->key)) {
+		tester_warn("Authenticated key for just-works");
+		return false;
+	}
+
+	if (!test->just_works && !lk_is_authenticated(&ev->key)) {
+		tester_warn("Unauthenticated key for MITM");
+		return false;
+	}
+
+	if (test->expect_sc_key && !lk_is_sc(&ev->key)) {
+		tester_warn("Non-LE SC key for SC pairing");
+		return false;
+	}
+
+	if (!test->expect_sc_key && lk_is_sc(&ev->key)) {
+		tester_warn("SC key for Non-SC pairing");
+		return false;
+	}
+
+	return true;
+}
+
+static uint16_t settings_powered_le_sc_bondable[] = {
+						MGMT_OP_SET_LE,
+						MGMT_OP_SET_SSP,
+						MGMT_OP_SET_BONDABLE,
+						MGMT_OP_SET_SECURE_CONN,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pair_device_le_sc_success_test_3 = {
+	.setup_settings = settings_powered_le_sc_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.addr_type_avail = true,
+	.addr_type = 0x01,
+	.client_enable_sc = true,
+	.client_enable_ssp = true,
+	.client_enable_adv = true,
+	.expect_sc_key = true,
+	.io_cap = 0x02, /* KeyboardOnly */
+	.client_io_cap = 0x02, /* KeyboardOnly */
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.verify_alt_ev_func = verify_link_key,
+};
+
 static uint16_t settings_powered_connectable_bondable[] = {
 						MGMT_OP_SET_BONDABLE,
 						MGMT_OP_SET_CONNECTABLE,
@@ -4808,11 +4899,12 @@
 static void setup_bthost(void)
 {
 	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
 	struct bthost *bthost;
 
 	bthost = hciemu_client_get_host(data->hciemu);
 	bthost_set_cmd_complete_cb(bthost, client_cmd_complete, data);
-	if (data->hciemu_type == HCIEMU_TYPE_LE)
+	if (data->hciemu_type == HCIEMU_TYPE_LE || test->client_enable_adv)
 		bthost_set_adv_enable(bthost, 0x01);
 	else
 		bthost_write_scan_enable(bthost, 0x03);
@@ -6653,6 +6745,9 @@
 	test_le("Pair Device - LE SC Success 2",
 				&pair_device_le_sc_success_test_2,
 				NULL, test_command_generic);
+	test_bredrle("Pair Device - LE SC Success 3",
+				&pair_device_le_sc_success_test_3,
+				NULL, test_command_generic);
 
 	test_bredrle("Pairing Acceptor - Legacy 1",
 				&pairing_acceptor_legacy_1, NULL,
diff --git a/unit/test-crypto.c b/unit/test-crypto.c
index 8d65707..bc37abb 100644
--- a/unit/test-crypto.c
+++ b/unit/test-crypto.c
@@ -34,6 +34,48 @@
 
 static struct bt_crypto *crypto;
 
+static void print_debug(const char *str, void *user_data)
+{
+	tester_debug("%s", str);
+}
+
+static void test_h6(gconstpointer data)
+{
+	const uint8_t w[16] = {
+			0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
+			0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
+	const uint8_t m[4] = { 0x72, 0x62, 0x65, 0x6c };
+	const uint8_t exp[16] = {
+			0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8,
+			0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d };
+	uint8_t res[16];
+
+	tester_debug("W:");
+	util_hexdump(' ', w, 16, print_debug, NULL);
+
+	tester_debug("M:");
+	util_hexdump(' ', m, 4, print_debug, NULL);
+
+	if (!bt_crypto_h6(crypto, w, m, res)) {
+		tester_test_failed();
+		return;
+	}
+
+	tester_debug("Expected:");
+	util_hexdump(' ', exp, 16, print_debug, NULL);
+
+	tester_debug("Result:");
+	util_hexdump(' ', res, 16, print_debug, NULL);
+
+
+	if (memcmp(res, exp, 16)) {
+		tester_test_failed();
+		return;
+	}
+
+	tester_test_passed();
+}
+
 struct test_data {
 	const uint8_t *msg;
 	uint16_t msg_len;
@@ -137,11 +179,6 @@
 	.key = key_5,
 };
 
-static void print_debug(const char *str, void *user_data)
-{
-	tester_debug("%s", str);
-}
-
 static bool result_compare(const uint8_t exp[12], uint8_t res[12])
 {
 	int i;
@@ -181,6 +218,8 @@
 
 	tester_init(&argc, &argv);
 
+	tester_add("/crypto/h6", NULL, NULL, test_h6, NULL);
+
 	tester_add("/crypto/sign_att_1", &test_data_1, NULL, test_sign, NULL);
 	tester_add("/crypto/sign_att_2", &test_data_2, NULL, test_sign, NULL);
 	tester_add("/crypto/sign_att_3", &test_data_3, NULL, test_sign, NULL);