Merge BlueZ 5.43 to master

Release of BlueZ 5.43
29th October 2016, 09:44 am by Johan Hedberg

This is almost purely a bug-fix release with fixes to HoG, ATT and PAN.
There’s also a fix for a regression in 5.42 that caused connection failures
for external profiles like OBEX. Feature-wise there’s one notable addition:
LE privacy. By enabling this in main.conf it’s now possible to make BlueZ
generate a local Identity Resolving Key (IRK) and use Resolvable Private
Addresses (RPAs) for itself.

Release of BlueZ 5.42
26th September 2016, 06:33 pm by Johan Hedberg

The major API change with this release is that the GATT D-Bus API is no longer
marked as experimental. This should hopefully encourage the creation of more
applications using it. Feature-wise, btmon received support for decoding full
mgmt message traces – a feature that is available in the bluetooth-next kernel
tree, i.e. on its way to the 4.9 kernel release. In addition to these changes,
this release contains also fixes in areas such as PBAP, transport selection
(BR/EDR vs LE), ATT and A2DP.

NOTE:
Adjustments were necessary to audioOverBle, battery, and oad profiles due to:

commit 82666b345e14b0a723e3d7cf8e56003edef22fed
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date:   Mon Sep 26 16:46:09 2016 +0300
core: Remove attio callbacks

Change-Id: I8d25c7c866f76c23fcca284e3b20b237b4b1b7eb
diff --git a/AUTHORS b/AUTHORS
index 16fb14c..176b55b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -99,3 +99,4 @@
 Bharat Panda <bharat.panda@samsung.com>
 Marie Janssen <jamuraa@chromium.org>
 Jaganath Kanakkassery <jaganath.k@samsung.com>
+Michał Narajowski <michal.narajowski@codecoup.pl>
diff --git a/ChangeLog b/ChangeLog
index 920175e..8e01791 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+ver 5.43:
+	Fix issue with HID over GATT support.
+	Fix issue with ATT Find By Type response handling.
+	Fix issue with handling insufficient authentication.
+	Fix issue with bonding while pairing is in progress.
+	Fix issue with BR/EDR pairing for dual-mode devices.
+	Fix issue with handling profile policy resets.
+	Fix issue with connecting state of services.
+	Fix issue with handling PAN GN Master role.
+	Add support for enabling LE Privacy feature.
+
+ver 5.42:
+	Fix issue with PBAP call logs from different folders.
+	Fix issue with OBEX over L2CAP and PowerPC architecture.
+	Fix issue with BR/EDR over LE selection during discovery.
+	Fix issue with selection of bearer after bonding.
+	Fix issue with handling socket recv() return values.
+	Fix issue with setting connecting service state.
+	Fix issue with setting correct ATT default MTU value.
+	Fix issue with not setting AVRCP player identifier.
+	Fix issue with handling AVRCP browsable player.
+	Fix issue with addressing AVRCP player changes.
+	Add support for new management tracing capability.
+	Mark GATT D-Bus APIs as stable interfaces.
+
 ver 5.41:
 	Fix issue with service state changes handling.
 	Fix issue with AVRCP and no available player.
diff --git a/Makefile.am b/Makefile.am
index 4ce642f..c469a6c 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:13:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:14:18
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
@@ -185,7 +185,7 @@
 			src/profile.h src/profile.c \
 			src/service.h src/service.c \
 			src/gatt-client.h src/gatt-client.c \
-			src/device.h src/device.c src/attio.h \
+			src/device.h src/device.c \
 			src/dbus-common.c src/dbus-common.h \
 			src/eir.h src/eir.c
 src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
diff --git a/Makefile.plugins b/Makefile.plugins
index 78530ec..192a2cc 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -96,35 +96,6 @@
 builtin_modules += deviceinfo
 builtin_sources += profiles/deviceinfo/deviceinfo.c
 
-if DEPRECATED
-builtin_modules += alert
-builtin_sources += profiles/alert/server.c
-
-builtin_modules += time
-builtin_sources += profiles/time/server.c
-
-builtin_modules += proximity
-builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \
-			profiles/proximity/manager.c \
-			profiles/proximity/monitor.h \
-			profiles/proximity/monitor.c \
-			profiles/proximity/reporter.h \
-			profiles/proximity/reporter.c \
-			profiles/proximity/linkloss.h \
-			profiles/proximity/linkloss.c \
-			profiles/proximity/immalert.h \
-			profiles/proximity/immalert.c
-
-builtin_modules += thermometer
-builtin_sources += profiles/thermometer/thermometer.c
-
-builtin_modules += heartrate
-builtin_sources += profiles/heartrate/heartrate.c
-
-builtin_modules += cyclingspeed
-builtin_sources += profiles/cyclingspeed/cyclingspeed.c
-endif
-
 if SIXAXIS
 plugin_LTLIBRARIES += plugins/sixaxis.la
 plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
diff --git a/Makefile.tools b/Makefile.tools
index af709b8..7ed442c 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -5,6 +5,8 @@
 client_bluetoothctl_SOURCES = client/main.c \
 					client/display.h client/display.c \
 					client/agent.h client/agent.c \
+					client/advertising.h \
+					client/advertising.c \
 					client/gatt.h client/gatt.c \
 					monitor/uuid.h monitor/uuid.c
 client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
diff --git a/client/advertising.c b/client/advertising.c
new file mode 100644
index 0000000..62201d5
--- /dev/null
+++ b/client/advertising.c
@@ -0,0 +1,452 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2016  Intel Corporation. All rights reserved.
+ *
+ *
+ *  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
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <readline/readline.h>
+#include <wordexp.h>
+
+#include "gdbus/gdbus.h"
+#include "display.h"
+#include "advertising.h"
+
+#define AD_PATH "/org/bluez/advertising"
+#define AD_IFACE "org.bluez.LEAdvertisement1"
+
+static gboolean registered = FALSE;
+static char *ad_type = NULL;
+static char **ad_uuids = NULL;
+static size_t ad_uuids_len = 0;
+static char *ad_service_uuid = NULL;
+static uint8_t ad_service_data[25];
+static uint8_t ad_service_data_len = 0;
+static uint16_t ad_manufacturer_id;
+static uint8_t ad_manufacturer_data[25];
+static uint8_t ad_manufacturer_data_len = 0;
+static gboolean ad_tx_power = FALSE;
+
+static void ad_release(DBusConnection *conn)
+{
+	registered = FALSE;
+
+	g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE);
+}
+
+static DBusMessage *release_advertising(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	rl_printf("Advertising released\n");
+
+	ad_release(conn);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable ad_methods[] = {
+	{ GDBUS_METHOD("Release", NULL, NULL, release_advertising) },
+	{ }
+};
+
+static void register_setup(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+	const char *path = AD_PATH;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+	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 register_reply(DBusMessage *message, void *user_data)
+{
+	DBusConnection *conn = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == FALSE) {
+		registered = TRUE;
+		rl_printf("Advertising object registered\n");
+	} else {
+		rl_printf("Failed to register advertisement: %s\n", error.name);
+		dbus_error_free(&error);
+
+		if (g_dbus_unregister_interface(conn, AD_PATH,
+						AD_IFACE) == FALSE)
+			rl_printf("Failed to unregister advertising object\n");
+	}
+}
+
+static gboolean get_type(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	const char *type = "peripheral";
+
+	if (!ad_type || strlen(ad_type) > 0)
+		type = ad_type;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
+
+	return TRUE;
+}
+
+static gboolean uuids_exists(const GDBusPropertyTable *property, void *data)
+{
+	return ad_uuids_len != 0;
+}
+
+static gboolean get_uuids(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter array;
+	size_t i;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
+
+	for (i = 0; i < ad_uuids_len; i++)
+		dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+							&ad_uuids[i]);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+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_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);
+}
+
+static gboolean service_data_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	return ad_service_uuid != NULL;
+}
+
+static gboolean get_service_data(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+	const uint8_t *data = ad_service_data;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+	dict_append_array(&dict, ad_service_uuid, DBUS_TYPE_BYTE, &data,
+							ad_service_data_len);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return TRUE;
+}
+
+static gboolean manufacturer_data_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	return ad_manufacturer_id != 0;
+}
+
+static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+	const uint8_t *data = ad_manufacturer_data;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{qv}", &dict);
+
+	dict_append_basic_array(&dict, DBUS_TYPE_UINT16, &ad_manufacturer_id,
+					DBUS_TYPE_BYTE, &data,
+					ad_manufacturer_data_len);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return TRUE;
+}
+
+static gboolean tx_power_exists(const GDBusPropertyTable *property, void *data)
+{
+	return ad_tx_power;
+}
+
+static gboolean get_tx_power(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &ad_tx_power);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable ad_props[] = {
+	{ "Type", "s", get_type },
+	{ "ServiceUUIDs", "as", get_uuids, NULL, uuids_exists },
+	{ "ServiceData", "a{sv}", get_service_data, NULL, service_data_exists },
+	{ "ManufacturerData", "a{qv}", get_manufacturer_data, NULL,
+						manufacturer_data_exists },
+	{ "IncludeTxPower", "b", get_tx_power, NULL, tx_power_exists },
+	{ }
+};
+
+void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type)
+{
+	if (registered == TRUE) {
+		rl_printf("Advertisement is already registered\n");
+		return;
+	}
+
+	ad_type = g_strdup(type);
+
+	if (g_dbus_register_interface(conn, AD_PATH, AD_IFACE, ad_methods,
+					NULL, ad_props, NULL, NULL) == FALSE) {
+		rl_printf("Failed to register advertising object\n");
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(manager, "RegisterAdvertisement",
+					register_setup, register_reply,
+					conn, NULL) == FALSE) {
+		rl_printf("Failed to register advertising\n");
+		return;
+	}
+}
+
+static void unregister_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *path = AD_PATH;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void unregister_reply(DBusMessage *message, void *user_data)
+{
+	DBusConnection *conn = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == FALSE) {
+		registered = FALSE;
+		rl_printf("Advertising object unregistered\n");
+		if (g_dbus_unregister_interface(conn, AD_PATH,
+							AD_IFACE) == FALSE)
+			rl_printf("Failed to unregister advertising object\n");
+	} else {
+		rl_printf("Failed to unregister advertisement: %s\n",
+								error.name);
+		dbus_error_free(&error);
+	}
+}
+
+void ad_unregister(DBusConnection *conn, GDBusProxy *manager)
+{
+	if (!manager)
+		ad_release(conn);
+
+	if (g_dbus_proxy_method_call(manager, "UnregisterAdvertisement",
+					unregister_setup, unregister_reply,
+					conn, NULL) == FALSE) {
+		rl_printf("Failed to unregister advertisement method\n");
+		return;
+	}
+}
+
+void ad_advertise_uuids(const char *arg)
+{
+	g_strfreev(ad_uuids);
+	ad_uuids = NULL;
+	ad_uuids_len = 0;
+
+	if (!arg || !strlen(arg))
+		return;
+
+	ad_uuids = g_strsplit(arg, " ", -1);
+	if (!ad_uuids) {
+		rl_printf("Failed to parse input\n");
+		return;
+	}
+
+	ad_uuids_len = g_strv_length(ad_uuids);
+}
+
+static void ad_clear_service(void)
+{
+	g_free(ad_service_uuid);
+	ad_service_uuid = NULL;
+	memset(ad_service_data, 0, sizeof(ad_service_data));
+	ad_service_data_len = 0;
+}
+
+void ad_advertise_service(const char *arg)
+{
+	wordexp_t w;
+	unsigned int i;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	ad_clear_service();
+
+	if (w.we_wordc == 0)
+		goto done;
+
+	ad_service_uuid = g_strdup(w.we_wordv[0]);
+
+	for (i = 1; i < w.we_wordc; i++) {
+		long int val;
+		char *endptr = NULL;
+
+		if (i >= G_N_ELEMENTS(ad_service_data)) {
+			rl_printf("Too much data\n");
+			goto done;
+		}
+
+		val = strtol(w.we_wordv[i], &endptr, 0);
+		if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+			rl_printf("Invalid value at index %d\n", i);
+			ad_clear_service();
+			goto done;
+		}
+
+		ad_service_data[ad_service_data_len] = val;
+		ad_service_data_len++;
+	}
+
+done:
+	wordfree(&w);
+}
+
+static void ad_clear_manufacturer(void)
+{
+	ad_manufacturer_id = 0;
+	memset(ad_manufacturer_data, 0, sizeof(ad_manufacturer_data));
+	ad_manufacturer_data_len = 0;
+}
+
+void ad_advertise_manufacturer(const char *arg)
+{
+	wordexp_t w;
+	unsigned int i;
+	char *endptr = NULL;
+	long int val;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	ad_clear_manufacturer();
+
+	if (w.we_wordc == 0)
+		goto done;
+
+	val = strtol(w.we_wordv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || val > UINT16_MAX) {
+		rl_printf("Invalid manufacture id\n");
+		goto done;
+	}
+
+	ad_manufacturer_id = val;
+
+	for (i = 1; i < w.we_wordc; i++) {
+		if (i >= G_N_ELEMENTS(ad_service_data)) {
+			rl_printf("Too much data\n");
+			goto done;
+		}
+
+		val = strtol(w.we_wordv[i], &endptr, 0);
+		if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+			rl_printf("Invalid value at index %d\n", i);
+			ad_clear_service();
+			goto done;
+		}
+
+		ad_manufacturer_data[ad_manufacturer_data_len] = val;
+		ad_manufacturer_data_len++;
+	}
+
+done:
+	wordfree(&w);
+}
+
+
+void ad_advertise_tx_power(gboolean value)
+{
+	ad_tx_power = value;
+}
diff --git a/src/attio.h b/client/advertising.h
similarity index 62%
rename from src/attio.h
rename to client/advertising.h
index 16e2873..8638465 100644
--- a/src/attio.h
+++ b/client/advertising.h
@@ -2,8 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011  Nokia Corporation
- *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2016  Intel Corporation. All rights reserved.
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,12 +21,10 @@
  *
  */
 
-typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data);
-typedef void (*attio_disconnect_cb) (gpointer user_data);
+void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type);
+void ad_unregister(DBusConnection *conn, GDBusProxy *manager);
 
-guint btd_device_add_attio_callback(struct btd_device *device,
-						attio_connect_cb cfunc,
-						attio_disconnect_cb dcfunc,
-						gpointer user_data);
-
-gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id);
+void ad_advertise_uuids(const char *arg);
+void ad_advertise_service(const char *arg);
+void ad_advertise_manufacturer(const char *arg);
+void ad_advertise_tx_power(gboolean value);
diff --git a/client/gatt.c b/client/gatt.c
index fee1cf9..37f222d 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -73,15 +73,21 @@
 
 	text = uuidstr_to_str(uuid);
 	if (!text)
-		text = uuid;
-
-	rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
-				description ? "[" : "",
-				description ? : "",
-				description ? "] " : "",
-				primary ? "Primary" : "Secondary",
-				g_dbus_proxy_get_path(proxy),
-				text);
+		rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					primary ? "Primary" : "Secondary",
+					g_dbus_proxy_get_path(proxy),
+					uuid);
+	else
+		rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					primary ? "Primary" : "Secondary",
+					g_dbus_proxy_get_path(proxy),
+					uuid, text);
 }
 
 void gatt_add_service(GDBusProxy *proxy)
@@ -116,14 +122,19 @@
 
 	text = uuidstr_to_str(uuid);
 	if (!text)
-		text = uuid;
-
-	rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
-				description ? "[" : "",
-				description ? : "",
-				description ? "] " : "",
-				g_dbus_proxy_get_path(proxy),
-				text);
+		rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					g_dbus_proxy_get_path(proxy),
+					uuid);
+	else
+		rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n\t%s\n",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					g_dbus_proxy_get_path(proxy),
+					uuid, text);
 }
 
 static gboolean characteristic_is_child(GDBusProxy *characteristic)
@@ -184,14 +195,19 @@
 
 	text = uuidstr_to_str(uuid);
 	if (!text)
-		text = uuid;
-
-	rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
-				description ? "[" : "",
-				description ? : "",
-				description ? "] " : "",
-				g_dbus_proxy_get_path(proxy),
-				text);
+		rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					g_dbus_proxy_get_path(proxy),
+					uuid);
+	else
+		rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n\t%s\n",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					g_dbus_proxy_get_path(proxy),
+					uuid, text);
 }
 
 static gboolean descriptor_is_child(GDBusProxy *characteristic)
diff --git a/client/main.c b/client/main.c
index 32341ad..e1198a8 100644
--- a/client/main.c
+++ b/client/main.c
@@ -38,11 +38,13 @@
 #include <readline/history.h>
 #include <glib.h>
 
+#include "src/shared/util.h"
 #include "gdbus/gdbus.h"
 #include "monitor/uuid.h"
 #include "agent.h"
 #include "display.h"
 #include "gatt.h"
+#include "advertising.h"
 
 /* String display constants */
 #define COLORED_NEW	COLOR_GREEN "NEW" COLOR_OFF
@@ -58,11 +60,16 @@
 static GDBusProxy *agent_manager;
 static char *auto_register_agent = NULL;
 
-static GDBusProxy *default_ctrl;
+struct adapter {
+	GDBusProxy *proxy;
+	GList *devices;
+};
+
+static struct adapter *default_ctrl;
 static GDBusProxy *default_dev;
 static GDBusProxy *default_attr;
+static GDBusProxy *ad_manager;
 static GList *ctrl_list;
-static GList *dev_list;
 
 static guint input = 0;
 
@@ -77,6 +84,14 @@
 	NULL
 };
 
+static const char * const ad_arguments[] = {
+	"on",
+	"off",
+	"peripheral",
+	"broadcast",
+	NULL
+};
+
 static void proxy_leak(gpointer data)
 {
 	printf("Leaking proxy %p\n", data);
@@ -120,9 +135,6 @@
 	printf("\r");
 	rl_on_new_line();
 	rl_redisplay();
-
-	if (!input)
-		input = setup_standard_input();
 }
 
 static void disconnect_handler(DBusConnection *connection, void *user_data)
@@ -137,13 +149,10 @@
 	rl_on_new_line();
 	rl_redisplay();
 
-	g_list_free(ctrl_list);
+	g_list_free_full(ctrl_list, proxy_leak);
 	ctrl_list = NULL;
 
 	default_ctrl = NULL;
-
-	g_list_free(dev_list);
-	dev_list = NULL;
 }
 
 static void print_adapter(GDBusProxy *proxy, const char *description)
@@ -166,7 +175,9 @@
 				description ? : "",
 				description ? "] " : "",
 				address, name,
-				default_ctrl == proxy ? "[default]" : "");
+				default_ctrl &&
+				default_ctrl->proxy == proxy ?
+				"[default]" : "");
 
 }
 
@@ -349,8 +360,11 @@
 
 	dbus_message_iter_get_basic(&iter, &device);
 
-	for (l = dev_list; l; l = g_list_next(l)) {
-		GDBusProxy *proxy = l->data;
+	if (!default_ctrl)
+		return FALSE;
+
+	for (l = default_ctrl->devices; l; l = g_list_next(l)) {
+		struct GDBusProxy *proxy = l->data;
 
 		path = g_dbus_proxy_get_path(proxy);
 
@@ -361,6 +375,19 @@
 	return FALSE;
 }
 
+static struct adapter *find_parent(GDBusProxy *device)
+{
+	GList *list;
+
+	for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
+		struct adapter *adapter = list->data;
+
+		if (device_is_child(device, adapter->proxy) == TRUE)
+			return adapter;
+	}
+	return NULL;
+}
+
 static void set_default_device(GDBusProxy *proxy, const char *attribute)
 {
 	char *desc = NULL;
@@ -396,9 +423,14 @@
 static void device_added(GDBusProxy *proxy)
 {
 	DBusMessageIter iter;
+	struct adapter *adapter = find_parent(proxy);
 
-	dev_list = g_list_append(dev_list, proxy);
+	if (!adapter) {
+		/* TODO: Error */
+		return;
+	}
 
+	adapter->devices = g_list_append(adapter->devices, proxy);
 	print_device(proxy, COLORED_NEW);
 
 	if (default_dev)
@@ -414,6 +446,19 @@
 	}
 }
 
+static void adapter_added(GDBusProxy *proxy)
+{
+	struct adapter *adapter = g_malloc0(sizeof(struct adapter));
+
+	adapter->proxy = proxy;
+	ctrl_list = g_list_append(ctrl_list, adapter);
+
+	if (!default_ctrl)
+		default_ctrl = adapter;
+
+	print_adapter(proxy, COLORED_NEW);
+}
+
 static void proxy_added(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
@@ -421,16 +466,9 @@
 	interface = g_dbus_proxy_get_interface(proxy);
 
 	if (!strcmp(interface, "org.bluez.Device1")) {
-		if (device_is_child(proxy, default_ctrl) == TRUE)
-			device_added(proxy);
-
+		device_added(proxy);
 	} else if (!strcmp(interface, "org.bluez.Adapter1")) {
-		ctrl_list = g_list_append(ctrl_list, proxy);
-
-		if (!default_ctrl)
-			default_ctrl = proxy;
-
-		print_adapter(proxy, COLORED_NEW);
+		adapter_added(proxy);
 	} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
 		if (!agent_manager) {
 			agent_manager = proxy;
@@ -448,6 +486,8 @@
 		gatt_add_descriptor(proxy);
 	} else if (!strcmp(interface, "org.bluez.GattManager1")) {
 		gatt_add_manager(proxy);
+	} else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+		ad_manager = proxy;
 	}
 }
 
@@ -462,6 +502,46 @@
 	set_default_device(default_dev, path);
 }
 
+static void device_removed(GDBusProxy *proxy)
+{
+	struct adapter *adapter = find_parent(proxy);
+	if (!adapter) {
+		/* TODO: Error */
+		return;
+	}
+
+	adapter->devices = g_list_remove(adapter->devices, proxy);
+
+	print_device(proxy, COLORED_DEL);
+
+	if (default_dev == proxy)
+		set_default_device(NULL, NULL);
+}
+
+static void adapter_removed(GDBusProxy *proxy)
+{
+	GList *ll;
+
+	for (ll = g_list_first(ctrl_list); ll; ll = g_list_next(ll)) {
+		struct adapter *adapter = ll->data;
+
+		if (adapter->proxy == proxy) {
+			print_adapter(proxy, COLORED_DEL);
+
+			if (default_ctrl && default_ctrl->proxy == proxy) {
+				default_ctrl = NULL;
+				set_default_device(NULL, NULL);
+			}
+
+			ctrl_list = g_list_remove_link(ctrl_list, ll);
+			g_list_free(adapter->devices);
+			g_free(adapter);
+			g_list_free(ll);
+			return;
+		}
+	}
+}
+
 static void proxy_removed(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
@@ -469,26 +549,9 @@
 	interface = g_dbus_proxy_get_interface(proxy);
 
 	if (!strcmp(interface, "org.bluez.Device1")) {
-		if (device_is_child(proxy, default_ctrl) == TRUE) {
-			dev_list = g_list_remove(dev_list, proxy);
-
-			print_device(proxy, COLORED_DEL);
-
-			if (default_dev == proxy)
-				set_default_device(NULL, NULL);
-		}
+		device_removed(proxy);
 	} else if (!strcmp(interface, "org.bluez.Adapter1")) {
-		ctrl_list = g_list_remove(ctrl_list, proxy);
-
-		print_adapter(proxy, COLORED_DEL);
-
-		if (default_ctrl == proxy) {
-			default_ctrl = NULL;
-			set_default_device(NULL, NULL);
-
-			g_list_free(dev_list);
-			dev_list = NULL;
-		}
+		adapter_removed(proxy);
 	} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
 		if (agent_manager == proxy) {
 			agent_manager = NULL;
@@ -512,6 +575,11 @@
 			set_default_attribute(NULL);
 	} else if (!strcmp(interface, "org.bluez.GattManager1")) {
 		gatt_remove_manager(proxy);
+	} else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+		if (ad_manager == proxy) {
+			agent_manager = NULL;
+			ad_unregister(dbus_conn, NULL);
+		}
 	}
 }
 
@@ -523,7 +591,8 @@
 	interface = g_dbus_proxy_get_interface(proxy);
 
 	if (!strcmp(interface, "org.bluez.Device1")) {
-		if (device_is_child(proxy, default_ctrl) == TRUE) {
+		if (default_ctrl && device_is_child(proxy,
+					default_ctrl->proxy) == TRUE) {
 			DBusMessageIter addr_iter;
 			char *str;
 
@@ -586,6 +655,28 @@
 					dbus_message_get_member(message));
 }
 
+static struct adapter *find_ctrl_by_address(GList *source, const char *address)
+{
+	GList *list;
+
+	for (list = g_list_first(source); list; list = g_list_next(list)) {
+		struct adapter *adapter = list->data;
+		DBusMessageIter iter;
+		const char *str;
+
+		if (g_dbus_proxy_get_property(adapter->proxy,
+					"Address", &iter) == FALSE)
+			continue;
+
+		dbus_message_iter_get_basic(&iter, &str);
+
+		if (!strcmp(str, address))
+			return adapter;
+	}
+
+	return NULL;
+}
+
 static GDBusProxy *find_proxy_by_address(GList *source, const char *address)
 {
 	GList *list;
@@ -676,13 +767,14 @@
 	GList *list;
 
 	for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
-		GDBusProxy *proxy = list->data;
-		print_adapter(proxy, NULL);
+		struct adapter *adapter = list->data;
+		print_adapter(adapter->proxy, NULL);
 	}
 }
 
 static void cmd_show(const char *arg)
 {
+	struct adapter *adapter;
 	GDBusProxy *proxy;
 	DBusMessageIter iter;
 	const char *address;
@@ -691,13 +783,14 @@
 		if (check_default_ctrl() == FALSE)
 			return;
 
-		proxy = default_ctrl;
+		proxy = default_ctrl->proxy;
 	} else {
-		proxy = find_proxy_by_address(ctrl_list, arg);
-		if (!proxy) {
+		adapter = find_ctrl_by_address(ctrl_list, arg);
+		if (!adapter) {
 			rl_printf("Controller %s not available\n", arg);
 			return;
 		}
+		proxy = adapter->proxy;
 	}
 
 	if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
@@ -719,45 +812,50 @@
 
 static void cmd_select(const char *arg)
 {
-	GDBusProxy *proxy;
+	struct adapter *adapter;
 
 	if (!arg || !strlen(arg)) {
 		rl_printf("Missing controller address argument\n");
 		return;
 	}
 
-	proxy = find_proxy_by_address(ctrl_list, arg);
-	if (!proxy) {
+	adapter = find_ctrl_by_address(ctrl_list, arg);
+	if (!adapter) {
 		rl_printf("Controller %s not available\n", arg);
 		return;
 	}
 
-	if (default_ctrl == proxy)
+	if (default_ctrl && default_ctrl->proxy == adapter->proxy)
 		return;
 
-	default_ctrl = proxy;
-	print_adapter(proxy, NULL);
-
-	g_list_free(dev_list);
-	dev_list = NULL;
+	default_ctrl = adapter;
+	print_adapter(adapter->proxy, NULL);
 }
 
 static void cmd_devices(const char *arg)
 {
-	GList *list;
+	GList *ll;
 
-	for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
-		GDBusProxy *proxy = list->data;
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	for (ll = g_list_first(default_ctrl->devices);
+			ll; ll = g_list_next(ll)) {
+		GDBusProxy *proxy = ll->data;
 		print_device(proxy, NULL);
 	}
 }
 
 static void cmd_paired_devices(const char *arg)
 {
-	GList *list;
+	GList *ll;
 
-	for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
-		GDBusProxy *proxy = list->data;
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	for (ll = g_list_first(default_ctrl->devices);
+			ll; ll = g_list_next(ll)) {
+		GDBusProxy *proxy = ll->data;
 		DBusMessageIter iter;
 		dbus_bool_t paired;
 
@@ -796,7 +894,7 @@
 
 	name = g_strdup(arg);
 
-	if (g_dbus_proxy_set_property_basic(default_ctrl, "Alias",
+	if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias",
 					DBUS_TYPE_STRING, &name,
 					generic_callback, name, g_free) == TRUE)
 		return;
@@ -813,7 +911,7 @@
 
 	name = g_strdup("");
 
-	if (g_dbus_proxy_set_property_basic(default_ctrl, "Alias",
+	if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias",
 					DBUS_TYPE_STRING, &name,
 					generic_callback, name, g_free) == TRUE)
 		return;
@@ -834,7 +932,7 @@
 
 	str = g_strdup_printf("power %s", powered == TRUE ? "on" : "off");
 
-	if (g_dbus_proxy_set_property_basic(default_ctrl, "Powered",
+	if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Powered",
 					DBUS_TYPE_BOOLEAN, &powered,
 					generic_callback, str, g_free) == TRUE)
 		return;
@@ -855,7 +953,7 @@
 
 	str = g_strdup_printf("pairable %s", pairable == TRUE ? "on" : "off");
 
-	if (g_dbus_proxy_set_property_basic(default_ctrl, "Pairable",
+	if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Pairable",
 					DBUS_TYPE_BOOLEAN, &pairable,
 					generic_callback, str, g_free) == TRUE)
 		return;
@@ -877,7 +975,7 @@
 	str = g_strdup_printf("discoverable %s",
 				discoverable == TRUE ? "on" : "off");
 
-	if (g_dbus_proxy_set_property_basic(default_ctrl, "Discoverable",
+	if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Discoverable",
 					DBUS_TYPE_BOOLEAN, &discoverable,
 					generic_callback, str, g_free) == TRUE)
 		return;
@@ -951,7 +1049,7 @@
 	else
 		method = "StopDiscovery";
 
-	if (g_dbus_proxy_method_call(default_ctrl, method,
+	if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
 				NULL, start_discovery_reply,
 				GUINT_TO_POINTER(enable), NULL) == FALSE) {
 		rl_printf("Failed to %s discovery\n",
@@ -1120,7 +1218,7 @@
 	if (check_default_ctrl() == FALSE)
 		return;
 
-	if (g_dbus_proxy_method_call(default_ctrl, "SetDiscoveryFilter",
+	if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
 		set_discovery_filter_setup, set_discovery_filter_reply,
 		&args, NULL) == FALSE) {
 		rl_printf("Failed to set discovery filter\n");
@@ -1209,7 +1307,10 @@
 	g_free(filtered_scan_transport);
 	filtered_scan_transport = NULL;
 
-	if (g_dbus_proxy_method_call(default_ctrl, "SetDiscoveryFilter",
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
 		clear_discovery_filter_setup, set_discovery_filter_reply,
 		NULL, NULL) == FALSE) {
 		rl_printf("Failed to clear discovery filter\n");
@@ -1227,7 +1328,10 @@
 		return NULL;
 	}
 
-	proxy = find_proxy_by_address(dev_list, arg);
+	if (check_default_ctrl() == FALSE)
+		return NULL;
+
+	proxy = find_proxy_by_address(default_ctrl->devices, arg);
 	if (!proxy) {
 		rl_printf("Device %s not available\n", arg);
 		return NULL;
@@ -1418,7 +1522,10 @@
 
 	path = g_strdup(g_dbus_proxy_get_path(proxy));
 
-	if (g_dbus_proxy_method_call(default_ctrl, "RemoveDevice",
+	if (!default_ctrl)
+		return;
+
+	if (g_dbus_proxy_method_call(default_ctrl->proxy, "RemoveDevice",
 						remove_device_setup,
 						remove_device_reply,
 						path, g_free) == FALSE) {
@@ -1442,16 +1549,16 @@
 	if (strcmp(arg, "*") == 0) {
 		GList *list;
 
-		for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
+		for (list = default_ctrl->devices; list;
+						list = g_list_next(list)) {
 			GDBusProxy *proxy = list->data;
 
 			remove_device(proxy);
 		}
-
 		return;
 	}
 
-	proxy = find_proxy_by_address(dev_list, arg);
+	proxy = find_proxy_by_address(default_ctrl->devices, arg);
 	if (!proxy) {
 		rl_printf("Device %s not available\n", arg);
 		return;
@@ -1487,7 +1594,10 @@
 		return;
 	}
 
-	proxy = find_proxy_by_address(dev_list, arg);
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	proxy = find_proxy_by_address(default_ctrl->devices, arg);
 	if (!proxy) {
 		rl_printf("Device %s not available\n", arg);
 		return;
@@ -1556,6 +1666,30 @@
 	gatt_list_attributes(g_dbus_proxy_get_path(proxy));
 }
 
+static void cmd_set_alias(const char *arg)
+{
+	char *name;
+
+	if (!arg || !strlen(arg)) {
+		rl_printf("Missing name argument\n");
+		return;
+	}
+
+	if (!default_dev) {
+		rl_printf("No device connected\n");
+		return;
+	}
+
+	name = g_strdup(arg);
+
+	if (g_dbus_proxy_set_property_basic(default_dev, "Alias",
+					DBUS_TYPE_STRING, &name,
+					generic_callback, name, g_free) == TRUE)
+		return;
+
+	g_free(name);
+}
+
 static void cmd_select_attribute(const char *arg)
 {
 	GDBusProxy *proxy;
@@ -1697,7 +1831,7 @@
 		return;
 	}
 
-	gatt_register_profile(dbus_conn, default_ctrl, &w);
+	gatt_register_profile(dbus_conn, default_ctrl->proxy, &w);
 
 	wordfree(&w);
 }
@@ -1707,7 +1841,7 @@
 	if (check_default_ctrl() == FALSE)
 		return;
 
-	gatt_unregister_profile(dbus_conn, default_ctrl);
+	gatt_unregister_profile(dbus_conn, default_ctrl->proxy);
 }
 
 static void cmd_version(const char *arg)
@@ -1753,12 +1887,40 @@
 
 static char *ctrl_generator(const char *text, int state)
 {
-	return generic_generator(text, state, ctrl_list, "Address");
+	static int index = 0;
+	static int len = 0;
+	GList *list;
+
+	if (!state) {
+		index = 0;
+		len = strlen(text);
+	}
+
+	for (list = g_list_nth(ctrl_list, index); list;
+						list = g_list_next(list)) {
+		struct adapter *adapter = list->data;
+		DBusMessageIter iter;
+		const char *str;
+
+		index++;
+
+		if (g_dbus_proxy_get_property(adapter->proxy,
+					"Address", &iter) == FALSE)
+			continue;
+
+		dbus_message_iter_get_basic(&iter, &str);
+
+		if (!strncmp(str, text, len))
+			return strdup(str);
+	}
+
+	return NULL;
 }
 
 static char *dev_generator(const char *text, int state)
 {
-	return generic_generator(text, state, dev_list, "Address");
+	return generic_generator(text, state,
+			default_ctrl ? default_ctrl->devices : NULL, "Address");
 }
 
 static char *attribute_generator(const char *text, int state)
@@ -1786,6 +1948,113 @@
 	return NULL;
 }
 
+static gboolean parse_argument_advertise(const char *arg, dbus_bool_t *value,
+							const char **type)
+{
+	const char * const *opt;
+
+	if (arg == NULL || strlen(arg) == 0) {
+		rl_printf("Missing on/off/type argument\n");
+		return FALSE;
+	}
+
+	if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+		*value = TRUE;
+		*type = "";
+		return TRUE;
+	}
+
+	if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+		*value = FALSE;
+		return TRUE;
+	}
+
+	for (opt = ad_arguments; *opt; opt++) {
+		if (strcmp(arg, *opt) == 0) {
+			*value = TRUE;
+			*type = *opt;
+			return TRUE;
+		}
+	}
+
+	rl_printf("Invalid argument %s\n", arg);
+	return FALSE;
+}
+
+static void cmd_advertise(const char *arg)
+{
+	dbus_bool_t enable;
+	const char *type;
+
+	if (parse_argument_advertise(arg, &enable, &type) == FALSE)
+		return;
+
+	if (!ad_manager) {
+		rl_printf("LEAdvertisingManager not found\n");
+		return;
+	}
+
+	if (enable == TRUE)
+		ad_register(dbus_conn, ad_manager, type);
+	else
+		ad_unregister(dbus_conn, ad_manager);
+}
+
+static char *ad_generator(const char *text, int state)
+{
+	static int index, len;
+	const char *arg;
+
+	if (!state) {
+		index = 0;
+		len = strlen(text);
+	}
+
+	while ((arg = ad_arguments[index])) {
+		index++;
+
+		if (!strncmp(arg, text, len))
+			return strdup(arg);
+	}
+
+	return NULL;
+}
+
+static void cmd_set_advertise_uuids(const char *arg)
+{
+	ad_advertise_uuids(arg);
+}
+
+static void cmd_set_advertise_service(const char *arg)
+{
+	ad_advertise_service(arg);
+}
+
+static void cmd_set_advertise_manufacturer(const char *arg)
+{
+	ad_advertise_manufacturer(arg);
+}
+
+static void cmd_set_advertise_tx_power(const char *arg)
+{
+	if (arg == NULL || strlen(arg) == 0) {
+		rl_printf("Missing on/off argument\n");
+		return;
+	}
+
+	if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+		ad_advertise_tx_power(TRUE);
+		return;
+	}
+
+	if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+		ad_advertise_tx_power(FALSE);
+		return;
+	}
+
+	rl_printf("Invalid argument\n");
+}
+
 static const struct {
 	const char *cmd;
 	const char *arg;
@@ -1814,6 +2083,20 @@
 							capability_generator},
 	{ "default-agent",NULL,       cmd_default_agent,
 				"Set agent as the default one" },
+	{ "advertise",    "<on/off/type>", cmd_advertise,
+				"Enable/disable advertising with given type",
+							ad_generator},
+	{ "set-advertise-uuids", "[uuid1 uuid2 ...]",
+			cmd_set_advertise_uuids, "Set advertise uuids" },
+	{ "set-advertise-service", "[uuid][data=[xx xx ...]",
+			cmd_set_advertise_service,
+			"Set advertise service data" },
+	{ "set-advertise-manufacturer", "[id][data=[xx xx ...]",
+			cmd_set_advertise_manufacturer,
+			"Set advertise manufacturer data" },
+	{ "set-advertise-tx-power", "<on/off>",
+			cmd_set_advertise_tx_power,
+			"Enable/disable TX power to be advertised" },
 	{ "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
 			cmd_set_scan_filter_uuids, "Set scan filter uuids" },
 	{ "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
@@ -1846,6 +2129,7 @@
 							dev_generator },
 	{ "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
 							dev_generator },
+	{ "set-alias",    "<alias>",  cmd_set_alias, "Set device alias" },
 	{ "select-attribute", "<attribute>",  cmd_select_attribute,
 				"Select attribute", attribute_generator },
 	{ "attribute-info", "[attribute]",  cmd_attribute_info,
@@ -2158,7 +2442,6 @@
 	g_main_loop_unref(main_loop);
 
 	g_list_free_full(ctrl_list, proxy_leak);
-	g_list_free_full(dev_list, proxy_leak);
 
 	g_free(auto_register_agent);
 
diff --git a/configure.ac b/configure.ac
index 2f459cc..530d512 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.41)
+AC_INIT(bluez, 5.43)
 
 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 97462a3..08de6cd 100644
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -45,7 +45,7 @@
 			Possible errors: org.bluez.Error.InvalidArguments
 					 org.bluez.Error.Failed
 
-		void SetDiscoveryFilter(dict filter) [Experimental]
+		void SetDiscoveryFilter(dict filter)
 
 			This method sets the device discovery filter for the
 			caller. When this method is called with no filter
diff --git a/doc/device-api.txt b/doc/device-api.txt
index 96832e8..13b2881 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -21,6 +21,20 @@
 			If at least one profile was connected successfully this
 			method will indicate success.
 
+			For dual-mode devices only one bearer is connected at
+			time, the conditions are in the following order:
+
+				1. Connect the disconnected bearer if already
+				connected.
+
+				2. Connect first the bonded bearer. If no
+				bearers are bonded or both are skip and check
+				latest seen bearer.
+
+				3. Connect last seen bearer, in case the
+				timestamps are the same BR/EDR takes
+				precedence.
+
 			Possible errors: org.bluez.Error.NotReady
 					 org.bluez.Error.Failed
 					 org.bluez.Error.InProgress
@@ -196,7 +210,7 @@
 			Received Signal Strength Indicator of the remote
 			device (inquiry or advertising).
 
-		int16 TxPower [readonly, optional, experimental]
+		int16 TxPower [readonly, optional]
 
 			Advertised transmitted power level (inquiry or
 			advertising).
@@ -212,7 +226,11 @@
 			Service advertisement data. Keys are the UUIDs in
 			string format followed by its byte array value.
 
-		bool ServicesResolved [readonly, experimental]
+		bool ServicesResolved [readonly]
 
 			Indicate whether or not service discovery has been
 			resolved.
+
+		array{byte} AdvertisingFlags [readonly, experimental]
+
+			The Advertising Data Flags of the remote device.
diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 4992243..6c98b87 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -27,7 +27,7 @@
 properties defined in GattService1 interface.
 
 Service		org.bluez
-Interface	org.bluez.GattService1 [Experimental]
+Interface	org.bluez.GattService1
 Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX
 
 Properties	string UUID [read-only]
@@ -58,7 +58,7 @@
 path hierarchy and are freely definable.
 
 Service		org.bluez
-Interface	org.bluez.GattCharacteristic1 [Experimental]
+Interface	org.bluez.GattCharacteristic1
 Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY
 
 Methods		array{byte} ReadValue(dict options)
@@ -115,7 +115,7 @@
 
 		object Service [read-only]
 
-			Object path of the GATT service the characteristc
+			Object path of the GATT service the characteristic
 			belongs to.
 
 		array{byte} Value [read-only, optional]
@@ -159,7 +159,7 @@
 Local or remote GATT characteristic descriptors hierarchy.
 
 Service		org.bluez
-Interface	org.bluez.GattDescriptor1 [Experimental]
+Interface	org.bluez.GattDescriptor1
 Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ
 
 Methods		array{byte} ReadValue(dict flags)
@@ -198,7 +198,7 @@
 
 		object Characteristic [read-only]
 
-			Object path of the GATT characteristc the descriptor
+			Object path of the GATT characteristic the descriptor
 			belongs to.
 
 		array{byte} Value [read-only, optional]
@@ -222,7 +222,7 @@
 				"secure-read" (Server Only)
 				"secure-write" (Server Only)
 
-GATT Profile hierarcy
+GATT Profile hierarchy
 =====================
 
 Local profile (GATT client) instance. By registering this type of object
@@ -231,7 +231,7 @@
 supporting it.
 
 Service		<application dependent>
-Interface	org.bluez.GattProfile1 [Experimental]
+Interface	org.bluez.GattProfile1
 Object path	<application dependent>
 
 Methods		void Release()
@@ -307,8 +307,17 @@
 all of its registered services will be automatically unregistered.
 InterfacesAdded signals will be ignored.
 
+Examples:
+	- Client
+		test/example-gatt-client
+		client/bluetoothctl
+	- Server
+		test/example-gatt-server
+		tools/gatt-service
+
+
 Service		org.bluez
-Interface	org.bluez.GattManager1 [Experimental]
+Interface	org.bluez.GattManager1
 Object path	[variable prefix]/{hci0,hci1,...}
 
 Methods		void RegisterApplication(object application, dict options)
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index 2bc2a88..34270dd 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -27,7 +27,8 @@
 Linux kernel v4.2	Version 1.10
 Linux kernel v4.5	Version 1.11
 Linux kernel v4.6	Version 1.12
-Linux kernel v4.8	Version 1.13 (not yet released)
+Linux kernel v4.8	Version 1.13
+Linux kernel v4.9	Version 1.14 (not yet released)
 
 Version 1.1 introduces Set Device ID command.
 
@@ -71,7 +72,8 @@
 Version 1.10 does not introduce any new command or event. It extends the
 advertising feature to support 5 parallel advertising instances.
 
-Version 1.11 introduces Get Advertising Size Information command.
+Version 1.11 introduces Get Advertising Size Information and Start Limited
+Discovery commands.
 
 Version 1.12 introduces a new limited privacy mode (value 0x02 passed to
 the Set Privacy command).
@@ -79,6 +81,11 @@
 Version 1.13 introduces a new authentication failure reason code for the
 Device Disconnected event.
 
+Version 1.14 introduces Read Extended Controller Information command and
+Extended Controller Information Changed event. It also adds Set Appearance
+command. The advertising flags Appearance and Local Name for adding scan
+response information are now supported.
+
 
 Example
 =======
@@ -2264,7 +2271,7 @@
 	Controller Index:	<controller id>
 	Command Parameters:	Address_Type (1 Octet)
 				RSSI_Threshold (1 Octet)
-				UUID_Count (1 Octet)
+				UUID_Count (2 Octets)
 				UUID[i] (16 Octets)
 	Return Parameters:	Address_Type (1 Octet)
 
@@ -2462,6 +2469,7 @@
 		0x06	SDIO
 		0x07	SPI
 		0x08	I2C
+		0x09	SMD
 
 	Controllers marked as RAW only operation are currently not listed
 	by this command.
@@ -2810,6 +2818,105 @@
 				Invalid Index
 
 
+Read Extended Controller Information Command
+============================================
+
+	Command Code:		0x0042
+	Controller Index:	<controller id>
+	Command Parameters:
+	Return Parameters:	Address (6 Octets)
+				Bluetooth_Version (1 Octet)
+				Manufacturer (2 Octets)
+				Supported_Settings (4 Octets)
+				Current_Settings (4 Octets)
+				EIR_Data_Length (2 Octets)
+				EIR_Data (0-65535 Octets)
+
+	This command is used to retrieve the current state and basic
+	information of a controller. It is typically used right after
+	getting the response to the Read Controller Index List command
+	or an Index Added event (or its extended counterparts).
+
+	The Address parameter describes the controllers public address
+	and it can be expected that it is set. However in case of single
+	mode Low Energy only controllers it can be 00:00:00:00:00:00. To
+	power on the controller in this case, it is required to configure
+	a static address using Set Static Address command first.
+
+	If the public address is set, then it will be used as identity
+	address for the controller. If no public address is available,
+	then the configured static address will be used as identity
+	address.
+
+	In the case of a dual-mode controller with public address that
+	is configured as Low Energy only device (BR/EDR switched off),
+	the static address is used when set and public address otherwise.
+
+	Current_Settings and Supported_Settings is a bitmask with
+	currently the following available bits:
+
+		0	Powered
+		1	Connectable
+		2	Fast Connectable
+		3	Discoverable
+		4	Bondable
+		5	Link Level Security (Sec. mode 3)
+		6	Secure Simple Pairing
+		7	Basic Rate/Enhanced Data Rate
+		8	High Speed
+		9	Low Energy
+		10	Advertising
+		11	Secure Connections
+		12	Debug Keys
+		13	Privacy
+		14	Controller Configuration
+		15	Static Address
+
+	The EIR_Data field contains information about class of device,
+	local name and other values. Not all of them might be present. For
+	example a Low Energy only device does not contain class of device
+	information.
+
+	When any of the values in the EIR_Data field changes, the event
+	Extended Controller Information Changed will be used to inform
+	clients about the updated information.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Set Appearance Command
+======================
+
+	Command Code:		0x0042
+	Controller Index:	<controller id>
+	Command Parameters:	Appearance (2 Octets)
+	Return Parameters:
+
+	This command is used to set the appearance value of a controller.
+
+	This command can be used when the controller is not
+	powered and all settings will be programmed once powered.
+
+	The value of appearance will be remembered when switching
+	the controller off and back on again. So the appearance only
+	have to be set once when a new controller is found and will
+	stay until removed.
+
+	This command generates a Command Complete event on success
+	or a Command Status event on failure.
+
+	This command is only available for LE capable controllers.
+	It will return Not Supported otherwise.
+
+	Possible errors:	Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
 Command Complete Event
 ======================
 
@@ -3655,3 +3762,23 @@
 
 	The event will only be sent to management sockets other than the
 	one through which the command was sent.
+
+
+Extended Controller Information Changed Event
+=============================================
+
+	Event Code:		0x0025
+	Controller Index:	<controller id>
+	Event Parameters:	EIR_Data_Length (2 Octets)
+				EIR_Data (0-65535 Octets)
+
+	This event indicates that controller information has been updated
+	and new values are used. This includes the local name, class of
+	device, device id and LE address information.
+
+	This event will only be used after Read Extended Controller
+	Information command has been used at least once. If it has not
+	been used the legacy events are used.
+
+	The event will only be sent to management sockets other than the
+	one through which the change was triggered.
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index 2c34ec4..6a708b4 100644
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -89,6 +89,18 @@
   DiscoverableTimeout=0
 
 
+Identity file format
+====================
+Identity file contains one [General] group that holds identity information
+such as keys and adresses:
+
+	IdentityResolvingKey	String	128-bit value of the IRK
+
+Sample:
+  [General]
+  IdentityResolvingKey=00112233445566778899aabbccddeeff
+
+
 Attributes file format
 ======================
 
diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt
index 54e5fe4..741492a 100644
--- a/doc/test-coverage.txt
+++ b/doc/test-coverage.txt
@@ -39,7 +39,7 @@
 
 Application		Count	Description
 -------------------------------------------
-mgmt-tester		 307	Kernel management interface testing
+mgmt-tester		 331	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
 			-----
-			 384
+			 408
 
 
 Android end-to-end testing
diff --git a/emulator/hciemu.c b/emulator/hciemu.c
index ea1e3cf..7debb8f 100644
--- a/emulator/hciemu.c
+++ b/emulator/hciemu.c
@@ -467,6 +467,16 @@
 	return true;
 }
 
+bool hciemu_clear_master_post_command_hooks(struct hciemu *hciemu)
+{
+	if (!hciemu)
+		return false;
+
+	queue_remove_all(hciemu->post_command_hooks,
+					NULL, NULL, destroy_command_hook);
+	return true;
+}
+
 int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
 				uint16_t opcode, hciemu_hook_func_t function,
 				void *user_data)
diff --git a/emulator/hciemu.h b/emulator/hciemu.h
index c5578d1..783f99c 100644
--- a/emulator/hciemu.h
+++ b/emulator/hciemu.h
@@ -66,6 +66,8 @@
 bool hciemu_add_master_post_command_hook(struct hciemu *hciemu,
 			hciemu_command_func_t function, void *user_data);
 
+bool hciemu_clear_master_post_command_hooks(struct hciemu *hciemu);
+
 int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
 				uint16_t opcode, hciemu_hook_func_t function,
 				void *user_data);
diff --git a/emulator/smp.c b/emulator/smp.c
index 40836cf..c30de36 100644
--- a/emulator/smp.c
+++ b/emulator/smp.c
@@ -370,7 +370,7 @@
 				conn->ra_type, conn->ra, confirm))
 		return false;
 
-	if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf) != 0)) {
+	if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
 		printf("Confirmation values don't match\n");
 		return false;
 	}
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 3726b50..3276b68 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -540,7 +540,7 @@
 	case 138:
 		return "Jawbone";
 	case 139:
-		return "Topcorn Positioning Systems, LLC";
+		return "Topcon Positioning Systems, LLC";
 	case 140:
 		return "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)";
 	case 141:
@@ -1078,7 +1078,7 @@
 	case 407:
 		return "WiSilica Inc";
 	case 408:
-		return "Vengit Limited";
+		return "VENGIT Korlátolt Felelősségű Társaság";
 	case 409:
 		return "SALTO SYSTEMS S.L.";
 	case 410:
@@ -2143,6 +2143,200 @@
 		return "Meizu Technology Co., Ltd.";
 	case 940:
 		return "Smablo LTD";
+	case 941:
+		return "XiQ";
+	case 942:
+		return "Allswell Inc.";
+	case 943:
+		return "Comm-N-Sense Corp DBA Verigo";
+	case 944:
+		return "VIBRADORM GmbH";
+	case 945:
+		return "Otodata Wireless Network Inc.";
+	case 946:
+		return "Propagation Systems Limited";
+	case 947:
+		return "Midwest Instruments & Controls";
+	case 948:
+		return "Alpha Nodus, inc.";
+	case 949:
+		return "petPOMM, Inc";
+	case 950:
+		return "Mattel";
+	case 951:
+		return "Airbly Inc.";
+	case 952:
+		return "A-Safe Limited";
+	case 953:
+		return "FREDERIQUE CONSTANT SA";
+	case 954:
+		return "Maxscend Microelectronics Company Limited";
+	case 955:
+		return "Abbott Diabetes Care";
+	case 956:
+		return "ASB Bank Ltd";
+	case 957:
+		return "amadas";
+	case 958:
+		return "Applied Science, Inc.";
+	case 959:
+		return "iLumi Solutions Inc.";
+	case 960:
+		return "Arch Systems Inc.";
+	case 961:
+		return "Ember Technologies, Inc.";
+	case 962:
+		return "Snapchat Inc";
+	case 963:
+		return "Casambi Technologies Oy";
+	case 964:
+		return "Pico Technology Inc.";
+	case 965:
+		return "St. Jude Medical, Inc.";
+	case 966:
+		return "Intricon";
+	case 967:
+		return "Structural Health Systems, Inc.";
+	case 968:
+		return "Avvel International";
+	case 969:
+		return "Gallagher Group";
+	case 970:
+		return "In2things Automation Pvt. Ltd.";
+	case 971:
+		return "SYSDEV Srl";
+	case 972:
+		return "Vonkil Technologies Ltd";
+	case 973:
+		return "Wynd Technologies, Inc.";
+	case 974:
+		return "CONTRINEX S.A.";
+	case 975:
+		return "MIRA, Inc.";
+	case 976:
+		return "Watteam Ltd";
+	case 977:
+		return "Density Inc.";
+	case 978:
+		return "IOT Pot India Private Limited";
+	case 979:
+		return "Sigma Connectivity AB";
+	case 980:
+		return "PEG PEREGO SPA";
+	case 981:
+		return "Wyzelink Systems Inc.";
+	case 982:
+		return "Yota Devices LTD";
+	case 983:
+		return "FINSECUR";
+	case 984:
+		return "Zen-Me Labs Ltd";
+	case 985:
+		return "3IWare Co., Ltd.";
+	case 986:
+		return "EnOcean GmbH";
+	case 987:
+		return "Instabeat, Inc";
+	case 988:
+		return "Nima Labs";
+	case 989:
+		return "Andreas Stihl AG & Co. KG";
+	case 990:
+		return "Nathan Rhoades LLC";
+	case 991:
+		return "Grob Technologies, LLC";
+	case 992:
+		return "Actions (Zhuhai) Technology Co., Limited";
+	case 993:
+		return "SPD Development Company Ltd";
+	case 994:
+		return "Sensoan Oy";
+	case 995:
+		return "Qualcomm Life Inc";
+	case 996:
+		return "Chip-ing AG";
+	case 997:
+		return "ffly4u";
+	case 998:
+		return "IoT Instruments Oy";
+	case 999:
+		return "TRUE Fitness Technology";
+	case 1000:
+		return "Reiner Kartengeraete GmbH & Co. KG.";
+	case 1001:
+		return "SHENZHEN LEMONJOY TECHNOLOGY CO., LTD.";
+	case 1002:
+		return "Hello Inc.";
+	case 1003:
+		return "Evollve Inc.";
+	case 1004:
+		return "Jigowatts Inc.";
+	case 1005:
+		return "BASIC MICRO.COM,INC.";
+	case 1006:
+		return "CUBE TECHNOLOGIES";
+	case 1007:
+		return "foolography GmbH";
+	case 1008:
+		return "CLINK";
+	case 1009:
+		return "Hestan Smart Cooking Inc.";
+	case 1010:
+		return "WindowMaster A/S";
+	case 1011:
+		return "Flowscape AB";
+	case 1012:
+		return "PAL Technologies Ltd";
+	case 1013:
+		return "WHERE, Inc.";
+	case 1014:
+		return "Iton Technology Corp.";
+	case 1015:
+		return "Owl Labs Inc.";
+	case 1016:
+		return "Rockford Corp.";
+	case 1017:
+		return "Becon Technologies Co.,Ltd.";
+	case 1018:
+		return "Vyassoft Technologies Inc";
+	case 1019:
+		return "Nox Medical";
+	case 1020:
+		return "Kimberly-Clark";
+	case 1021:
+		return "Trimble Navigation Ltd.";
+	case 1022:
+		return "Littelfuse";
+	case 1023:
+		return "Withings";
+	case 1024:
+		return "i-developer IT Beratung UG";
+	case 1025:
+		return "リレーションズ株式会社";
+	case 1026:
+		return "Sears Holdings Corporation";
+	case 1027:
+		return "Gantner Electronic GmbH";
+	case 1028:
+		return "Authomate Inc";
+	case 1029:
+		return "Vertex International, Inc.";
+	case 1030:
+		return "Airtago";
+	case 1031:
+		return "Swiss Audio SA";
+	case 1032:
+		return "ToGetHome Inc.";
+	case 1033:
+		return "AXIS";
+	case 1034:
+		return "Openmatics";
+	case 1035:
+		return "Jana Care Inc.";
+	case 1036:
+		return "Senix Corporation";
+	case 1037:
+		return "NorthStar Battery Company, LLC";
 	case 65535:
 		return "internal use";
 	default:
diff --git a/lib/hci.c b/lib/hci.c
index 0c85875..5ce39bd 100644
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -160,6 +160,8 @@
 		return "SPI";
 	case HCI_I2C:
 		return "I2C";
+	case HCI_SMD:
+		return "SMD";
 	default:
 		return "Unknown";
 	}
diff --git a/lib/hci.h b/lib/hci.h
index bdd0df0..3ef4311 100644
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -57,6 +57,7 @@
 #define HCI_SDIO	6
 #define HCI_SPI		7
 #define HCI_I2C		8
+#define HCI_SMD		9
 
 /* HCI controller types */
 #define HCI_PRIMARY	0x00
diff --git a/lib/mgmt.h b/lib/mgmt.h
index f6a976a..798a05e 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -530,6 +530,22 @@
 
 #define MGMT_OP_START_LIMITED_DISCOVERY	0x0041
 
+#define MGMT_OP_READ_EXT_INFO		0x0042
+struct mgmt_rp_read_ext_info {
+	bdaddr_t bdaddr;
+	uint8_t version;
+	uint16_t manufacturer;
+	uint32_t supported_settings;
+	uint32_t current_settings;
+	uint16_t eir_len;
+	uint8_t  eir[0];
+} __packed;
+
+#define MGMT_OP_SET_APPEARANCE		0x0043
+struct mgmt_cp_set_appearance {
+	uint16_t appearance;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	uint16_t opcode;
@@ -742,6 +758,12 @@
 	uint8_t instance;
 } __packed;
 
+#define MGMT_EV_EXT_INFO_CHANGED	0x0025
+struct mgmt_ev_ext_info_changed {
+	uint16_t eir_len;
+	uint8_t  eir[0];
+} __packed;
+
 static const char *mgmt_op[] = {
 	"<0x0000>",
 	"Read Version",
@@ -809,6 +831,8 @@
 	"Remove Advertising",
 	"Get Advertising Size Information",		/* 0x0040 */
 	"Start Limited Discovery",
+	"Read Extended Controller Information",
+	"Set Appearance",
 };
 
 static const char *mgmt_ev[] = {
@@ -849,6 +873,7 @@
 	"Local Out Of Band Extended Data Updated",
 	"Advertising Added",
 	"Advertising Removed",
+	"Extended Controller Information Changed",
 };
 
 static const char *mgmt_status[] = {
diff --git a/lib/uuid.c b/lib/uuid.c
index ac071fa..d4c7002 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -280,8 +280,11 @@
 {
 	bt_uuid_t u1, u2;
 
-	bt_string_to_uuid(&u1, a);
-	bt_string_to_uuid(&u2, b);
+	if (bt_string_to_uuid(&u1, a) < 0)
+		return -EINVAL;
+
+	if (bt_string_to_uuid(&u2, b) < 0)
+		return -EINVAL;
 
 	return bt_uuid_cmp(&u1, &u2);
 }
diff --git a/monitor/control.c b/monitor/control.c
index 26aecfd..9bbdc37 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -57,6 +57,7 @@
 
 static struct btsnoop *btsnoop_file = NULL;
 static bool hcidump_fallback = false;
+static bool decode_control = true;
 
 struct control_data {
 	uint16_t channel;
@@ -797,6 +798,9 @@
 
 void control_message(uint16_t opcode, const void *data, uint16_t size)
 {
+	if (!decode_control)
+		return;
+
 	switch (opcode) {
 	case MGMT_EV_INDEX_ADDED:
 		mgmt_index_added(size, data);
@@ -1450,3 +1454,8 @@
 
 	return 0;
 }
+
+void control_disable_decoding(void)
+{
+	decode_control = false;
+}
diff --git a/monitor/control.h b/monitor/control.h
index 55384db..630a852 100644
--- a/monitor/control.h
+++ b/monitor/control.h
@@ -29,5 +29,6 @@
 void control_server(const char *path);
 int control_tty(const char *path, unsigned int speed);
 int control_tracing(void);
+void control_disable_decoding(void);
 
 void control_message(uint16_t opcode, const void *data, uint16_t size);
diff --git a/monitor/display.h b/monitor/display.h
index 36e189a..b85f37b 100644
--- a/monitor/display.h
+++ b/monitor/display.h
@@ -38,6 +38,11 @@
 #define COLOR_WHITE_BG	"\x1B[0;47;30m"
 #define COLOR_HIGHLIGHT	"\x1B[1;39m"
 
+#define COLOR_RED_BOLD		"\x1B[1;31m"
+#define COLOR_GREEN_BOLD	"\x1B[1;32m"
+#define COLOR_BLUE_BOLD		"\x1B[1;34m"
+#define COLOR_MAGENTA_BOLD	"\x1B[1;35m"
+
 #define COLOR_ERROR	"\x1B[1;31m"
 #define COLOR_WARN	"\x1B[1m"
 #define COLOR_INFO	COLOR_OFF
diff --git a/monitor/l2cap.c b/monitor/l2cap.c
index 582c853..ebc90e0 100644
--- a/monitor/l2cap.c
+++ b/monitor/l2cap.c
@@ -1999,7 +1999,7 @@
 static void print_uuid(const char *label, const void *data, uint16_t size)
 {
 	const char *str;
-	char uuidstr[36];
+	char uuidstr[MAX_LEN_UUID_STR];
 
 	switch (size) {
 	case 2:
diff --git a/monitor/packet.c b/monitor/packet.c
index 0947213..45c9e8c 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -57,6 +57,7 @@
 #include "broadcom.h"
 #include "packet.h"
 
+#define COLOR_CHANNEL_LABEL		COLOR_WHITE
 #define COLOR_INDEX_LABEL		COLOR_WHITE
 #define COLOR_TIMESTAMP			COLOR_YELLOW
 
@@ -66,13 +67,12 @@
 #define COLOR_CLOSE_INDEX		COLOR_RED
 #define COLOR_INDEX_INFO		COLOR_GREEN
 #define COLOR_VENDOR_DIAG		COLOR_YELLOW
+#define COLOR_SYSTEM_NOTE		COLOR_OFF
 
 #define COLOR_HCI_COMMAND		COLOR_BLUE
 #define COLOR_HCI_COMMAND_UNKNOWN	COLOR_WHITE_BG
-
 #define COLOR_HCI_EVENT			COLOR_MAGENTA
 #define COLOR_HCI_EVENT_UNKNOWN		COLOR_WHITE_BG
-
 #define COLOR_HCI_ACLDATA		COLOR_CYAN
 #define COLOR_HCI_SCODATA		COLOR_YELLOW
 
@@ -84,6 +84,19 @@
 #define COLOR_UNKNOWN_SERVICE_CLASS	COLOR_WHITE_BG
 #define COLOR_UNKNOWN_PKT_TYPE_BIT	COLOR_WHITE_BG
 
+#define COLOR_CTRL_OPEN			COLOR_GREEN_BOLD
+#define COLOR_CTRL_CLOSE		COLOR_RED_BOLD
+#define COLOR_CTRL_COMMAND		COLOR_BLUE_BOLD
+#define COLOR_CTRL_COMMAND_UNKNOWN	COLOR_WHITE_BG
+#define COLOR_CTRL_EVENT		COLOR_MAGENTA_BOLD
+#define COLOR_CTRL_EVENT_UNKNOWN	COLOR_WHITE_BG
+
+#define COLOR_UNKNOWN_OPTIONS_BIT	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_SETTINGS_BIT	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_ADDRESS_TYPE	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_DEVICE_FLAG	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_ADV_FLAG		COLOR_WHITE_BG
+
 #define COLOR_PHY_PACKET		COLOR_BLUE
 
 static time_t time_offset = ((time_t) -1);
@@ -95,6 +108,71 @@
 
 #define UNKNOWN_MANUFACTURER 0xffff
 
+#define CTRL_RAW  0x0000
+#define CTRL_USER 0x0001
+#define CTRL_MGMT 0x0002
+
+#define MAX_CTRL 64
+
+struct ctrl_data {
+	bool used;
+	uint32_t cookie;
+	uint16_t format;
+	char name[20];
+};
+
+static struct ctrl_data ctrl_list[MAX_CTRL];
+
+static void assign_ctrl(uint32_t cookie, uint16_t format, const char *name)
+{
+	int i;
+
+	for (i = 0; i < MAX_CTRL; i++) {
+		if (!ctrl_list[i].used) {
+			ctrl_list[i].used = true;
+			ctrl_list[i].cookie = cookie;
+			ctrl_list[i].format = format;
+			if (name) {
+				strncpy(ctrl_list[i].name, name, 19);
+				ctrl_list[i].name[19] = '\0';
+			} else
+				strcpy(ctrl_list[i].name, "null");
+			break;
+		}
+	}
+}
+
+static void release_ctrl(uint32_t cookie, uint16_t *format, char *name)
+{
+	int i;
+
+	if (format)
+		*format = 0xffff;
+
+	for (i = 0; i < MAX_CTRL; i++) {
+		if (ctrl_list[i].used && ctrl_list[i].cookie == cookie) {
+			ctrl_list[i].used = false;
+			if (format)
+				*format = ctrl_list[i].format;
+			if (name)
+				strncpy(name, ctrl_list[i].name, 20);
+			break;
+		}
+	}
+}
+
+static uint16_t get_format(uint32_t cookie)
+{
+	int i;
+
+	for (i = 0; i < MAX_CTRL; i++) {
+		if (ctrl_list[i].used && ctrl_list[i].cookie == cookie)
+			return ctrl_list[i].format;
+	}
+
+	return 0xffff;
+}
+
 #define MAX_CONN 16
 
 struct conn_data {
@@ -181,15 +259,29 @@
 
 #define print_space(x) printf("%*c", (x), ' ');
 
-static void print_packet(struct timeval *tv, struct ucred *cred,
-					uint16_t index, char ident,
+static void print_packet(struct timeval *tv, struct ucred *cred, char ident,
+					uint16_t index, const char *channel,
 					const char *color, const char *label,
 					const char *text, const char *extra)
 {
 	int col = num_columns();
-	char line[256], ts_str[64];
+	char line[256], ts_str[96];
 	int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
 
+	if (channel) {
+		if (use_color()) {
+			n = sprintf(ts_str + ts_pos, "%s", COLOR_CHANNEL_LABEL);
+			if (n > 0)
+				ts_pos += n;
+		}
+
+		n = sprintf(ts_str + ts_pos, " {%s}", channel);
+		if (n > 0) {
+			ts_pos += n;
+			ts_len += n;
+		}
+	}
+
 	if ((filter_mask & PACKET_FILTER_SHOW_INDEX) &&
 					index != HCI_DEV_NONE) {
 		if (use_color()) {
@@ -2380,8 +2472,12 @@
 		break;
 	}
 
-	print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)", label, str, version,
+	if (sublabel)
+		print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)",
+					label, str, version,
 					sublabel, subversion, subversion);
+	else
+		print_field("%s: %s (0x%2.2x)", label, str, version);
 }
 
 static void print_hci_version(uint8_t version, uint16_t revision)
@@ -3297,16 +3393,18 @@
 {
 	uint8_t count = data_len / 16;
 	unsigned int i;
+	char uuidstr[MAX_LEN_UUID_STR];
 
 	print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
 
 	for (i = 0; i < count; i++) {
 		const uint8_t *uuid = data + (i * 16);
 
-		print_field("  %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+		sprintf(uuidstr, "%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
 				get_le32(&uuid[12]), get_le16(&uuid[10]),
 				get_le16(&uuid[8]), get_le16(&uuid[6]),
 				get_le32(&uuid[2]), get_le16(&uuid[0]));
+		print_field("  %s (%s)", uuidstr_to_str(uuidstr), uuidstr);
 	}
 }
 
@@ -3802,9 +3900,22 @@
 		packet_user_logging(tv, cred, index, ul->priority, ident,
 					data + sizeof(*ul) + ul->ident_len);
 		break;
+	case BTSNOOP_OPCODE_CTRL_OPEN:
+		control_disable_decoding();
+		packet_ctrl_open(tv, cred, index, data, size);
+		break;
+	case BTSNOOP_OPCODE_CTRL_CLOSE:
+		packet_ctrl_close(tv, cred, index, data, size);
+		break;
+	case BTSNOOP_OPCODE_CTRL_COMMAND:
+		packet_ctrl_command(tv, cred, index, data, size);
+		break;
+	case BTSNOOP_OPCODE_CTRL_EVENT:
+		packet_ctrl_event(tv, cred, index, data, size);
+		break;
 	default:
 		sprintf(extra_str, "(code %d len %d)", opcode, size);
-		print_packet(tv, cred, index, '*', COLOR_ERROR,
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
 					"Unknown packet", NULL, extra_str);
 		packet_hexdump(data, size);
 		break;
@@ -3821,7 +3932,7 @@
 
 	sprintf(str, "%u MHz", frequency);
 
-	print_packet(tv, NULL, 0, '*', COLOR_PHY_PACKET,
+	print_packet(tv, NULL, '*', 0, NULL, COLOR_PHY_PACKET,
 					"Physical packet:", NULL, str);
 
 	ll_packet(frequency, data, size, false);
@@ -8663,26 +8774,26 @@
 	sprintf(details, "(%s,%s,%s)", hci_typetostr(type),
 					hci_bustostr(bus), name);
 
-	print_packet(tv, NULL, index, '=', COLOR_NEW_INDEX, "New Index",
-							label, details);
+	print_packet(tv, NULL, '=', index, NULL, COLOR_NEW_INDEX,
+					"New Index", label, details);
 }
 
 void packet_del_index(struct timeval *tv, uint16_t index, const char *label)
 {
-	print_packet(tv, NULL, index, '=', COLOR_DEL_INDEX, "Delete Index",
-							label, NULL);
+	print_packet(tv, NULL, '=', index, NULL, COLOR_DEL_INDEX,
+					"Delete Index", label, NULL);
 }
 
 void packet_open_index(struct timeval *tv, uint16_t index, const char *label)
 {
-	print_packet(tv, NULL, index, '=', COLOR_OPEN_INDEX, "Open Index",
-							label, NULL);
+	print_packet(tv, NULL, '=', index, NULL, COLOR_OPEN_INDEX,
+					"Open Index", label, NULL);
 }
 
 void packet_close_index(struct timeval *tv, uint16_t index, const char *label)
 {
-	print_packet(tv, NULL, index, '=', COLOR_CLOSE_INDEX, "Close Index",
-							label, NULL);
+	print_packet(tv, NULL, '=', index, NULL, COLOR_CLOSE_INDEX,
+					"Close Index", label, NULL);
 }
 
 void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
@@ -8692,8 +8803,8 @@
 
 	sprintf(details, "(%s)", bt_compidtostr(manufacturer));
 
-	print_packet(tv, NULL, index, '=', COLOR_INDEX_INFO, "Index Info",
-							label, details);
+	print_packet(tv, NULL, '=', index, NULL, COLOR_INDEX_INFO,
+					"Index Info", label, details);
 }
 
 void packet_vendor_diag(struct timeval *tv, uint16_t index,
@@ -8704,7 +8815,7 @@
 
 	sprintf(extra_str, "(len %d)", size);
 
-	print_packet(tv, NULL, index, '=', COLOR_VENDOR_DIAG,
+	print_packet(tv, NULL, '=', index, NULL, COLOR_VENDOR_DIAG,
 					"Vendor Diagnostic", NULL, extra_str);
 
 	switch (manufacturer) {
@@ -8720,7 +8831,8 @@
 void packet_system_note(struct timeval *tv, struct ucred *cred,
 					uint16_t index, const void *message)
 {
-	print_packet(tv, cred, index, '=', COLOR_INFO, "Note", message, NULL);
+	print_packet(tv, cred, '=', index, NULL, COLOR_SYSTEM_NOTE,
+					"Note", message, NULL);
 }
 
 void packet_user_logging(struct timeval *tv, struct ucred *cred,
@@ -8780,7 +8892,7 @@
 			label = "Message";
 	}
 
-	print_packet(tv, cred, index, '=', color, label, message, NULL);
+	print_packet(tv, cred, '=', index, NULL, color, label, message, NULL);
 }
 
 void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index,
@@ -8798,7 +8910,7 @@
 
 	if (size < HCI_COMMAND_HDR_SIZE) {
 		sprintf(extra_str, "(len %d)", size);
-		print_packet(tv, cred, index, '*', COLOR_ERROR,
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
 			"Malformed HCI Command packet", NULL, extra_str);
 		packet_hexdump(data, size);
 		return;
@@ -8856,7 +8968,7 @@
 
 	sprintf(extra_str, "(0x%2.2x|0x%4.4x) plen %d", ogf, ocf, hdr->plen);
 
-	print_packet(tv, cred, index, '<', opcode_color, "HCI Command",
+	print_packet(tv, cred, '<', index, NULL, opcode_color, "HCI Command",
 							opcode_str, extra_str);
 
 	if (!opcode_data || !opcode_data->cmd_func) {
@@ -8899,7 +9011,7 @@
 
 	if (size < HCI_EVENT_HDR_SIZE) {
 		sprintf(extra_str, "(len %d)", size);
-		print_packet(tv, cred, index, '*', COLOR_ERROR,
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
 			"Malformed HCI Event packet", NULL, extra_str);
 		packet_hexdump(data, size);
 		return;
@@ -8936,7 +9048,7 @@
 
 	sprintf(extra_str, "(0x%2.2x) plen %d", hdr->evt, hdr->plen);
 
-	print_packet(tv, cred, index, '>', event_color, "HCI Event",
+	print_packet(tv, cred, '>', index, NULL, event_color, "HCI Event",
 						event_str, extra_str);
 
 	if (!event_data || !event_data->func) {
@@ -8977,24 +9089,24 @@
 	uint8_t flags = acl_flags(handle);
 	char handle_str[16], extra_str[32];
 
-	if (size < sizeof(*hdr)) {
+	if (size < HCI_ACL_HDR_SIZE) {
 		if (in)
-			print_packet(tv, cred, index, '*', COLOR_ERROR,
+			print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
 				"Malformed ACL Data RX packet", NULL, NULL);
 		else
-			print_packet(tv, cred, index, '*', COLOR_ERROR,
+			print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
 				"Malformed ACL Data TX packet", NULL, NULL);
 		packet_hexdump(data, size);
 		return;
 	}
 
-	data += sizeof(*hdr);
-	size -= sizeof(*hdr);
+	data += HCI_ACL_HDR_SIZE;
+	size -= HCI_ACL_HDR_SIZE;
 
 	sprintf(handle_str, "Handle %d", acl_handle(handle));
 	sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, dlen);
 
-	print_packet(tv, cred, index, in ? '>' : '<', COLOR_HCI_ACLDATA,
+	print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_ACLDATA,
 				in ? "ACL Data RX" : "ACL Data TX",
 						handle_str, extra_str);
 
@@ -9021,10 +9133,10 @@
 
 	if (size < HCI_SCO_HDR_SIZE) {
 		if (in)
-			print_packet(tv, cred, index, '*', COLOR_ERROR,
+			print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
 				"Malformed SCO Data RX packet", NULL, NULL);
 		else
-			print_packet(tv, cred, index, '*', COLOR_ERROR,
+			print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
 				"Malformed SCO Data TX packet", NULL, NULL);
 		packet_hexdump(data, size);
 		return;
@@ -9036,7 +9148,7 @@
 	sprintf(handle_str, "Handle %d", acl_handle(handle));
 	sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, hdr->dlen);
 
-	print_packet(tv, cred, index, in ? '>' : '<', COLOR_HCI_SCODATA,
+	print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_SCODATA,
 				in ? "SCO Data RX" : "SCO Data TX",
 						handle_str, extra_str);
 
@@ -9051,6 +9163,2514 @@
 		packet_hexdump(data, size);
 }
 
+void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size)
+{
+	uint32_t cookie;
+	uint16_t format;
+	char channel[11];
+
+	if (size < 6) {
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+				"Malformed Control Open packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	cookie = get_le32(data);
+	format = get_le16(data + 4);
+
+	data += 6;
+	size -= 6;
+
+	sprintf(channel, "0x%4.4x", cookie);
+
+	if ((format == CTRL_RAW || format == CTRL_USER || format == CTRL_MGMT)
+								&& size >= 8) {
+		uint8_t version;
+		uint16_t revision;
+		uint32_t flags;
+		uint8_t ident_len;
+		const char *comm;
+		char details[48];
+		const char *title;
+
+		version = get_u8(data);
+		revision = get_le16(data + 1);
+		flags = get_le32(data + 3);
+		ident_len = get_u8(data + 7);
+
+		data += 8;
+		size -= 8;
+
+		comm = ident_len > 0 ? data : "unknown";
+
+		data += ident_len;
+		size -= ident_len;
+
+		assign_ctrl(cookie, format, comm);
+
+		sprintf(details, "%sversion %u.%u",
+				flags & 0x0001 ? "(privileged) " : "",
+				version, revision);
+
+		switch (format) {
+		case CTRL_RAW:
+			title = "RAW Open";
+			break;
+		case CTRL_USER:
+			title = "USER Open";
+			break;
+		case CTRL_MGMT:
+			title = "MGMT Open";
+			break;
+		default:
+			title = "Control Open";
+			break;
+		}
+
+		print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN,
+						title, comm, details);
+	} else {
+		char label[7];
+
+		assign_ctrl(cookie, format, NULL);
+
+		sprintf(label, "0x%4.4x", format);
+
+		print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN,
+						"Control Open", label, NULL);
+	}
+
+	packet_hexdump(data, size);
+}
+
+void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size)
+{
+	uint32_t cookie;
+	uint16_t format;
+	char channel[11], label[22];
+	const char *title;
+
+	if (size < 4) {
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+				"Malformed Control Close packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	cookie = get_le32(data);
+
+	data += 4;
+	size -= 4;
+
+	sprintf(channel, "0x%4.4x", cookie);
+
+	release_ctrl(cookie, &format, label);
+
+	switch (format) {
+	case CTRL_RAW:
+		title = "RAW Close";
+		break;
+	case CTRL_USER:
+		title = "USER Close";
+		break;
+	case CTRL_MGMT:
+		title = "MGMT Close";
+		break;
+	default:
+		sprintf(label, "0x%4.4x", format);
+		title = "Control Close";
+		break;
+	}
+
+	print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+							title, label, NULL);
+
+	packet_hexdump(data, size);
+}
+
+static const struct {
+	uint8_t status;
+	const char *str;
+} mgmt_status_table[] = {
+	{ 0x00, "Success"		},
+	{ 0x01, "Unknown Command"	},
+	{ 0x02, "Not Connected"		},
+	{ 0x03, "Failed"		},
+	{ 0x04, "Connect Failed"	},
+	{ 0x05, "Authentication Failed"	},
+	{ 0x06, "Not Paired"		},
+	{ 0x07, "No Resources"		},
+	{ 0x08, "Timeout"		},
+	{ 0x09, "Already Connected"	},
+	{ 0x0a, "Busy"			},
+	{ 0x0b, "Rejected"		},
+	{ 0x0c, "Not Supported"		},
+	{ 0x0d, "Invalid Parameters"	},
+	{ 0x0e, "Disconnected"		},
+	{ 0x0f, "Not Powered"		},
+	{ 0x10, "Cancelled"		},
+	{ 0x11, "Invalid Index"		},
+	{ 0x12, "RFKilled"		},
+	{ 0x13, "Already Paired"	},
+	{ 0x14, "Permission Denied"	},
+	{ }
+};
+
+static void mgmt_print_status(uint8_t status)
+{
+	const char *str = "Unknown";
+	const char *color_on, *color_off;
+	bool unknown = true;
+	int i;
+
+	for (i = 0; mgmt_status_table[i].str; i++) {
+		if (mgmt_status_table[i].status == status) {
+			str = mgmt_status_table[i].str;
+			unknown = false;
+			break;
+		}
+	}
+
+	if (use_color()) {
+		if (status) {
+			if (unknown)
+				color_on = COLOR_UNKNOWN_ERROR;
+			else
+				color_on = COLOR_RED;
+		} else
+			color_on = COLOR_GREEN;
+		color_off = COLOR_OFF;
+	} else {
+		color_on = "";
+		color_off = "";
+	}
+
+	print_field("Status: %s%s%s (0x%2.2x)",
+				color_on, str, color_off, status);
+}
+
+static void mgmt_print_address(const uint8_t *address, uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		print_addr_resolve("BR/EDR Address", address, 0x00, false);
+		break;
+	case 0x01:
+		print_addr_resolve("LE Address", address, 0x00, false);
+		break;
+	case 0x02:
+		print_addr_resolve("LE Address", address, 0x01, false);
+		break;
+	default:
+		print_addr_resolve("Address", address, 0xff, false);
+		break;
+	}
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} mgmt_address_type_table[] = {
+	{  0, "BR/EDR"		},
+	{  1, "LE Public"	},
+	{  2, "LE Random"	},
+	{ }
+};
+
+static void mgmt_print_address_type(uint8_t type)
+{
+	uint8_t mask = type;
+	int i;
+
+	print_field("Address type: 0x%2.2x", type);
+
+	for (i = 0; mgmt_address_type_table[i].str; i++) {
+		if (type & (1 << mgmt_address_type_table[i].bit)) {
+			print_field("  %s", mgmt_address_type_table[i].str);
+			mask &= ~(1 << mgmt_address_type_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_ADDRESS_TYPE, "  Unknown address type"
+							" (0x%2.2x)", mask);
+}
+
+static void mgmt_print_version(uint8_t version)
+{
+	packet_print_version("Version", version, NULL, 0x0000);
+}
+
+static void mgmt_print_manufacturer(uint16_t manufacturer)
+{
+	packet_print_company("Manufacturer", manufacturer);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} mgmt_options_table[] = {
+	{  0, "External configuration"			},
+	{  1, "Bluetooth public address configuration"	},
+	{ }
+};
+
+static void mgmt_print_options(const char *label, uint32_t options)
+{
+	uint32_t mask = options;
+	int i;
+
+	print_field("%s: 0x%8.8x", label, options);
+
+	for (i = 0; mgmt_options_table[i].str; i++) {
+		if (options & (1 << mgmt_options_table[i].bit)) {
+			print_field("  %s", mgmt_options_table[i].str);
+			mask &= ~(1 << mgmt_options_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_OPTIONS_BIT, "  Unknown options"
+							" (0x%8.8x)", mask);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} mgmt_settings_table[] = {
+	{  0, "Powered"			},
+	{  1, "Connectable"		},
+	{  2, "Fast Connectable"	},
+	{  3, "Discoverable"		},
+	{  4, "Bondable"		},
+	{  5, "Link Security"		},
+	{  6, "Secure Simple Pairing"	},
+	{  7, "BR/EDR"			},
+	{  8, "High Speed"		},
+	{  9, "Low Energy"		},
+	{ 10, "Advertising"		},
+	{ 11, "Secure Connections"	},
+	{ 12, "Debug Keys"		},
+	{ 13, "Privacy"			},
+	{ 14, "Controller Configuration"},
+	{ 15, "Static Address"		},
+	{ }
+};
+
+static void mgmt_print_settings(const char *label, uint32_t settings)
+{
+	uint32_t mask = settings;
+	int i;
+
+	print_field("%s: 0x%8.8x", label, settings);
+
+	for (i = 0; mgmt_settings_table[i].str; i++) {
+		if (settings & (1 << mgmt_settings_table[i].bit)) {
+			print_field("  %s", mgmt_settings_table[i].str);
+			mask &= ~(1 << mgmt_settings_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_SETTINGS_BIT, "  Unknown settings"
+							" (0x%8.8x)", mask);
+}
+
+static void mgmt_print_name(const void *data)
+{
+	print_field("Name: %s", (char *) data);
+	print_field("Short name: %s", (char *) (data + 249));
+}
+
+static void mgmt_print_uuid(const void *data)
+{
+	const uint8_t *uuid = data;
+
+	print_field("UUID: %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+				get_le32(&uuid[12]), get_le16(&uuid[10]),
+				get_le16(&uuid[8]), get_le16(&uuid[6]),
+				get_le32(&uuid[2]), get_le16(&uuid[0]));
+}
+
+static void mgmt_print_enable(const char *label, uint8_t enable)
+{
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("%s: %s (0x%2.2x)", label, str, enable);
+}
+
+static void mgmt_print_io_capability(uint8_t capability)
+{
+	const char *str;
+
+	switch (capability) {
+	case 0x00:
+		str = "DisplayOnly";
+		break;
+	case 0x01:
+		str = "DisplayYesNo";
+		break;
+	case 0x02:
+		str = "KeyboardOnly";
+		break;
+	case 0x03:
+		str = "NoInputNoOutput";
+		break;
+	case 0x04:
+		str = "KeyboardDisplay";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Capability: %s (0x%2.2x)", str, capability);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} mgmt_device_flags_table[] = {
+	{  0, "Confirm Name"	},
+	{  1, "Legacy Pairing"	},
+	{  2, "Not Connectable"	},
+	{ }
+};
+
+static void mgmt_print_device_flags(uint32_t flags)
+{
+	uint32_t mask = flags;
+	int i;
+
+	print_field("Flags: 0x%8.8x", flags);
+
+	for (i = 0; mgmt_device_flags_table[i].str; i++) {
+		if (flags & (1 << mgmt_device_flags_table[i].bit)) {
+			print_field("  %s", mgmt_device_flags_table[i].str);
+			mask &= ~(1 << mgmt_device_flags_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_DEVICE_FLAG, "  Unknown device flag"
+							" (0x%8.8x)", mask);
+}
+
+static void mgmt_print_device_action(uint8_t action)
+{
+	const char *str;
+
+	switch (action) {
+	case 0x00:
+		str = "Background scan for device";
+		break;
+	case 0x01:
+		str = "Allow incoming connection";
+		break;
+	case 0x02:
+		str = "Auto-connect remote device";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Action: %s (0x%2.2x)", str, action);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} mgmt_adv_flags_table[] = {
+	{  0, "Switch into Connectable mode"		},
+	{  1, "Advertise as Discoverable"		},
+	{  2, "Advertise as Limited Discoverable"	},
+	{  3, "Add Flags field to Advertising Data"	},
+	{  4, "Add TX Power field to Advertising Data"	},
+	{  5, "Add Appearance field to Scan Response"	},
+	{  6, "Add Local Name in Scan Response"		},
+	{ }
+};
+
+static void mgmt_print_adv_flags(uint32_t flags)
+{
+	uint32_t mask = flags;
+	int i;
+
+	print_field("Flags: 0x%8.8x", flags);
+
+	for (i = 0; mgmt_adv_flags_table[i].str; i++) {
+		if (flags & (1 << mgmt_adv_flags_table[i].bit)) {
+			print_field("  %s", mgmt_adv_flags_table[i].str);
+			mask &= ~(1 << mgmt_adv_flags_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_ADV_FLAG, "  Unknown advertising flag"
+							" (0x%8.8x)", mask);
+}
+
+static void mgmt_print_store_hint(uint8_t hint)
+{
+	const char *str;
+
+	switch (hint) {
+	case 0x00:
+		str = "No";
+		break;
+	case 0x01:
+		str = "Yes";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Store hint: %s (0x%2.2x)", str, hint);
+}
+
+static void mgmt_print_connection_parameter(const void *data)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint16_t min_conn_interval = get_le16(data + 7);
+	uint16_t max_conn_interval = get_le16(data + 9);
+	uint16_t conn_latency = get_le16(data + 11);
+	uint16_t supv_timeout = get_le16(data + 13);
+
+	mgmt_print_address(data, address_type);
+	print_field("Min connection interval: %u", min_conn_interval);
+	print_field("Max connection interval: %u", max_conn_interval);
+	print_field("Connection latency: %u", conn_latency);
+	print_field("Supervision timeout: %u", supv_timeout);
+}
+
+static void mgmt_print_link_key(const void *data)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t key_type = get_u8(data + 7);
+	uint8_t pin_len = get_u8(data + 24);
+
+	mgmt_print_address(data, address_type);
+	print_key_type(key_type);
+	print_link_key(data + 8);
+	print_field("PIN length: %d", pin_len);
+}
+
+static void mgmt_print_long_term_key(const void *data)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t key_type = get_u8(data + 7);
+	uint8_t master = get_u8(data + 8);
+	uint8_t enc_size = get_u8(data + 9);
+	const char *str;
+
+	mgmt_print_address(data, address_type);
+
+	switch (key_type) {
+	case 0x00:
+		str = "Unauthenticated legacy key";
+		break;
+	case 0x01:
+		str = "Authenticated legacy key";
+		break;
+	case 0x02:
+		str = "Unauthenticated key from P-256";
+		break;
+	case 0x03:
+		str = "Authenticated key from P-256";
+		break;
+	case 0x04:
+		str = "Debug key from P-256";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Key type: %s (0x%2.2x)", str, key_type);
+	print_field("Master: 0x%2.2x", master);
+	print_field("Encryption size: %u", enc_size);
+	print_hex_field("Diversifier", data + 10, 2);
+	print_hex_field("Randomizer", data + 12, 8);
+	print_hex_field("Key", data + 20, 16);
+}
+
+static void mgmt_print_identity_resolving_key(const void *data)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+	print_hex_field("Key", data + 7, 16);
+}
+
+static void mgmt_print_signature_resolving_key(const void *data)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t key_type = get_u8(data + 7);
+	const char *str;
+
+	mgmt_print_address(data, address_type);
+
+	switch (key_type) {
+	case 0x00:
+		str = "Unauthenticated local CSRK";
+		break;
+	case 0x01:
+		str = "Unauthenticated remote CSRK";
+		break;
+	case 0x02:
+		str = "Authenticated local CSRK";
+		break;
+	case 0x03:
+		str = "Authenticated remote CSRK";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Key type: %s (0x%2.2x)", str, key_type);
+	print_hex_field("Key", data + 8, 16);
+}
+
+static void mgmt_print_oob_data(const void *data)
+{
+	print_hash_p192(data);
+	print_randomizer_p192(data + 16);
+	print_hash_p256(data + 32);
+	print_randomizer_p256(data + 48);
+}
+
+static void mgmt_null_cmd(const void *data, uint16_t size)
+{
+}
+
+static void mgmt_null_rsp(const void *data, uint16_t size)
+{
+}
+
+static void mgmt_read_version_info_rsp(const void *data, uint16_t size)
+{
+	uint8_t version;
+	uint16_t revision;
+
+	version = get_u8(data);
+	revision = get_le16(data + 1);
+
+	print_field("Version: %u.%u", version, revision);
+}
+
+static void mgmt_print_commands(const void *data, uint16_t num);
+static void mgmt_print_events(const void *data, uint16_t num);
+
+static void mgmt_read_supported_commands_rsp(const void *data, uint16_t size)
+{
+	uint16_t num_commands = get_le16(data);
+	uint16_t num_events = get_le16(data + 2);
+
+	if (size - 4 != (num_commands * 2) + (num_events *2)) {
+		packet_hexdump(data, size);
+		return;
+	}
+
+	mgmt_print_commands(data + 4, num_commands);
+	mgmt_print_events(data + 4 + num_commands * 2, num_events);
+}
+
+static void mgmt_read_index_list_rsp(const void *data, uint16_t size)
+{
+	uint16_t num_controllers = get_le16(data);
+	int i;
+
+	print_field("Controllers: %u", num_controllers);
+
+	if (size - 2 != num_controllers * 2) {
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	for (i = 0; i < num_controllers; i++) {
+		uint16_t index = get_le16(data + 2 + (i * 2));
+
+		print_field("  hci%u", index);
+	}
+}
+
+static void mgmt_read_controller_info_rsp(const void *data, uint16_t size)
+{
+	uint8_t version = get_u8(data + 6);
+	uint16_t manufacturer = get_le16(data + 7);
+	uint32_t supported_settings = get_le32(data + 9);
+	uint32_t current_settings = get_le32(data + 13);
+
+	print_addr_resolve("Address", data, 0x00, false);
+	mgmt_print_version(version);
+	mgmt_print_manufacturer(manufacturer);
+	mgmt_print_settings("Supported settings", supported_settings);
+	mgmt_print_settings("Current settings", current_settings);
+	print_dev_class(data + 17);
+	mgmt_print_name(data + 20);
+}
+
+static void mgmt_set_powered_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("Powered", enable);
+}
+
+static void mgmt_set_discoverable_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+	uint16_t timeout = get_le16(data + 1);
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "General";
+		break;
+	case 0x02:
+		str = "Limited";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Discoverable: %s (0x%2.2x)", str, enable);
+	print_field("Timeout: %u", timeout);
+}
+
+static void mgmt_set_connectable_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("Connectable", enable);
+}
+
+static void mgmt_set_fast_connectable_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("Fast Connectable", enable);
+}
+
+static void mgmt_set_bondable_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("Bondable", enable);
+}
+
+static void mgmt_set_link_security_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("Link Security", enable);
+}
+
+static void mgmt_set_secure_simple_pairing_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("Secure Simple Pairing", enable);
+}
+
+static void mgmt_set_high_speed_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("High Speed", enable);
+}
+
+static void mgmt_set_low_energy_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("Low Energy", enable);
+}
+
+static void mgmt_new_settings_rsp(const void *data, uint16_t size)
+{
+	uint32_t current_settings = get_le32(data);
+
+	mgmt_print_settings("Current settings", current_settings);
+}
+
+static void mgmt_set_device_class_cmd(const void *data, uint16_t size)
+{
+	uint8_t major = get_u8(data);
+	uint8_t minor = get_u8(data + 1);
+
+	print_field("Major class: 0x%2.2x", major);
+	print_field("Minor class: 0x%2.2x", minor);
+}
+
+static void mgmt_set_device_class_rsp(const void *data, uint16_t size)
+{
+	print_dev_class(data);
+}
+
+static void mgmt_set_local_name_cmd(const void *data, uint16_t size)
+{
+	mgmt_print_name(data);
+}
+
+static void mgmt_set_local_name_rsp(const void *data, uint16_t size)
+{
+	mgmt_print_name(data);
+}
+
+static void mgmt_add_uuid_cmd(const void *data, uint16_t size)
+{
+	uint8_t service_class = get_u8(data + 16);
+
+	mgmt_print_uuid(data);
+	print_field("Service class: 0x%2.2x", service_class);
+}
+
+static void mgmt_add_uuid_rsp(const void *data, uint16_t size)
+{
+	print_dev_class(data);
+}
+
+static void mgmt_remove_uuid_cmd(const void *data, uint16_t size)
+{
+	mgmt_print_uuid(data);
+}
+
+static void mgmt_remove_uuid_rsp(const void *data, uint16_t size)
+{
+	print_dev_class(data);
+}
+
+static void mgmt_load_link_keys_cmd(const void *data, uint16_t size)
+{
+	uint8_t debug_keys = get_u8(data);
+	uint16_t num_keys = get_le16(data + 1);
+	int i;
+
+	mgmt_print_enable("Debug keys", debug_keys);
+	print_field("Keys: %u", num_keys);
+
+	if (size - 3 != num_keys * 25) {
+		packet_hexdump(data + 3, size - 3);
+		return;
+	}
+
+	for (i = 0; i < num_keys; i++)
+		mgmt_print_link_key(data + 3 + (i * 25));
+}
+
+static void mgmt_load_long_term_keys_cmd(const void *data, uint16_t size)
+{
+	uint16_t num_keys = get_le16(data + 1);
+	int i;
+
+	print_field("Keys: %u", num_keys);
+
+	if (size - 2 != num_keys * 36) {
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	for (i = 0; i < num_keys; i++)
+		mgmt_print_long_term_key(data + 2 + (i * 36));
+}
+
+static void mgmt_disconnect_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_disconnect_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_get_connections_rsp(const void *data, uint16_t size)
+{
+	uint16_t num_connections = get_le16(data);
+	int i;
+
+	print_field("Connections: %u", num_connections);
+
+	if (size - 2 != num_connections * 7) {
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	for (i = 0; i < num_connections; i++) {
+		uint8_t address_type = get_u8(data + 2 + (i * 7) + 6);
+
+		mgmt_print_address(data + 2 + (i * 7), address_type);
+	}
+}
+
+static void mgmt_pin_code_reply_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t pin_len = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	print_field("PIN length: %u", pin_len);
+	print_hex_field("PIN code", data + 8, 16);
+}
+
+static void mgmt_pin_code_reply_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_pin_code_neg_reply_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_pin_code_neg_reply_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_set_io_capability_cmd(const void *data, uint16_t size)
+{
+	uint8_t capability = get_u8(data);
+
+	mgmt_print_io_capability(capability);
+}
+
+static void mgmt_pair_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t capability = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_io_capability(capability);
+}
+
+static void mgmt_pair_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_cancel_pair_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_cancel_pair_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_unpair_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t disconnect = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_enable("Disconnect", disconnect);
+}
+
+static void mgmt_unpair_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_confirmation_reply_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_confirmation_reply_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_confirmation_neg_reply_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_confirmation_neg_reply_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_passkey_reply_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint32_t passkey = get_le32(data + 7);
+
+	mgmt_print_address(data, address_type);
+	print_field("Passkey: 0x%4.4x", passkey);
+}
+
+static void mgmt_user_passkey_reply_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_passkey_neg_reply_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_passkey_neg_reply_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_read_local_oob_data_rsp(const void *data, uint16_t size)
+{
+	mgmt_print_oob_data(data);
+}
+
+static void mgmt_add_remote_oob_data_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_oob_data(data + 7);
+}
+
+static void mgmt_add_remote_oob_data_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_remove_remote_oob_data_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_remove_remote_oob_data_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_start_discovery_cmd(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+
+	mgmt_print_address_type(type);
+}
+
+static void mgmt_start_discovery_rsp(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+
+	mgmt_print_address_type(type);
+}
+
+static void mgmt_stop_discovery_cmd(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+
+	mgmt_print_address_type(type);
+}
+
+static void mgmt_stop_discovery_rsp(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+
+	mgmt_print_address_type(type);
+}
+
+static void mgmt_confirm_name_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t name_known = get_u8(data + 7);
+	const char *str;
+
+	mgmt_print_address(data, address_type);
+
+	switch (name_known) {
+	case 0x00:
+		str = "No";
+		break;
+	case 0x01:
+		str = "Yes";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Name known: %s (0x%2.2x)", str, name_known);
+}
+
+static void mgmt_confirm_name_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_block_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_block_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_unblock_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_unblock_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_set_device_id_cmd(const void *data, uint16_t size)
+{
+	print_device_id(data, size);
+}
+
+static void mgmt_set_advertising_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	case 0x02:
+		str = "Connectable";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Advertising: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_bredr_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("BR/EDR", enable);
+}
+
+static void mgmt_set_static_address_cmd(const void *data, uint16_t size)
+{
+	print_addr_resolve("Address", data, 0x01, false);
+}
+
+static void mgmt_set_scan_parameters_cmd(const void *data, uint16_t size)
+{
+	uint16_t interval = get_le16(data);
+	uint16_t window = get_le16(data + 2);
+
+	print_field("Interval: %u (0x%2.2x)", interval, interval);
+	print_field("Window: %u (0x%2.2x)", window, window);
+}
+
+static void mgmt_set_secure_connections_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	case 0x02:
+		str = "Only";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Secure Connections: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_debug_keys_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	case 0x02:
+		str = "Generate";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Debug Keys: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_privacy_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	case 0x02:
+		str = "Limited";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Privacy: %s (0x%2.2x)", str, enable);
+	print_hex_field("Key", data + 1, 16);
+}
+
+static void mgmt_load_identity_resolving_keys_cmd(const void *data, uint16_t size)
+{
+	uint16_t num_keys = get_le16(data + 1);
+	int i;
+
+	print_field("Keys: %u", num_keys);
+
+	if (size - 2 != num_keys * 23) {
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	for (i = 0; i < num_keys; i++)
+		mgmt_print_identity_resolving_key(data + 2 + (i * 23));
+}
+
+static void mgmt_get_connection_information_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_get_connection_information_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	int8_t rssi = get_s8(data + 7);
+	int8_t tx_power = get_s8(data + 8);
+	int8_t max_tx_power = get_s8(data + 9);
+
+	mgmt_print_address(data, address_type);
+	print_rssi(rssi);
+	print_power_level(tx_power, NULL);
+	print_power_level(max_tx_power, "max");
+}
+
+static void mgmt_get_clock_information_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_get_clock_information_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint32_t local_clock = get_le32(data + 7);
+	uint32_t piconet_clock = get_le32(data + 11);
+	uint16_t accuracy = get_le16(data + 15);
+
+	mgmt_print_address(data, address_type);
+	print_field("Local clock: 0x%8.8x", local_clock);
+	print_field("Piconet clock: 0x%8.8x", piconet_clock);
+	print_field("Accuracy: 0x%4.4x", accuracy);
+}
+
+static void mgmt_add_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t action = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_device_action(action);
+}
+
+static void mgmt_add_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_remove_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_remove_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_load_connection_parameters_cmd(const void *data, uint16_t size)
+{
+	uint16_t num_parameters = get_le16(data);
+	int i;
+
+	print_field("Parameters: %u", num_parameters);
+
+	if (size - 2 != num_parameters * 15) {
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	for (i = 0; i < num_parameters; i++)
+		mgmt_print_connection_parameter(data + 2 + (i * 15));
+}
+
+static void mgmt_read_unconf_index_list_rsp(const void *data, uint16_t size)
+{
+	uint16_t num_controllers = get_le16(data);
+	int i;
+
+	print_field("Controllers: %u", num_controllers);
+
+	if (size - 2 != num_controllers * 2) {
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	for (i = 0; i < num_controllers; i++) {
+		uint16_t index = get_le16(data + 2 + (i * 2));
+
+		print_field("  hci%u", index);
+	}
+}
+
+static void mgmt_read_controller_conf_info_rsp(const void *data, uint16_t size)
+{
+	uint16_t manufacturer = get_le16(data);
+	uint32_t supported_options = get_le32(data + 2);
+	uint32_t missing_options = get_le32(data + 6);
+
+	mgmt_print_manufacturer(manufacturer);
+	mgmt_print_options("Supported options", supported_options);
+	mgmt_print_options("Missing options", missing_options);
+}
+
+static void mgmt_set_external_configuration_cmd(const void *data, uint16_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	mgmt_print_enable("Configuration", enable);
+}
+
+static void mgmt_set_public_address_cmd(const void *data, uint16_t size)
+{
+	print_addr_resolve("Address", data, 0x00, false);
+}
+
+static void mgmt_new_options_rsp(const void *data, uint16_t size)
+{
+	uint32_t missing_options = get_le32(data);
+
+	mgmt_print_options("Missing options", missing_options);
+}
+
+static void mgmt_start_service_discovery_cmd(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+	int8_t rssi = get_s8(data + 1);
+	uint16_t num_uuids = get_le16(data + 2);
+	int i;
+
+	mgmt_print_address_type(type);
+	print_rssi(rssi);
+	print_field("UUIDs: %u", num_uuids);
+
+	if (size - 4 != num_uuids * 16) {
+		packet_hexdump(data + 4, size - 4);
+		return;
+	}
+
+	for (i = 0; i < num_uuids; i++)
+		mgmt_print_uuid(data + 4 + (i * 16));
+}
+
+static void mgmt_start_service_discovery_rsp(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+
+	mgmt_print_address_type(type);
+}
+
+static void mgmt_read_ext_index_list_rsp(const void *data, uint16_t size)
+{
+	uint16_t num_controllers = get_le16(data);
+	int i;
+
+	print_field("Controllers: %u", num_controllers);
+
+	if (size - 2 != num_controllers * 4) {
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	for (i = 0; i < num_controllers; i++) {
+		uint16_t index = get_le16(data + 2 + (i * 4));
+		uint8_t type = get_u8(data + 4 + (i * 4));
+		uint8_t bus = get_u8(data + 5 + (i * 4));
+		const char *str;
+
+		switch (type) {
+		case 0x00:
+			str = "Primary";
+			break;
+		case 0x01:
+			str = "Unconfigured";
+			break;
+		case 0x02:
+			str = "AMP";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("  hci%u (%s,%s)", index, str, hci_bustostr(bus));
+	}
+}
+
+static void mgmt_read_local_oob_ext_data_cmd(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+
+	mgmt_print_address_type(type);
+}
+
+static void mgmt_read_local_oob_ext_data_rsp(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+	uint16_t data_len = get_le16(data + 1);
+
+	mgmt_print_address_type(type);
+	print_field("Data length: %u", data_len);
+	print_eir(data + 3, size - 3, true);
+}
+
+static void mgmt_read_advertising_features_rsp(const void *data, uint16_t size)
+{
+	uint32_t flags = get_le32(data);
+	uint8_t adv_data_len = get_u8(data + 4);
+	uint8_t scan_rsp_len = get_u8(data + 5);
+	uint8_t max_instances = get_u8(data + 6);
+	uint8_t num_instances = get_u8(data + 7);
+	int i;
+
+	mgmt_print_adv_flags(flags);
+	print_field("Advertising data length: %u", adv_data_len);
+	print_field("Scan response length: %u", scan_rsp_len);
+	print_field("Max instances: %u", max_instances);
+	print_field("Instances: %u", num_instances);
+
+	if (size - 8 != num_instances) {
+		packet_hexdump(data + 8, size - 8);
+		return;
+	}
+
+	for (i = 0; i < num_instances; i++) {
+		uint8_t instance = get_u8(data + 8 + i);
+
+		print_field("  %u", instance);
+	}
+}
+
+static void mgmt_add_advertising_cmd(const void *data, uint16_t size)
+{
+	uint8_t instance = get_u8(data);
+	uint32_t flags = get_le32(data + 1);
+	uint16_t duration = get_le16(data + 5);
+	uint16_t timeout = get_le16(data + 7);
+	uint8_t adv_data_len = get_u8(data + 9);
+	uint8_t scan_rsp_len = get_u8(data + 10);
+
+	print_field("Instance: %u", instance);
+	mgmt_print_adv_flags(flags);
+	print_field("Duration: %u", duration);
+	print_field("Timeout: %u", timeout);
+	print_field("Advertising data length: %u", adv_data_len);
+	print_eir(data + 11, adv_data_len, false);
+	print_field("Scan response length: %u", scan_rsp_len);
+	print_eir(data + 11 + adv_data_len, scan_rsp_len, false);
+}
+
+static void mgmt_add_advertising_rsp(const void *data, uint16_t size)
+{
+	uint8_t instance = get_u8(data);
+
+	print_field("Instance: %u", instance);
+}
+
+static void mgmt_remove_advertising_cmd(const void *data, uint16_t size)
+{
+	uint8_t instance = get_u8(data);
+
+	print_field("Instance: %u", instance);
+}
+
+static void mgmt_remove_advertising_rsp(const void *data, uint16_t size)
+{
+	uint8_t instance = get_u8(data);
+
+	print_field("Instance: %u", instance);
+}
+
+static void mgmt_get_advertising_size_info_cmd(const void *data, uint16_t size)
+{
+	uint8_t instance = get_u8(data);
+	uint32_t flags = get_le32(data + 1);
+
+	print_field("Instance: %u", instance);
+	mgmt_print_adv_flags(flags);
+}
+
+static void mgmt_get_advertising_size_info_rsp(const void *data, uint16_t size)
+{
+	uint8_t instance = get_u8(data);
+	uint32_t flags = get_le32(data + 1);
+	uint8_t adv_data_len = get_u8(data + 5);
+	uint8_t scan_rsp_len = get_u8(data + 6);
+
+	print_field("Instance: %u", instance);
+	mgmt_print_adv_flags(flags);
+	print_field("Advertising data length: %u", adv_data_len);
+	print_field("Scan response length: %u", scan_rsp_len);
+}
+
+static void mgmt_start_limited_discovery_cmd(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+
+	mgmt_print_address_type(type);
+}
+
+static void mgmt_start_limited_discovery_rsp(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+
+	mgmt_print_address_type(type);
+}
+
+static void mgmt_read_ext_controller_info_rsp(const void *data, uint16_t size)
+{
+	uint8_t version = get_u8(data + 6);
+	uint16_t manufacturer = get_le16(data + 7);
+	uint32_t supported_settings = get_le32(data + 9);
+	uint32_t current_settings = get_le32(data + 13);
+	uint16_t data_len = get_le16(data + 17);
+
+	print_addr_resolve("Address", data, 0x00, false);
+	mgmt_print_version(version);
+	mgmt_print_manufacturer(manufacturer);
+	mgmt_print_settings("Supported settings", supported_settings);
+	mgmt_print_settings("Current settings", current_settings);
+	print_field("Data length: %u", data_len);
+	print_eir(data + 19, size - 19, false);
+}
+
+static void mgmt_set_apperance_cmd(const void *data, uint16_t size)
+{
+	uint16_t appearance = get_le16(data);
+
+	print_appearance(appearance);
+}
+
+struct mgmt_data {
+	uint16_t opcode;
+	const char *str;
+	void (*func) (const void *data, uint16_t size);
+	uint16_t size;
+	bool fixed;
+	void (*rsp_func) (const void *data, uint16_t size);
+	uint16_t rsp_size;
+	bool rsp_fixed;
+};
+
+static const struct mgmt_data mgmt_command_table[] = {
+	{ 0x0001, "Read Management Version Information",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_version_info_rsp, 3, true },
+	{ 0x0002, "Read Management Supported Commands",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_supported_commands_rsp, 4, false },
+	{ 0x0003, "Read Controller Index List",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_index_list_rsp, 2, false },
+	{ 0x0004, "Read Controller Information",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_controller_info_rsp, 280, true },
+	{ 0x0005, "Set Powered",
+				mgmt_set_powered_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x0006, "Set Discoverable",
+				mgmt_set_discoverable_cmd, 3, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x0007, "Set Connectable",
+				mgmt_set_connectable_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x0008, "Set Fast Connectable",
+				mgmt_set_fast_connectable_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x0009, "Set Bondable",
+				mgmt_set_bondable_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x000a, "Set Link Security",
+				mgmt_set_link_security_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x000b, "Set Secure Simple Pairing",
+				mgmt_set_secure_simple_pairing_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x000c, "Set High Speed",
+				mgmt_set_high_speed_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x000d, "Set Low Energy",
+				mgmt_set_low_energy_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x000e, "Set Device Class",
+				mgmt_set_device_class_cmd, 2, true,
+				mgmt_set_device_class_rsp, 3, true },
+	{ 0x000f, "Set Local Name",
+				mgmt_set_local_name_cmd, 260, true,
+				mgmt_set_local_name_rsp, 260, true },
+	{ 0x0010, "Add UUID",
+				mgmt_add_uuid_cmd, 17, true,
+				mgmt_add_uuid_rsp, 3, true },
+	{ 0x0011, "Remove UUID",
+				mgmt_remove_uuid_cmd, 16, true,
+				mgmt_remove_uuid_rsp, 3, true },
+	{ 0x0012, "Load Link Keys",
+				mgmt_load_link_keys_cmd, 3, false,
+				mgmt_null_rsp, 0, true },
+	{ 0x0013, "Load Long Term Keys",
+				mgmt_load_long_term_keys_cmd, 2, false,
+				mgmt_null_rsp, 0, true },
+	{ 0x0014, "Disconnect",
+				mgmt_disconnect_cmd, 7, true,
+				mgmt_disconnect_rsp, 7, true },
+	{ 0x0015, "Get Connections",
+				mgmt_null_cmd, 0, true,
+				mgmt_get_connections_rsp, 2, false },
+	{ 0x0016, "PIN Code Reply",
+				mgmt_pin_code_reply_cmd, 24, true,
+				mgmt_pin_code_reply_rsp, 7, true },
+	{ 0x0017, "PIN Code Negative Reply",
+				mgmt_pin_code_neg_reply_cmd, 7, true,
+				mgmt_pin_code_neg_reply_rsp, 7, true },
+	{ 0x0018, "Set IO Capability",
+				mgmt_set_io_capability_cmd, 1, true,
+				mgmt_null_rsp, 0, true },
+	{ 0x0019, "Pair Device",
+				mgmt_pair_device_cmd, 8, true,
+				mgmt_pair_device_rsp, 7, true },
+	{ 0x001a, "Cancel Pair Device",
+				mgmt_cancel_pair_device_cmd, 7, true,
+				mgmt_cancel_pair_device_rsp, 7, true },
+	{ 0x001b, "Unpair Device",
+				mgmt_unpair_device_cmd, 8, true,
+				mgmt_unpair_device_rsp, 7, true },
+	{ 0x001c, "User Confirmation Reply",
+				mgmt_user_confirmation_reply_cmd, 7, true,
+				mgmt_user_confirmation_reply_rsp, 7, true },
+	{ 0x001d, "User Confirmation Negative Reply",
+				mgmt_user_confirmation_neg_reply_cmd, 7, true,
+				mgmt_user_confirmation_neg_reply_rsp, 7, true },
+	{ 0x001e, "User Passkey Reply",
+				mgmt_user_passkey_reply_cmd, 11, true,
+				mgmt_user_passkey_reply_rsp, 7, true },
+	{ 0x001f, "User Passkey Negative Reply",
+				mgmt_user_passkey_neg_reply_cmd, 7, true,
+				mgmt_user_passkey_neg_reply_rsp, 7, true },
+	{ 0x0020, "Read Local Out Of Band Data",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_local_oob_data_rsp, 64, true },
+	{ 0x0021, "Add Remote Out Of Band Data",
+				mgmt_add_remote_oob_data_cmd, 71, true,
+				mgmt_add_remote_oob_data_rsp, 7, true },
+	{ 0x0022, "Remove Remote Out Of Band Data",
+				mgmt_remove_remote_oob_data_cmd, 7, true,
+				mgmt_remove_remote_oob_data_rsp, 7, true },
+	{ 0x0023, "Start Discovery",
+				mgmt_start_discovery_cmd, 1, true,
+				mgmt_start_discovery_rsp, 1, true },
+	{ 0x0024, "Stop Discovery",
+				mgmt_stop_discovery_cmd, 1, true,
+				mgmt_stop_discovery_rsp, 1, true },
+	{ 0x0025, "Confirm Name",
+				mgmt_confirm_name_cmd, 8, true,
+				mgmt_confirm_name_rsp, 7, true },
+	{ 0x0026, "Block Device",
+				mgmt_block_device_cmd, 7, true,
+				mgmt_block_device_rsp, 7, true },
+	{ 0x0027, "Unblock Device",
+				mgmt_unblock_device_cmd, 7, true,
+				mgmt_unblock_device_rsp, 7, true },
+	{ 0x0028, "Set Device ID",
+				mgmt_set_device_id_cmd, 8, true,
+				mgmt_null_rsp, 0, true },
+	{ 0x0029, "Set Advertising",
+				mgmt_set_advertising_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x002a, "Set BR/EDR",
+				mgmt_set_bredr_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x002b, "Set Static Address",
+				mgmt_set_static_address_cmd, 6, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x002c, "Set Scan Parameters",
+				mgmt_set_scan_parameters_cmd, 4, true,
+				mgmt_null_rsp, 0, true },
+	{ 0x002d, "Set Secure Connections",
+				mgmt_set_secure_connections_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x002e, "Set Debug Keys",
+				mgmt_set_debug_keys_cmd, 1, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x002f, "Set Privacy",
+				mgmt_set_privacy_cmd, 17, true,
+				mgmt_new_settings_rsp, 4, true },
+	{ 0x0030, "Load Identity Resolving Keys",
+				mgmt_load_identity_resolving_keys_cmd, 2, false,
+				mgmt_null_rsp, 0, true },
+	{ 0x0031, "Get Connection Information",
+				mgmt_get_connection_information_cmd, 7, true,
+				mgmt_get_connection_information_rsp, 10, true },
+	{ 0x0032, "Get Clock Information",
+				mgmt_get_clock_information_cmd, 7, true,
+				mgmt_get_clock_information_rsp, 17, true },
+	{ 0x0033, "Add Device",
+				mgmt_add_device_cmd, 8, true,
+				mgmt_add_device_rsp, 7, true },
+	{ 0x0034, "Remove Device",
+				mgmt_remove_device_cmd, 7, true,
+				mgmt_remove_device_rsp, 7, true },
+	{ 0x0035, "Load Connection Parameters",
+				mgmt_load_connection_parameters_cmd, 2, false,
+				mgmt_null_rsp, 0, true },
+	{ 0x0036, "Read Unconfigured Controller Index List",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_unconf_index_list_rsp, 2, false },
+	{ 0x0037, "Read Controller Configuration Information",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_controller_conf_info_rsp, 10, true },
+	{ 0x0038, "Set External Configuration",
+				mgmt_set_external_configuration_cmd, 1, true,
+				mgmt_new_options_rsp, 4, true },
+	{ 0x0039, "Set Public Address",
+				mgmt_set_public_address_cmd, 6, true,
+				mgmt_new_options_rsp, 4, true },
+	{ 0x003a, "Start Service Discovery",
+				mgmt_start_service_discovery_cmd, 3, false,
+				mgmt_start_service_discovery_rsp, 1, true },
+	{ 0x003b, "Read Local Out Of Band Extended Data",
+				mgmt_read_local_oob_ext_data_cmd, 1, true,
+				mgmt_read_local_oob_ext_data_rsp, 3, false },
+	{ 0x003c, "Read Extended Controller Index List",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_ext_index_list_rsp, 2, false },
+	{ 0x003d, "Read Advertising Features",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_advertising_features_rsp, 8, false },
+	{ 0x003e, "Add Advertising",
+				mgmt_add_advertising_cmd, 11, false,
+				mgmt_add_advertising_rsp, 1, true },
+	{ 0x003f, "Remove Advertising",
+				mgmt_remove_advertising_cmd, 1, true,
+				mgmt_remove_advertising_rsp, 1, true },
+	{ 0x0040, "Get Advertising Size Information",
+				mgmt_get_advertising_size_info_cmd, 5, true,
+				mgmt_get_advertising_size_info_rsp, 7, true },
+	{ 0x0041, "Start Limited Discovery",
+				mgmt_start_limited_discovery_cmd, 1, true,
+				mgmt_start_limited_discovery_rsp, 1, true },
+	{ 0x0042, "Read Extended Controller Information",
+				mgmt_null_cmd, 0, true,
+				mgmt_read_ext_controller_info_rsp, 19, false },
+	{ 0x0043, "Set Appearance",
+				mgmt_set_apperance_cmd, 2, true,
+				mgmt_null_rsp, 0, true },
+	{ }
+};
+
+static void mgmt_null_evt(const void *data, uint16_t size)
+{
+}
+
+static void mgmt_command_complete_evt(const void *data, uint16_t size)
+{
+	uint16_t opcode;
+	uint8_t status;
+	const struct mgmt_data *mgmt_data = NULL;
+	const char *mgmt_color, *mgmt_str;
+	int i;
+
+	opcode = get_le16(data);
+	status = get_u8(data + 2);
+
+	data += 3;
+	size -= 3;
+
+	for (i = 0; mgmt_command_table[i].str; i++) {
+		if (mgmt_command_table[i].opcode == opcode) {
+			mgmt_data = &mgmt_command_table[i];
+			break;
+		}
+	}
+
+	if (mgmt_data) {
+		if (mgmt_data->rsp_func)
+			mgmt_color = COLOR_CTRL_COMMAND;
+		else
+			mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+		mgmt_str = mgmt_data->str;
+	} else {
+		mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+		mgmt_str = "Unknown";
+	}
+
+	print_indent(6, mgmt_color, "", mgmt_str, COLOR_OFF,
+					" (0x%4.4x) plen %u", opcode, size);
+
+	mgmt_print_status(status);
+
+	if (!mgmt_data || !mgmt_data->rsp_func) {
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (mgmt_data->rsp_fixed) {
+		if (size != mgmt_data->rsp_size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data, size);
+			return;
+		}
+	} else {
+		if (size < mgmt_data->rsp_size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data, size);
+			return;
+		}
+	}
+
+	mgmt_data->rsp_func(data, size);
+}
+
+static void mgmt_command_status_evt(const void *data, uint16_t size)
+{
+	uint16_t opcode;
+	uint8_t status;
+	const struct mgmt_data *mgmt_data = NULL;
+	const char *mgmt_color, *mgmt_str;
+	int i;
+
+	opcode = get_le16(data);
+	status = get_u8(data + 2);
+
+	for (i = 0; mgmt_command_table[i].str; i++) {
+		if (mgmt_command_table[i].opcode == opcode) {
+			mgmt_data = &mgmt_command_table[i];
+			break;
+		}
+	}
+
+	if (mgmt_data) {
+		mgmt_color = COLOR_CTRL_COMMAND;
+		mgmt_str = mgmt_data->str;
+	} else {
+		mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+		mgmt_str = "Unknown";
+	}
+
+	print_indent(6, mgmt_color, "", mgmt_str, COLOR_OFF,
+						" (0x%4.4x)", opcode);
+
+	mgmt_print_status(status);
+}
+
+static void mgmt_controller_error_evt(const void *data, uint16_t size)
+{
+	uint8_t error = get_u8(data);
+
+	print_field("Error: 0x%2.2x", error);
+}
+
+static void mgmt_new_settings_evt(const void *data, uint16_t size)
+{
+	uint32_t settings = get_le32(data);
+
+	mgmt_print_settings("Current settings", settings);
+}
+
+static void mgmt_class_of_dev_changed_evt(const void *data, uint16_t size)
+{
+	print_dev_class(data);
+}
+
+static void mgmt_local_name_changed_evt(const void *data, uint16_t size)
+{
+	mgmt_print_name(data);
+}
+
+static void mgmt_new_link_key_evt(const void *data, uint16_t size)
+{
+	uint8_t store_hint = get_u8(data);
+
+	mgmt_print_store_hint(store_hint);
+	mgmt_print_link_key(data + 1);
+}
+
+static void mgmt_new_long_term_key_evt(const void *data, uint16_t size)
+{
+	uint8_t store_hint = get_u8(data);
+
+	mgmt_print_store_hint(store_hint);
+	mgmt_print_long_term_key(data + 1);
+}
+
+static void mgmt_device_connected_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint32_t flags = get_le32(data + 7);
+	uint16_t data_len = get_le16(data + 11);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_device_flags(flags);
+	print_field("Data length: %u", data_len);
+	print_eir(data + 13, size - 13, false);
+}
+
+static void mgmt_device_disconnected_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t reason = get_u8(data + 7);
+	const char *str;
+
+	mgmt_print_address(data, address_type);
+
+	switch (reason) {
+	case 0x00:
+		str = "Unspecified";
+		break;
+	case 0x01:
+		str = "Connection timeout";
+		break;
+	case 0x02:
+		str = "Connection terminated by local host";
+		break;
+	case 0x03:
+		str = "Connection terminated by remote host";
+		break;
+	case 0x04:
+		str = "Connection terminated due to authentication failure";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reason: %s (0x%2.2x)", str, reason);
+}
+
+static void mgmt_connect_failed_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t status = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_status(status);
+}
+
+static void mgmt_pin_code_request_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t secure_pin = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	print_field("Secure PIN: 0x%2.2x", secure_pin);
+}
+
+static void mgmt_user_confirmation_request_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t confirm_hint = get_u8(data + 7);
+	uint32_t value = get_le32(data + 8);
+
+	mgmt_print_address(data, address_type);
+	print_field("Confirm hint: 0x%2.2x", confirm_hint);
+	print_field("Value: 0x%8.8x", value);
+}
+
+static void mgmt_user_passkey_request_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_authentication_failed_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t status = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_status(status);
+}
+
+static void mgmt_device_found_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	int8_t rssi = get_s8(data + 7);
+	uint32_t flags = get_le32(data + 8);
+	uint16_t data_len = get_le16(data + 12);
+
+	mgmt_print_address(data, address_type);
+	print_rssi(rssi);
+	mgmt_print_device_flags(flags);
+	print_field("Data length: %u", data_len);
+	print_eir(data + 14, size - 14, false);
+}
+
+static void mgmt_discovering_evt(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+	uint8_t enable = get_u8(data + 1);
+
+	mgmt_print_address_type(type);
+	mgmt_print_enable("Discovery", enable);
+}
+
+static void mgmt_device_blocked_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_device_unblocked_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_device_unpaired_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_passkey_notify_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint32_t passkey = get_le32(data + 7);
+	uint8_t entered = get_u8(data + 11);
+
+	mgmt_print_address(data, address_type);
+	print_field("Passkey: 0x%8.8x", passkey);
+	print_field("Entered: %u", entered);
+}
+
+static void mgmt_new_identity_resolving_key_evt(const void *data, uint16_t size)
+{
+	uint8_t store_hint = get_u8(data);
+
+	mgmt_print_store_hint(store_hint);
+	print_addr_resolve("Random address", data + 1, 0x01, false);
+	mgmt_print_identity_resolving_key(data + 7);
+}
+
+static void mgmt_new_signature_resolving_key_evt(const void *data, uint16_t size)
+{
+	uint8_t store_hint = get_u8(data);
+
+	mgmt_print_store_hint(store_hint);
+	mgmt_print_signature_resolving_key(data + 1);
+}
+
+static void mgmt_device_added_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t action = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_device_action(action);
+}
+
+static void mgmt_device_removed_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_new_connection_parameter_evt(const void *data, uint16_t size)
+{
+	uint8_t store_hint = get_u8(data);
+
+	mgmt_print_store_hint(store_hint);
+	mgmt_print_connection_parameter(data + 1);
+}
+
+static void mgmt_new_conf_options_evt(const void *data, uint16_t size)
+{
+	uint32_t missing_options = get_le32(data);
+
+	mgmt_print_options("Missing options", missing_options);
+}
+
+static void mgmt_ext_index_added_evt(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+	uint8_t bus = get_u8(data + 1);
+
+	print_field("type 0x%2.2x - bus 0x%2.2x", type, bus);
+}
+
+static void mgmt_ext_index_removed_evt(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+	uint8_t bus = get_u8(data + 1);
+
+	print_field("type 0x%2.2x - bus 0x%2.2x", type, bus);
+}
+
+static void mgmt_local_oob_ext_data_updated_evt(const void *data, uint16_t size)
+{
+	uint8_t type = get_u8(data);
+	uint16_t data_len = get_le16(data + 1);
+
+	mgmt_print_address_type(type);
+	print_field("Data length: %u", data_len);
+	print_eir(data + 3, size - 3, true);
+}
+
+static void mgmt_advertising_added_evt(const void *data, uint16_t size)
+{
+	uint8_t instance = get_u8(data);
+
+	print_field("Instance: %u", instance);
+}
+
+static void mgmt_advertising_removed_evt(const void *data, uint16_t size)
+{
+	uint8_t instance = get_u8(data);
+
+	print_field("Instance: %u", instance);
+}
+
+static void mgmt_ext_controller_info_changed_evt(const void *data, uint16_t size)
+{
+	uint16_t data_len = get_le16(data);
+
+	print_field("Data length: %u", data_len);
+	print_eir(data + 2, size - 2, false);
+}
+
+static const struct mgmt_data mgmt_event_table[] = {
+	{ 0x0001, "Command Complete",
+			mgmt_command_complete_evt, 3, false },
+	{ 0x0002, "Command Status",
+			mgmt_command_status_evt, 3, true },
+	{ 0x0003, "Controller Error",
+			mgmt_controller_error_evt, 1, true },
+	{ 0x0004, "Index Added",
+			mgmt_null_evt, 0, true },
+	{ 0x0005, "Index Removed",
+			mgmt_null_evt, 0, true },
+	{ 0x0006, "New Settings",
+			mgmt_new_settings_evt, 4, true },
+	{ 0x0007, "Class Of Device Changed",
+			mgmt_class_of_dev_changed_evt, 3, true },
+	{ 0x0008, "Local Name Changed",
+			mgmt_local_name_changed_evt, 260, true },
+	{ 0x0009, "New Link Key",
+			mgmt_new_link_key_evt, 26, true },
+	{ 0x000a, "New Long Term Key",
+			mgmt_new_long_term_key_evt, 37, true },
+	{ 0x000b, "Device Connected",
+			mgmt_device_connected_evt, 13, false },
+	{ 0x000c, "Device Disconnected",
+			mgmt_device_disconnected_evt, 8, true },
+	{ 0x000d, "Connect Failed",
+			mgmt_connect_failed_evt, 8, true },
+	{ 0x000e, "PIN Code Request",
+			mgmt_pin_code_request_evt, 8, true },
+	{ 0x000f, "User Confirmation Request",
+			mgmt_user_confirmation_request_evt, 12, true },
+	{ 0x0010, "User Passkey Request",
+			mgmt_user_passkey_request_evt, 7, true },
+	{ 0x0011, "Authentication Failed",
+			mgmt_authentication_failed_evt, 8, true },
+	{ 0x0012, "Device Found",
+			mgmt_device_found_evt, 14, false },
+	{ 0x0013, "Discovering",
+			mgmt_discovering_evt, 2, true },
+	{ 0x0014, "Device Blocked",
+			mgmt_device_blocked_evt, 7, true },
+	{ 0x0015, "Device Unblocked",
+			mgmt_device_unblocked_evt, 7, true },
+	{ 0x0016, "Device Unpaired",
+			mgmt_device_unpaired_evt, 7, true },
+	{ 0x0017, "Passkey Notify",
+			mgmt_passkey_notify_evt, 12, true },
+	{ 0x0018, "New Identity Resolving Key",
+			mgmt_new_identity_resolving_key_evt, 30, true },
+	{ 0x0019, "New Signature Resolving Key",
+			mgmt_new_signature_resolving_key_evt, 25, true },
+	{ 0x001a, "Device Added",
+			mgmt_device_added_evt, 8, true },
+	{ 0x001b, "Device Removed",
+			mgmt_device_removed_evt, 7, true },
+	{ 0x001c, "New Connection Parameter",
+			mgmt_new_connection_parameter_evt, 16, true },
+	{ 0x001d, "Unconfigured Index Added",
+			mgmt_null_evt, 0, true },
+	{ 0x001e, "Unconfigured Index Removed",
+			mgmt_null_evt, 0, true },
+	{ 0x001f, "New Configuration Options",
+			mgmt_new_conf_options_evt, 4, true },
+	{ 0x0020, "Extended Index Added",
+			mgmt_ext_index_added_evt, 2, true },
+	{ 0x0021, "Extended Index Removed",
+			mgmt_ext_index_removed_evt, 2, true },
+	{ 0x0022, "Local Out Of Band Extended Data Updated",
+			mgmt_local_oob_ext_data_updated_evt, 3, false },
+	{ 0x0023, "Advertising Added",
+			mgmt_advertising_added_evt, 1, true },
+	{ 0x0024, "Advertising Removed",
+			mgmt_advertising_removed_evt, 1, true },
+	{ 0x0025, "Extended Controller Information Changed",
+			mgmt_ext_controller_info_changed_evt, 2, false },
+	{ }
+};
+
+static void mgmt_print_commands(const void *data, uint16_t num)
+{
+	int i;
+
+	print_field("Commands: %u", num);
+
+	for (i = 0; i < num; i++) {
+		uint16_t opcode = get_le16(data + (i * 2));
+		const char *str = NULL;
+		int n;
+
+		for (n = 0; mgmt_command_table[n].str; n++) {
+			if (mgmt_command_table[n].opcode == opcode) {
+				str = mgmt_command_table[n].str;
+				break;
+			}
+		}
+
+		print_field("  %s (0x%4.4x)", str ?: "Reserved", opcode);
+	}
+}
+
+static void mgmt_print_events(const void *data, uint16_t num)
+{
+	int i;
+
+	print_field("Events: %u", num);
+
+	for (i = 0; i < num; i++) {
+		uint16_t opcode = get_le16(data + (i * 2));
+		const char *str = NULL;
+		int n;
+
+		for (n = 0; mgmt_event_table[n].str; n++) {
+			if (mgmt_event_table[n].opcode == opcode) {
+				str = mgmt_event_table[n].str;
+				break;
+			}
+		}
+
+		print_field("  %s (0x%4.4x)", str ?: "Reserved", opcode);
+	}
+}
+
+void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size)
+{
+	uint32_t cookie;
+	uint16_t format, opcode;
+	const struct mgmt_data *mgmt_data = NULL;
+	const char *mgmt_color, *mgmt_str;
+	char channel[11], extra_str[25];
+	int i;
+
+	if (size < 4) {
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+				"Malformed Control Command packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	cookie = get_le32(data);
+
+	data += 4;
+	size -= 4;
+
+	sprintf(channel, "0x%4.4x", cookie);
+
+	format = get_format(cookie);
+
+	if (format != CTRL_MGMT) {
+		char label[7];
+
+		sprintf(label, "0x%4.4x", format);
+
+		print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+						"Control Command", label, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (size < 2) {
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+				"Malformed MGMT Command packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	opcode = get_le16(data);
+
+	data += 2;
+	size -= 2;
+
+	for (i = 0; mgmt_command_table[i].str; i++) {
+		if (mgmt_command_table[i].opcode == opcode) {
+			mgmt_data = &mgmt_command_table[i];
+			break;
+		}
+	}
+
+	if (mgmt_data) {
+		if (mgmt_data->func)
+			mgmt_color = COLOR_CTRL_COMMAND;
+		else
+			mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+		mgmt_str = mgmt_data->str;
+	} else {
+		mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+		mgmt_str = "Unknown";
+	}
+
+	sprintf(extra_str, "(0x%4.4x) plen %d", opcode, size);
+
+	print_packet(tv, cred, '@', index, channel, mgmt_color,
+					"MGMT Command", mgmt_str, extra_str);
+
+	if (!mgmt_data || !mgmt_data->func) {
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (mgmt_data->fixed) {
+		if (size != mgmt_data->size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data, size);
+			return;
+		}
+	} else {
+		if (size < mgmt_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data, size);
+			return;
+		}
+	}
+
+	mgmt_data->func(data, size);
+}
+
+void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size)
+{
+	uint32_t cookie;
+	uint16_t format, opcode;
+	const struct mgmt_data *mgmt_data = NULL;
+	const char *mgmt_color, *mgmt_str;
+	char channel[11], extra_str[25];
+	int i;
+
+	if (size < 4) {
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+				"Malformed Control Event packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	cookie = get_le32(data);
+
+	data += 4;
+	size -= 4;
+
+	sprintf(channel, "0x%4.4x", cookie);
+
+	format = get_format(cookie);
+
+	if (format != CTRL_MGMT) {
+		char label[7];
+
+		sprintf(label, "0x%4.4x", format);
+
+		print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+						"Control Event", label, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (size < 2) {
+		print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+				"Malformed MGMT Event packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	opcode = get_le16(data);
+
+	data += 2;
+	size -= 2;
+
+	for (i = 0; mgmt_event_table[i].str; i++) {
+		if (mgmt_event_table[i].opcode == opcode) {
+			mgmt_data = &mgmt_event_table[i];
+			break;
+		}
+	}
+
+	if (mgmt_data) {
+		if (mgmt_data->func)
+			mgmt_color = COLOR_CTRL_EVENT;
+		else
+			mgmt_color = COLOR_CTRL_EVENT_UNKNOWN;
+		mgmt_str = mgmt_data->str;
+	} else {
+		mgmt_color = COLOR_CTRL_EVENT_UNKNOWN;
+		mgmt_str = "Unknown";
+	}
+
+	sprintf(extra_str, "(0x%4.4x) plen %d", opcode, size);
+
+	print_packet(tv, cred, '@', index, channel, mgmt_color,
+					"MGMT Event", mgmt_str, extra_str);
+
+	if (!mgmt_data || !mgmt_data->func) {
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (mgmt_data->fixed) {
+		if (size != mgmt_data->size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data, size);
+			return;
+		}
+	} else {
+		if (size < mgmt_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data, size);
+			return;
+		}
+	}
+
+	mgmt_data->func(data, size);
+}
+
 void packet_todo(void)
 {
 	int i;
diff --git a/monitor/packet.h b/monitor/packet.h
index 30dfe38..a02ea50 100644
--- a/monitor/packet.h
+++ b/monitor/packet.h
@@ -90,4 +90,13 @@
 void packet_hci_scodata(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);
+void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size);
+void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size);
+void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size);
+
 void packet_todo(void);
diff --git a/monitor/uuid.h b/monitor/uuid.h
index 6ffc0ee..22d2363 100644
--- a/monitor/uuid.h
+++ b/monitor/uuid.h
@@ -24,6 +24,8 @@
 
 #include <stdint.h>
 
+#define MAX_LEN_UUID_STR 37
+
 const char *uuid16_to_str(uint16_t uuid);
 const char *uuid32_to_str(uint32_t uuid);
 const char *uuidstr_to_str(const char *uuid);
diff --git a/obexd/client/bluetooth.c b/obexd/client/bluetooth.c
index be007de..e35124a 100644
--- a/obexd/client/bluetooth.c
+++ b/obexd/client/bluetooth.c
@@ -443,8 +443,8 @@
 {
 	int sk = g_io_channel_unix_get_fd(io);
 	int type;
-	int omtu = -1;
-	int imtu = -1;
+	uint16_t omtu = BT_TX_MTU;
+	uint16_t imtu = BT_RX_MTU;
 	socklen_t len = sizeof(int);
 
 	DBG("");
diff --git a/obexd/plugins/bluetooth.c b/obexd/plugins/bluetooth.c
index d8b872a..3ee5432 100644
--- a/obexd/plugins/bluetooth.c
+++ b/obexd/plugins/bluetooth.c
@@ -75,8 +75,8 @@
 	struct bluetooth_profile *profile = user_data;
 	struct obex_server *server = profile->server;
 	int type;
-	int omtu = BT_TX_MTU;
-	int imtu = BT_RX_MTU;
+	uint16_t omtu = BT_TX_MTU;
+	uint16_t imtu = BT_RX_MTU;
 	gboolean stream = TRUE;
 	socklen_t len = sizeof(int);
 
diff --git a/obexd/plugins/pbap.c b/obexd/plugins/pbap.c
index bab691c..ad93208 100644
--- a/obexd/plugins/pbap.c
+++ b/obexd/plugins/pbap.c
@@ -543,13 +543,18 @@
 
 	} else if (g_ascii_strcasecmp(type, VCARDLISTING_TYPE) == 0) {
 		/* Always relative */
-		if (!name || strlen(name) == 0)
+		if (!name || strlen(name) == 0) {
 			/* Current folder */
 			path = g_strdup(pbap->folder);
-		else
+		} else {
 			/* Current folder + relative path */
 			path = g_build_filename(pbap->folder, name, NULL);
 
+			/* clear cache */
+			pbap->cache.valid = FALSE;
+			pbap->cache.index = 0;
+			cache_clear(&pbap->cache);
+		}
 	} else if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) == 0) {
 		/* File name only */
 		path = g_strdup(name);
diff --git a/plugins/policy.c b/plugins/policy.c
index 218a3ed..c9a7c84 100644
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -95,10 +95,29 @@
 	uint8_t tg_retries;
 };
 
+static struct reconnect_data *reconnect_find(struct btd_device *dev)
+{
+	GSList *l;
+
+	for (l = reconnects; l; l = g_slist_next(l)) {
+		struct reconnect_data *reconnect = l->data;
+
+		if (reconnect->dev == dev)
+			return reconnect;
+	}
+
+	return NULL;
+}
+
 static void policy_connect(struct policy_data *data,
 						struct btd_service *service)
 {
 	struct btd_profile *profile = btd_service_get_profile(service);
+	struct reconnect_data *reconnect;
+
+	reconnect = reconnect_find(btd_service_get_device(service));
+	if (reconnect && reconnect->active)
+		return;
 
 	DBG("%s profile %s", device_get_path(data->dev), profile->name);
 
@@ -496,6 +515,7 @@
 static void reconnect_reset(struct reconnect_data *reconnect)
 {
 	reconnect->attempt = 0;
+	reconnect->active = false;
 
 	if (reconnect->timer > 0) {
 		g_source_remove(reconnect->timer);
@@ -518,20 +538,6 @@
 	return false;
 }
 
-static struct reconnect_data *reconnect_find(struct btd_device *dev)
-{
-	GSList *l;
-
-	for (l = reconnects; l; l = g_slist_next(l)) {
-		struct reconnect_data *reconnect = l->data;
-
-		if (reconnect->dev == dev)
-			return reconnect;
-	}
-
-	return NULL;
-}
-
 static struct reconnect_data *reconnect_add(struct btd_service *service)
 {
 	struct btd_device *dev = btd_service_get_device(service);
@@ -643,7 +649,6 @@
 	 */
 	reconnect = reconnect_add(service);
 
-	reconnect->active = false;
 	reconnect_reset(reconnect);
 
 	/*
@@ -675,7 +680,6 @@
 		return FALSE;
 	}
 
-	reconnect->active = true;
 	reconnect->attempt++;
 
 	return FALSE;
@@ -685,12 +689,13 @@
 {
 	static int timeout = 0;
 
-	reconnect->attempt++;
+	reconnect->active = true;
 
 	if (reconnect->attempt < reconnect_intervals_len)
 		timeout = reconnect_intervals[reconnect->attempt];
 
-	DBG("%d seconds", timeout);
+	DBG("attempt %u/%zu %d seconds", reconnect->attempt + 1,
+						reconnect_attempts, timeout);
 
 	reconnect->timer = g_timeout_add_seconds(timeout, reconnect_timeout,
 								reconnect);
@@ -709,6 +714,8 @@
 	if (!reconnect || !reconnect->reconnect)
 		return;
 
+	reconnect_reset(reconnect);
+
 	DBG("Device %s identified for auto-reconnection",
 							device_get_path(dev));
 
@@ -728,8 +735,6 @@
 	if (!reconnect->active)
 		return;
 
-	reconnect->active = false;
-
 	/* Give up if we were powered off */
 	if (status == MGMT_STATUS_NOT_POWERED) {
 		reconnect_reset(reconnect);
@@ -777,6 +782,8 @@
 		goto done;
 	}
 
+	g_key_file_set_list_separator(conf, ',');
+
 	reconnect_uuids = g_key_file_get_string_list(conf, "Policy",
 							"ReconnectUUIDs",
 							NULL, &gerr);
diff --git a/profiles/alert/server.c b/profiles/alert/server.c
deleted file mode 100644
index 2f6e3cd..0000000
--- a/profiles/alert/server.c
+++ /dev/null
@@ -1,1044 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2011  Nokia Corporation
- *  Copyright (C) 2011  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
-
-#include <stdbool.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/hci.h"
-#include "lib/hci_lib.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "gdbus/gdbus.h"
-
-#include "src/plugin.h"
-#include "src/dbus-common.h"
-#include "attrib/att.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "attrib/att-database.h"
-#include "src/log.h"
-#include "attrib/gatt-service.h"
-#include "attrib/gattrib.h"
-#include "src/attrib-server.h"
-#include "attrib/gatt.h"
-#include "src/profile.h"
-#include "src/error.h"
-#include "src/textfile.h"
-#include "src/attio.h"
-
-#define PHONE_ALERT_STATUS_SVC_UUID	0x180E
-#define ALERT_NOTIF_SVC_UUID		0x1811
-
-#define ALERT_STATUS_CHR_UUID		0x2A3F
-#define RINGER_CP_CHR_UUID		0x2A40
-#define RINGER_SETTING_CHR_UUID		0x2A41
-
-#define ALERT_NOTIF_CP_CHR_UUID		0x2A44
-#define UNREAD_ALERT_CHR_UUID		0x2A45
-#define NEW_ALERT_CHR_UUID		0x2A46
-#define SUPP_NEW_ALERT_CAT_CHR_UUID	0x2A47
-#define SUPP_UNREAD_ALERT_CAT_CHR_UUID	0x2A48
-
-#define ALERT_OBJECT_PATH		"/org/bluez"
-#define ALERT_INTERFACE			"org.bluez.Alert1"
-#define ALERT_AGENT_INTERFACE		"org.bluez.AlertAgent1"
-
-/* Maximum length for "Text String Information" */
-#define NEW_ALERT_MAX_INFO_SIZE		18
-/* Maximum length for New Alert Characteristic Value */
-#define NEW_ALERT_CHR_MAX_VALUE_SIZE	(NEW_ALERT_MAX_INFO_SIZE + 2)
-
-enum {
-	ENABLE_NEW_INCOMING,
-	ENABLE_UNREAD_CAT,
-	DISABLE_NEW_INCOMING,
-	DISABLE_UNREAD_CAT,
-	NOTIFY_NEW_INCOMING,
-	NOTIFY_UNREAD_CAT,
-};
-
-enum {
-	RINGER_SILENT_MODE = 1,
-	RINGER_MUTE_ONCE,
-	RINGER_CANCEL_SILENT_MODE,
-};
-
-/* Ringer Setting characteristic values */
-enum {
-	RINGER_SILENT,
-	RINGER_NORMAL,
-};
-
-enum notify_type {
-	NOTIFY_RINGER_SETTING = 0,
-	NOTIFY_ALERT_STATUS,
-	NOTIFY_NEW_ALERT,
-	NOTIFY_UNREAD_ALERT,
-	NOTIFY_SIZE,
-};
-
-struct alert_data {
-	const char *category;
-	char *srv;
-	char *path;
-	guint watcher;
-};
-
-struct alert_adapter {
-	struct btd_adapter *adapter;
-	uint16_t supp_new_alert_cat_handle;
-	uint16_t supp_unread_alert_cat_handle;
-	uint16_t hnd_ccc[NOTIFY_SIZE];
-	uint16_t hnd_value[NOTIFY_SIZE];
-};
-
-struct notify_data {
-	struct alert_adapter *al_adapter;
-	enum notify_type type;
-	uint8_t *value;
-	size_t len;
-};
-
-struct notify_callback {
-	struct notify_data *notify_data;
-	struct btd_device *device;
-	guint id;
-};
-
-static GSList *registered_alerts = NULL;
-static GSList *alert_adapters = NULL;
-static uint8_t ringer_setting = RINGER_NORMAL;
-static uint8_t alert_status = 0;
-
-static const char * const anp_categories[] = {
-	"simple",
-	"email",
-	"news",
-	"call",
-	"missed-call",
-	"sms-mms",
-	"voice-mail",
-	"schedule",
-	"high-priority",
-	"instant-message",
-};
-
-static const char * const pasp_categories[] = {
-	"ringer",
-	"vibrate",
-	"display",
-};
-
-static int adapter_cmp(gconstpointer a, gconstpointer b)
-{
-	const struct alert_adapter *al_adapter = a;
-	const struct btd_adapter *adapter = b;
-
-	return al_adapter->adapter == adapter ? 0 : -1;
-}
-
-static struct alert_adapter *find_alert_adapter(struct btd_adapter *adapter)
-{
-	GSList *l = g_slist_find_custom(alert_adapters, adapter, adapter_cmp);
-
-	return l ? l->data : NULL;
-}
-
-static void alert_data_destroy(gpointer user_data)
-{
-	struct alert_data *alert = user_data;
-
-	if (alert->watcher)
-		g_dbus_remove_watch(btd_get_dbus_connection(), alert->watcher);
-
-	g_free(alert->srv);
-	g_free(alert->path);
-	g_free(alert);
-}
-
-static void alert_release(gpointer user_data)
-{
-	struct alert_data *alert = user_data;
-	DBusMessage *msg;
-
-	msg = dbus_message_new_method_call(alert->srv, alert->path,
-							ALERT_AGENT_INTERFACE,
-							"Release");
-	if (msg)
-		g_dbus_send_message(btd_get_dbus_connection(), msg);
-
-	alert_data_destroy(alert);
-}
-
-static void alert_destroy(gpointer user_data)
-{
-	DBG("");
-
-	g_slist_free_full(registered_alerts, alert_release);
-	registered_alerts = NULL;
-}
-
-static const char *valid_category(const char *category)
-{
-	unsigned i;
-
-	for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
-		if (g_str_equal(anp_categories[i], category))
-			return anp_categories[i];
-	}
-
-	for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
-		if (g_str_equal(pasp_categories[i], category))
-			return pasp_categories[i];
-	}
-
-	return NULL;
-}
-
-static struct alert_data *get_alert_data_by_category(const char *category)
-{
-	GSList *l;
-	struct alert_data *alert;
-
-	for (l = registered_alerts; l; l = g_slist_next(l)) {
-		alert = l->data;
-		if (g_str_equal(alert->category, category))
-			return alert;
-	}
-
-	return NULL;
-}
-
-static gboolean registered_category(const char *category)
-{
-	struct alert_data *alert;
-
-	alert = get_alert_data_by_category(category);
-	if (alert)
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean pasp_category(const char *category)
-{
-	unsigned i;
-
-	for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++)
-		if (g_str_equal(category, pasp_categories[i]))
-			return TRUE;
-
-	return FALSE;
-}
-
-static gboolean valid_description(const char *category,
-						const char *description)
-{
-	if (!pasp_category(category)) {
-		if (strlen(description) >= NEW_ALERT_MAX_INFO_SIZE)
-			return FALSE;
-
-		return TRUE;
-	}
-
-	if (g_str_equal(description, "active") ||
-					g_str_equal(description, "not active"))
-		return TRUE;
-
-	if (g_str_equal(category, "ringer"))
-		if (g_str_equal(description, "enabled") ||
-					g_str_equal(description, "disabled"))
-			return TRUE;
-
-	return FALSE;
-}
-
-static gboolean valid_count(const char *category, uint16_t count)
-{
-	if (!pasp_category(category) && count > 0 && count <= 255)
-		return TRUE;
-
-	if (pasp_category(category) && count == 1)
-		return TRUE;
-
-	return FALSE;
-}
-
-static void update_supported_categories(gpointer data, gpointer user_data)
-{
-	struct alert_adapter *al_adapter = data;
-	struct btd_adapter *adapter = al_adapter->adapter;
-	uint8_t value[2];
-	unsigned int i;
-
-	memset(value, 0, sizeof(value));
-
-	for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
-		if (registered_category(anp_categories[i]))
-			hci_set_bit(i, value);
-	}
-
-	attrib_db_update(adapter, al_adapter->supp_new_alert_cat_handle, NULL,
-						value, sizeof(value), NULL);
-
-	/* FIXME: For now report all registered categories as supporting unread
-	 * status, until it is known which ones should be supported */
-	attrib_db_update(adapter, al_adapter->supp_unread_alert_cat_handle,
-					NULL, value, sizeof(value), NULL);
-}
-
-static void watcher_disconnect(DBusConnection *conn, void *user_data)
-{
-	struct alert_data *alert = user_data;
-
-	DBG("Category %s was disconnected", alert->category);
-
-	registered_alerts = g_slist_remove(registered_alerts, alert);
-	alert_data_destroy(alert);
-
-	g_slist_foreach(alert_adapters, update_supported_categories, NULL);
-}
-
-static gboolean is_notifiable_device(struct btd_device *device, uint16_t ccc)
-{
-	char *filename;
-	GKeyFile *key_file;
-	char handle[6];
-	char *str;
-	uint16_t val;
-	gboolean result;
-
-	sprintf(handle, "%hu", ccc);
-
-	filename = btd_device_get_storage_path(device, "ccc");
-	if (!filename) {
-		warn("Unable to get ccc storage path for device");
-		return FALSE;
-	}
-
-	key_file = g_key_file_new();
-	g_key_file_load_from_file(key_file, filename, 0, NULL);
-
-	str = g_key_file_get_string(key_file, handle, "Value", NULL);
-	if (!str) {
-		result = FALSE;
-		goto end;
-	}
-
-	val = strtol(str, NULL, 16);
-	if (!(val & 0x0001)) {
-		result = FALSE;
-		goto end;
-	}
-
-	result = TRUE;
-end:
-	g_free(str);
-	g_free(filename);
-	g_key_file_free(key_file);
-
-	return result;
-}
-
-static void destroy_notify_callback(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	struct notify_callback *cb = user_data;
-
-	DBG("status=%#x", status);
-
-	btd_device_remove_attio_callback(cb->device, cb->id);
-	btd_device_unref(cb->device);
-	g_free(cb->notify_data->value);
-	g_free(cb->notify_data);
-	g_free(cb);
-}
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
-	struct notify_callback *cb = user_data;
-	struct notify_data *nd = cb->notify_data;
-	enum notify_type type = nd->type;
-	struct alert_adapter *al_adapter = nd->al_adapter;
-	size_t len;
-	uint8_t *pdu = g_attrib_get_buffer(attrib, &len);
-
-
-	switch (type) {
-	case NOTIFY_RINGER_SETTING:
-		len = enc_notification(al_adapter->hnd_value[type],
-				&ringer_setting, sizeof(ringer_setting),
-				pdu, len);
-		break;
-	case NOTIFY_ALERT_STATUS:
-		len = enc_notification(al_adapter->hnd_value[type],
-				&alert_status, sizeof(alert_status),
-				pdu, len);
-		break;
-	case NOTIFY_NEW_ALERT:
-	case NOTIFY_UNREAD_ALERT:
-		len = enc_notification(al_adapter->hnd_value[type],
-					nd->value, nd->len, pdu, len);
-		break;
-	case NOTIFY_SIZE:
-	default:
-		DBG("Unknown type, could not send notification");
-		goto end;
-	}
-
-	DBG("Send notification for handle: 0x%04x, ccc: 0x%04x",
-					al_adapter->hnd_value[type],
-					al_adapter->hnd_ccc[type]);
-
-	g_attrib_send(attrib, 0, pdu, len, destroy_notify_callback, cb, NULL);
-
-	return;
-
-end:
-	btd_device_remove_attio_callback(cb->device, cb->id);
-	btd_device_unref(cb->device);
-	g_free(cb->notify_data->value);
-	g_free(cb->notify_data);
-	g_free(cb);
-}
-
-static void filter_devices_notify(struct btd_device *device, void *user_data)
-{
-	struct notify_data *notify_data = user_data;
-	struct alert_adapter *al_adapter = notify_data->al_adapter;
-	enum notify_type type = notify_data->type;
-	struct notify_callback *cb;
-
-	if (!is_notifiable_device(device, al_adapter->hnd_ccc[type]))
-		return;
-
-	cb = g_new0(struct notify_callback, 1);
-	cb->notify_data = notify_data;
-	cb->device = btd_device_ref(device);
-	cb->id = btd_device_add_attio_callback(device,
-						attio_connected_cb, NULL, cb);
-}
-
-static void notify_devices(struct alert_adapter *al_adapter,
-			enum notify_type type, uint8_t *value, size_t len)
-{
-	struct notify_data *notify_data;
-
-	notify_data = g_new0(struct notify_data, 1);
-	notify_data->al_adapter = al_adapter;
-	notify_data->type = type;
-	notify_data->value = g_memdup(value, len);
-	notify_data->len = len;
-
-	btd_adapter_for_each_device(al_adapter->adapter, filter_devices_notify,
-					notify_data);
-}
-
-static void pasp_notification(enum notify_type type)
-{
-	GSList *it;
-	struct alert_adapter *al_adapter;
-
-	for (it = alert_adapters; it; it = g_slist_next(it)) {
-		al_adapter = it->data;
-
-		notify_devices(al_adapter, type, NULL, 0);
-	}
-}
-
-static DBusMessage *register_alert(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	const char *sender = dbus_message_get_sender(msg);
-	char *path;
-	const char *category;
-	const char *c;
-	struct alert_data *alert;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &c,
-			DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	category = valid_category(c);
-	if (!category) {
-		DBG("Invalid category: %s", c);
-		return btd_error_invalid_args(msg);
-	}
-
-	if (registered_category(category)) {
-		DBG("Category %s already registered", category);
-		return dbus_message_new_method_return(msg);
-	}
-
-	alert = g_new0(struct alert_data, 1);
-	alert->srv = g_strdup(sender);
-	alert->path = g_strdup(path);
-	alert->category = category;
-	alert->watcher = g_dbus_add_disconnect_watch(conn, alert->srv,
-					watcher_disconnect, alert, NULL);
-
-	if (alert->watcher == 0) {
-		alert_data_destroy(alert);
-		DBG("Could not register disconnect watcher");
-		return btd_error_failed(msg,
-				"Could not register disconnect watcher");
-	}
-
-	registered_alerts = g_slist_append(registered_alerts, alert);
-
-	g_slist_foreach(alert_adapters, update_supported_categories, NULL);
-
-	DBG("RegisterAlert(\"%s\", \"%s\")", alert->category, alert->path);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static void update_new_alert(gpointer data, gpointer user_data)
-{
-	struct alert_adapter *al_adapter = data;
-	struct btd_adapter *adapter = al_adapter->adapter;
-	uint8_t *value = user_data;
-
-	attrib_db_update(adapter, al_adapter->hnd_value[NOTIFY_NEW_ALERT], NULL,
-						&value[1], value[0], NULL);
-
-	notify_devices(al_adapter, NOTIFY_NEW_ALERT, &value[1], value[0]);
-}
-
-static void update_phone_alerts(const char *category, const char *description)
-{
-	unsigned int i;
-
-	if (g_str_equal(category, "ringer")) {
-		if (g_str_equal(description, "enabled")) {
-			ringer_setting = RINGER_NORMAL;
-			pasp_notification(NOTIFY_RINGER_SETTING);
-			return;
-		} else if (g_str_equal(description, "disabled")) {
-			ringer_setting = RINGER_SILENT;
-			pasp_notification(NOTIFY_RINGER_SETTING);
-			return;
-		}
-	}
-
-	for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
-		if (g_str_equal(pasp_categories[i], category)) {
-			if (g_str_equal(description, "active")) {
-				alert_status |= (1 << i);
-				pasp_notification(NOTIFY_ALERT_STATUS);
-			} else if (g_str_equal(description, "not active")) {
-				alert_status &= ~(1 << i);
-				pasp_notification(NOTIFY_ALERT_STATUS);
-			}
-			break;
-		}
-	}
-}
-
-static DBusMessage *new_alert(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	const char *sender = dbus_message_get_sender(msg);
-	const char *category, *description;
-	struct alert_data *alert;
-	uint16_t count;
-	unsigned int i;
-	size_t dlen;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
-			DBUS_TYPE_UINT16, &count, DBUS_TYPE_STRING,
-			&description, DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	alert = get_alert_data_by_category(category);
-	if (!alert) {
-		DBG("Category %s not registered", category);
-		return btd_error_invalid_args(msg);
-	}
-
-	if (!g_str_equal(alert->srv, sender)) {
-		DBG("Sender %s is not registered in category %s", sender,
-								category);
-		return btd_error_invalid_args(msg);
-	}
-
-	if (!valid_description(category, description)) {
-		DBG("Description %s is invalid for %s category",
-							description, category);
-		return btd_error_invalid_args(msg);
-	}
-
-	if (!valid_count(category, count)) {
-		DBG("Count %d is invalid for %s category", count, category);
-		return btd_error_invalid_args(msg);
-	}
-
-	dlen = strlen(description);
-
-	for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
-		uint8_t value[NEW_ALERT_CHR_MAX_VALUE_SIZE + 1];
-		uint8_t *ptr = value;
-
-		if (!g_str_equal(anp_categories[i], category))
-			continue;
-
-		memset(value, 0, sizeof(value));
-
-		*ptr++ = 2; /* Attribute value size */
-		*ptr++ = i; /* Category ID (mandatory) */
-		*ptr++ = count; /* Number of New Alert (mandatory) */
-		/* Text String Information (optional) */
-		strncpy((char *) ptr, description,
-						NEW_ALERT_MAX_INFO_SIZE - 1);
-
-		if (dlen > 0)
-			*value += dlen + 1;
-
-		g_slist_foreach(alert_adapters, update_new_alert, value);
-	}
-
-	if (pasp_category(category))
-		update_phone_alerts(category, description);
-
-	DBG("NewAlert(\"%s\", %d, \"%s\")", category, count, description);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static int agent_ringer_mute_once(void)
-{
-	struct alert_data *alert;
-	DBusMessage *msg;
-
-	alert = get_alert_data_by_category("ringer");
-	if (!alert) {
-		DBG("Category ringer is not registered");
-		return -EINVAL;
-	}
-
-	msg = dbus_message_new_method_call(alert->srv, alert->path,
-					ALERT_AGENT_INTERFACE, "MuteOnce");
-	if (!msg)
-		return -ENOMEM;
-
-	dbus_message_set_no_reply(msg, TRUE);
-	g_dbus_send_message(btd_get_dbus_connection(), msg);
-
-	return 0;
-}
-
-static int agent_ringer_set_ringer(const char *mode)
-{
-	struct alert_data *alert;
-	DBusMessage *msg;
-
-	alert = get_alert_data_by_category("ringer");
-	if (!alert) {
-		DBG("Category ringer is not registered");
-		return -EINVAL;
-	}
-
-	msg = dbus_message_new_method_call(alert->srv, alert->path,
-					ALERT_AGENT_INTERFACE, "SetRinger");
-	if (!msg)
-		return -ENOMEM;
-
-	dbus_message_append_args(msg, DBUS_TYPE_STRING, &mode,
-							DBUS_TYPE_INVALID);
-
-	dbus_message_set_no_reply(msg, TRUE);
-	g_dbus_send_message(btd_get_dbus_connection(), msg);
-
-	return 0;
-}
-
-static void update_unread_alert(gpointer data, gpointer user_data)
-{
-	struct alert_adapter *al_adapter = data;
-	struct btd_adapter *adapter = al_adapter->adapter;
-	uint8_t *value = user_data;
-
-	attrib_db_update(adapter,
-			al_adapter->hnd_value[NOTIFY_UNREAD_ALERT], NULL, value,
-			2, NULL);
-
-	notify_devices(al_adapter, NOTIFY_UNREAD_ALERT, value, 2);
-}
-
-static DBusMessage *unread_alert(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	const char *sender = dbus_message_get_sender(msg);
-	struct alert_data *alert;
-	const char *category;
-	unsigned int i;
-	uint16_t count;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
-						DBUS_TYPE_UINT16, &count,
-						DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	alert = get_alert_data_by_category(category);
-	if (!alert) {
-		DBG("Category %s not registered", category);
-		return btd_error_invalid_args(msg);
-	}
-
-	if (!valid_count(category, count)) {
-		DBG("Count %d is invalid for %s category", count, category);
-		return btd_error_invalid_args(msg);
-	}
-
-	if (!g_str_equal(alert->srv, sender)) {
-		DBG("Sender %s is not registered in category %s", sender,
-								category);
-		return btd_error_invalid_args(msg);
-	}
-
-	for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
-		if (g_str_equal(anp_categories[i], category)) {
-			uint8_t value[2];
-
-			value[0] = i; /* Category ID */
-			value[1] = count; /* Unread count */
-
-			g_slist_foreach(alert_adapters, update_unread_alert,
-									value);
-		}
-	}
-
-	DBG("category %s, count %d", category, count);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static uint8_t ringer_cp_write(struct attribute *a,
-						struct btd_device *device,
-						gpointer user_data)
-{
-	DBG("a = %p", a);
-
-	if (a->len > 1) {
-		DBG("Invalid command size (%zu)", a->len);
-		return 0;
-	}
-
-	switch (a->data[0]) {
-	case RINGER_SILENT_MODE:
-		DBG("Silent Mode");
-		agent_ringer_set_ringer("disabled");
-		break;
-	case RINGER_MUTE_ONCE:
-		DBG("Mute Once");
-		agent_ringer_mute_once();
-		break;
-	case RINGER_CANCEL_SILENT_MODE:
-		DBG("Cancel Silent Mode");
-		agent_ringer_set_ringer("enabled");
-		break;
-	default:
-		DBG("Invalid command (0x%02x)", a->data[0]);
-	}
-
-	return 0;
-}
-
-static uint8_t alert_status_read(struct attribute *a,
-						struct btd_device *device,
-						gpointer user_data)
-{
-	struct btd_adapter *adapter = user_data;
-
-	DBG("a = %p", a);
-
-	if (a->data == NULL || a->data[0] != alert_status)
-		attrib_db_update(adapter, a->handle, NULL, &alert_status,
-						sizeof(alert_status), NULL);
-
-	return 0;
-}
-
-static uint8_t ringer_setting_read(struct attribute *a,
-						struct btd_device *device,
-						gpointer user_data)
-{
-	struct btd_adapter *adapter = user_data;
-
-	DBG("a = %p", a);
-
-	if (a->data == NULL || a->data[0] != ringer_setting)
-		attrib_db_update(adapter, a->handle, NULL, &ringer_setting,
-						sizeof(ringer_setting), NULL);
-
-	return 0;
-}
-
-static void register_phone_alert_service(struct alert_adapter *al_adapter)
-{
-	bt_uuid_t uuid;
-
-	bt_uuid16_create(&uuid, PHONE_ALERT_STATUS_SVC_UUID);
-
-	/* Phone Alert Status Service */
-	gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
-			/* Alert Status characteristic */
-			GATT_OPT_CHR_UUID16, ALERT_STATUS_CHR_UUID,
-			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
-							GATT_CHR_PROP_NOTIFY,
-			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
-			alert_status_read, al_adapter->adapter,
-			GATT_OPT_CCC_GET_HANDLE,
-			&al_adapter->hnd_ccc[NOTIFY_ALERT_STATUS],
-			GATT_OPT_CHR_VALUE_GET_HANDLE,
-			&al_adapter->hnd_value[NOTIFY_ALERT_STATUS],
-			/* Ringer Control Point characteristic */
-			GATT_OPT_CHR_UUID16, RINGER_CP_CHR_UUID,
-			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE_WITHOUT_RESP,
-			GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
-			ringer_cp_write, NULL,
-			/* Ringer Setting characteristic */
-			GATT_OPT_CHR_UUID16, RINGER_SETTING_CHR_UUID,
-			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
-							GATT_CHR_PROP_NOTIFY,
-			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
-			ringer_setting_read, al_adapter->adapter,
-			GATT_OPT_CCC_GET_HANDLE,
-			&al_adapter->hnd_ccc[NOTIFY_RINGER_SETTING],
-			GATT_OPT_CHR_VALUE_GET_HANDLE,
-			&al_adapter->hnd_value[NOTIFY_RINGER_SETTING],
-			GATT_OPT_INVALID);
-}
-
-static uint8_t supp_new_alert_cat_read(struct attribute *a,
-						struct btd_device *device,
-						gpointer user_data)
-{
-	struct btd_adapter *adapter = user_data;
-	uint8_t value[] = { 0x00, 0x00 };
-
-	DBG("a = %p", a);
-
-	if (a->data == NULL)
-		attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
-									NULL);
-
-	return 0;
-}
-
-static uint8_t supp_unread_alert_cat_read(struct attribute *a,
-						struct btd_device *device,
-						gpointer user_data)
-{
-	struct btd_adapter *adapter = user_data;
-	uint8_t value[] = { 0x00, 0x00 };
-
-	DBG("a = %p", a);
-
-	if (a->data == NULL)
-		attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
-									NULL);
-
-	return 0;
-}
-
-static uint8_t alert_notif_cp_write(struct attribute *a,
-						struct btd_device *device,
-						gpointer user_data)
-{
-	DBG("a = %p", a);
-
-	if (a->len < 2)
-		return 0;
-
-	switch (a->data[0]) {
-	case ENABLE_NEW_INCOMING:
-		DBG("ENABLE_NEW_INCOMING: 0x%02x", a->data[1]);
-		break;
-	case ENABLE_UNREAD_CAT:
-		DBG("ENABLE_UNREAD_CAT: 0x%02x", a->data[1]);
-		break;
-	case DISABLE_NEW_INCOMING:
-		DBG("DISABLE_NEW_INCOMING: 0x%02x", a->data[1]);
-		break;
-	case DISABLE_UNREAD_CAT:
-		DBG("DISABLE_UNREAD_CAT: 0x%02x", a->data[1]);
-		break;
-	case NOTIFY_NEW_INCOMING:
-		DBG("NOTIFY_NEW_INCOMING: 0x%02x", a->data[1]);
-		break;
-	case NOTIFY_UNREAD_CAT:
-		DBG("NOTIFY_UNREAD_CAT: 0x%02x", a->data[1]);
-		break;
-	default:
-		DBG("0x%02x 0x%02x", a->data[0], a->data[1]);
-	}
-
-	return 0;
-}
-
-static void register_alert_notif_service(struct alert_adapter *al_adapter)
-{
-	bt_uuid_t uuid;
-
-	bt_uuid16_create(&uuid, ALERT_NOTIF_SVC_UUID);
-
-	/* Alert Notification Service */
-	gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
-			/* Supported New Alert Category */
-			GATT_OPT_CHR_UUID16, SUPP_NEW_ALERT_CAT_CHR_UUID,
-			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
-			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
-			supp_new_alert_cat_read, al_adapter->adapter,
-			GATT_OPT_CHR_VALUE_GET_HANDLE,
-			&al_adapter->supp_new_alert_cat_handle,
-			/* New Alert */
-			GATT_OPT_CHR_UUID16, NEW_ALERT_CHR_UUID,
-			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
-			GATT_OPT_CCC_GET_HANDLE,
-			&al_adapter->hnd_ccc[NOTIFY_NEW_ALERT],
-			GATT_OPT_CHR_VALUE_GET_HANDLE,
-			&al_adapter->hnd_value[NOTIFY_NEW_ALERT],
-			/* Supported Unread Alert Category */
-			GATT_OPT_CHR_UUID16, SUPP_UNREAD_ALERT_CAT_CHR_UUID,
-			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
-			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
-			supp_unread_alert_cat_read, al_adapter->adapter,
-			GATT_OPT_CHR_VALUE_GET_HANDLE,
-			&al_adapter->supp_unread_alert_cat_handle,
-			/* Unread Alert Status */
-			GATT_OPT_CHR_UUID16, UNREAD_ALERT_CHR_UUID,
-			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
-			GATT_OPT_CCC_GET_HANDLE,
-			&al_adapter->hnd_ccc[NOTIFY_UNREAD_ALERT],
-			GATT_OPT_CHR_VALUE_GET_HANDLE,
-			&al_adapter->hnd_value[NOTIFY_UNREAD_ALERT],
-			/* Alert Notification Control Point */
-			GATT_OPT_CHR_UUID16, ALERT_NOTIF_CP_CHR_UUID,
-			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE,
-			GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
-			alert_notif_cp_write, NULL,
-			GATT_OPT_INVALID);
-}
-
-static int alert_server_probe(struct btd_profile *p,
-						struct btd_adapter *adapter)
-{
-	struct alert_adapter *al_adapter;
-
-	al_adapter = g_new0(struct alert_adapter, 1);
-	al_adapter->adapter = btd_adapter_ref(adapter);
-
-	alert_adapters = g_slist_append(alert_adapters, al_adapter);
-
-	register_phone_alert_service(al_adapter);
-	register_alert_notif_service(al_adapter);
-
-	return 0;
-}
-
-static void alert_server_remove(struct btd_profile *p,
-						struct btd_adapter *adapter)
-{
-	struct alert_adapter *al_adapter;
-
-	al_adapter = find_alert_adapter(adapter);
-	if (!al_adapter)
-		return;
-
-	alert_adapters = g_slist_remove(alert_adapters, al_adapter);
-	btd_adapter_unref(al_adapter->adapter);
-
-	g_free(al_adapter);
-}
-
-static struct btd_profile alert_profile = {
-	.name = "gatt-alert-server",
-	.adapter_probe = alert_server_probe,
-	.adapter_remove = alert_server_remove,
-};
-
-static const GDBusMethodTable alert_methods[] = {
-	{ GDBUS_METHOD("RegisterAlert",
-			GDBUS_ARGS({ "category", "s" },
-				   { "agent", "o" }), NULL,
-			register_alert) },
-	{ GDBUS_METHOD("NewAlert",
-			GDBUS_ARGS({ "category", "s" },
-				   { "count", "q" },
-				   { "description", "s" }), NULL,
-			new_alert) },
-	{ GDBUS_METHOD("UnreadAlert",
-			GDBUS_ARGS({ "category", "s" }, { "count", "q" }), NULL,
-			unread_alert) },
-	{ }
-};
-
-static int alert_server_init(void)
-{
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
-					ALERT_OBJECT_PATH, ALERT_INTERFACE,
-					alert_methods, NULL, NULL, NULL,
-					alert_destroy)) {
-		error("D-Bus failed to register %s interface",
-							ALERT_INTERFACE);
-		return -EIO;
-	}
-
-	return btd_profile_register(&alert_profile);
-}
-
-static void alert_server_exit(void)
-{
-	btd_profile_unregister(&alert_profile);
-
-	g_dbus_unregister_interface(btd_get_dbus_connection(),
-					ALERT_OBJECT_PATH, ALERT_INTERFACE);
-}
-
-static int alert_init(void)
-{
-	return alert_server_init();
-}
-
-static void alert_exit(void)
-{
-	alert_server_exit();
-}
-
-BLUETOOTH_PLUGIN_DEFINE(alert, VERSION,
-			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
-			alert_init, alert_exit)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index b391fc2..db0736d 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -235,11 +235,11 @@
 	if (avdtp_error_category(err) != AVDTP_ERRNO)
 		return -EIO;
 
-	perr = -avdtp_error_posix_errno(err);
-	switch (-perr) {
-	case -EHOSTDOWN:
-	case -ECONNABORTED:
-		return perr;
+	perr = avdtp_error_posix_errno(err);
+	switch (perr) {
+	case EHOSTDOWN:
+	case ECONNABORTED:
+		return -perr;
 	default:
 		/*
 		 * An unexpect error has occurred setup may be attempted again.
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index c100149..51a89b1 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2793,6 +2793,83 @@
 					avrcp_player_value_rsp, session);
 }
 
+static gboolean avrcp_set_addressed_player_rsp(struct avctp *conn, uint8_t code,
+					uint8_t subunit, uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct avrcp_header *pdu = (void *) operands;
+
+	if (!pdu || code != AVC_CTYPE_ACCEPTED)
+		return FALSE;
+
+	player->addressed = true;
+
+	return FALSE;
+}
+
+static void avrcp_set_addressed_player(struct avrcp *session,
+						struct avrcp_player *player)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 2];
+	struct avrcp_header *pdu = (void *) buf;
+	uint16_t id;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_SET_ADDRESSED_PLAYER;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	id = htons(player->id);
+	memcpy(pdu->params, &id, 2);
+	pdu->params_len = htons(2);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+					AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+					avrcp_set_addressed_player_rsp,
+					session);
+}
+
+static void set_addressed_player(struct avrcp *session,
+					struct avrcp_player *player)
+{
+	if (!player || !player->id || player->addressed ||
+				session->controller->version < 0x0104)
+		return;
+
+	/* Set player as addressed */
+	avrcp_set_addressed_player(session, player);
+}
+
+static void set_browsed_player(struct avrcp *session,
+					struct avrcp_player *player)
+{
+	if (!player || !player->id || player->browsed)
+		return;
+
+	if (media_player_get_browsable(player->user_data))
+		avrcp_set_browsed_player(session, player);
+}
+
+static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
+{
+	struct btd_service *service;
+
+	if (session->controller->player == player)
+		goto done;
+
+	session->controller->player = player;
+	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+	control_set_player(service, player ?
+			media_player_get_path(player->user_data) : NULL);
+
+done:
+	set_addressed_player(session, player);
+	set_browsed_player(session, player);
+}
+
 static bool ct_set_setting(struct media_player *mp, const char *key,
 					const char *value, void *user_data)
 {
@@ -2808,6 +2885,8 @@
 	if (session->controller->version < 0x0103)
 		return false;
 
+	set_ct_player(session, player);
+
 	attr = attr_to_val(key);
 	if (attr < 0)
 		return false;
@@ -2830,6 +2909,8 @@
 	if (session == NULL)
 		return -ENOTCONN;
 
+	set_ct_player(session, player);
+
 	err = avctp_send_passthrough(session->conn, op);
 	if (err < 0)
 		return err;
@@ -2898,6 +2979,8 @@
 
 	session = player->sessions->data;
 
+	set_ct_player(session, player);
+
 	if (g_str_has_prefix(name, "/NowPlaying"))
 		player->scope = 0x03;
 	else if (g_str_has_suffix(name, "/search"))
@@ -2942,6 +3025,7 @@
 	uint8_t direction;
 
 	session = player->sessions->data;
+	set_ct_player(session, player);
 	player->change_path = g_strdup(path);
 
 	direction = g_str_has_prefix(path, player->path) ? 0x01 : 0x00;
@@ -3008,6 +3092,7 @@
 
 	session = player->sessions->data;
 
+	set_ct_player(session, player);
 	avrcp_search(session, string);
 
 	return 0;
@@ -3048,9 +3133,12 @@
 		return -EBUSY;
 
 	session = player->sessions->data;
+	set_ct_player(session, player);
 
 	if (g_strrstr(name, "/NowPlaying"))
 		player->scope = 0x03;
+	else if (g_strrstr(name, "/Search"))
+		player->scope = 0x02;
 	else
 		player->scope = 0x01;
 
@@ -3100,6 +3188,7 @@
 	else
 		player->scope = 0x01;
 
+	set_ct_player(session, player);
 	avrcp_add_to_nowplaying(session, uid);
 
 	return 0;
@@ -3159,6 +3248,7 @@
 	struct avrcp *session;
 
 	session = player->sessions->data;
+	set_ct_player(session, player);
 
 	if (session->controller->version != 0x0106) {
 		error("version not supported");
@@ -3194,16 +3284,6 @@
 	.total_items = ct_get_total_numberofitems,
 };
 
-static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
-{
-	struct btd_service *service;
-
-	session->controller->player = player;
-	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
-	control_set_player(service, player ?
-			media_player_get_path(player->user_data) : NULL);
-}
-
 static struct avrcp_player *create_ct_player(struct avrcp *session,
 								uint16_t id)
 {
@@ -3212,6 +3292,7 @@
 	const char *path;
 
 	player = g_new0(struct avrcp_player, 1);
+	player->id = id;
 	player->sessions = g_slist_prepend(player->sessions, session);
 
 	path = device_get_path(session->dev);
@@ -3303,8 +3384,8 @@
 		media_player_set_name(mp, name);
 	}
 
-	if (session->controller->player == player && !player->browsed)
-		avrcp_set_browsed_player(session, player);
+	if (player->addressed)
+		set_browsed_player(session, player);
 
 	return player;
 }
@@ -3523,6 +3604,7 @@
 			return;
 	}
 
+	player->addressed = true;
 	player->uid_counter = get_be16(&pdu->params[3]);
 	set_ct_player(session, player);
 
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 4736396..7944b49 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1102,10 +1102,8 @@
 };
 
 static const GDBusPropertyTable media_folder_properties[] = {
-	{ "Name", "s", get_folder_name, NULL, folder_name_exists,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "NumberOfItems", "u", get_items, NULL, items_exists,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Name", "s", get_folder_name, NULL, folder_name_exists },
+	{ "NumberOfItems", "u", get_items, NULL, items_exists },
 	{ }
 };
 
@@ -1406,9 +1404,14 @@
 					"Browsable");
 }
 
+bool media_player_get_browsable(struct media_player *mp)
+{
+	return mp->browsable;
+}
+
 void media_player_set_searchable(struct media_player *mp, bool enabled)
 {
-	if (mp->browsable == enabled)
+	if (mp->searchable == enabled)
 		return;
 
 	DBG("%s", enabled ? "true" : "false");
@@ -1480,12 +1483,15 @@
 	struct media_item *item = data;
 	struct media_player *mp = item->player;
 	struct player_callback *cb = mp->cb;
+	const char *path;
 	int err;
 
 	if (!item->playable || !cb->cbs->play_item)
 		return btd_error_not_supported(msg);
 
-	err = cb->cbs->play_item(mp, item->path, item->uid, cb->user_data);
+	path = mp->search && mp->scope == mp->search ? "/Search" : item->path;
+
+	err = cb->cbs->play_item(mp, path, item->uid, cb->user_data);
 	if (err < 0)
 		return btd_error_failed(msg, strerror(-err));
 
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 4ad8bfe..54e395a 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -85,6 +85,7 @@
 void media_player_set_subtype(struct media_player *mp, const char *subtype);
 void media_player_set_name(struct media_player *mp, const char *name);
 void media_player_set_browsable(struct media_player *mp, bool enabled);
+bool media_player_get_browsable(struct media_player *mp);
 void media_player_set_searchable(struct media_player *mp, bool enabled);
 void media_player_set_folder(struct media_player *mp, const char *path,
 								uint32_t items);
diff --git a/profiles/audioOverBle/btvoice.c b/profiles/audioOverBle/btvoice.c
index fd90b63..d6f192b 100644
--- a/profiles/audioOverBle/btvoice.c
+++ b/profiles/audioOverBle/btvoice.c
@@ -51,11 +51,11 @@
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 #include "src/adapter.h"
-#include "src/attio.h"
 #include "src/dbus-common.h"
 #include "src/device.h"
 #include "src/error.h"
 #include "src/log.h"
+#include "src/service.h"
 #include "src/shared/util.h"
 #include "src/textfile.h"
 
@@ -70,7 +70,6 @@
   bdaddr_t bdaddr;
   struct btd_device *device;
   GAttrib *attrib;
-  guint attioid;
   struct gatt_primary *audioOverBle;
   struct btvoiceenable voiceenable;
   GSList *reports;
@@ -452,20 +451,26 @@
 }
 
 
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data) {
-  struct btvoice_device *btvoicedev = user_data;
+int btvoice_accept(struct btd_service *service) {
+  struct btvoice_device *btvoicedev = btd_service_get_user_data(service);
   struct gatt_primary *prim = btvoicedev->audioOverBle;
+  struct btd_device *device = btd_service_get_device(service);
+  GAttrib *attrib = btd_device_get_attrib(device);
 
   btvoicedev->attrib = g_attrib_ref(attrib);
 
+  btd_service_connecting_complete(service, 0);
+
   if (btvoicedev->voiceenable.audioOverBle) {
     gatt_discover_char(btvoicedev->attrib, prim->range.start, prim->range.end,
                        NULL, char_discovered_cb, btvoicedev);
   }
+
+  return 0;
 }
 
-static void attio_disconnected_cb(gpointer user_data) {
-  struct btvoice_device *btvoicedev = user_data;
+int btvoice_disconnect(struct btd_service *service) {
+  struct btvoice_device *btvoicedev = btd_service_get_user_data(service);
   GSList *l;
 
   for (l = btvoicedev->reports; l; l = l->next) {
@@ -476,10 +481,14 @@
 
   g_attrib_unref(btvoicedev->attrib);
   btvoicedev->attrib = NULL;
+
+  btd_service_disconnecting_complete(service, 0);
+
+  return 0;
 }
 
 static struct btvoice_device *register_btvoicedevice(
-    struct btd_device *device)
+    struct btd_service *service, struct btd_device *device)
 {
   struct btvoice_device *btvoicedev;
   const bdaddr_t *bdaddr = device_get_address(device);
@@ -493,38 +502,27 @@
   btvoicedev->device = btd_device_ref(device);
   btvoicedev->bdaddr = *bdaddr;
 
+  btd_service_set_user_data(service, btvoicedev);
   btvoicedevices = g_slist_append(btvoicedevices, btvoicedev);
 
   return btvoicedev;
 }
 
-static void update_btvoicedevice(struct btvoice_device *btvoicedev)
-{
-  if (!btvoicedev->voiceenable.audioOverBle) return;
-
-  if (btvoicedev->attioid != 0) return;
-
-  btvoicedev->attioid =
-      btd_device_add_attio_callback(btvoicedev->device, attio_connected_cb,
-                                    attio_disconnected_cb, btvoicedev);
-}
-
-int btvoice_register_audioOverBle(struct btd_device *device,
+int btvoice_register_audioOverBle(struct btd_service *service,
+                                  struct btd_device *device,
                                   struct btvoiceenable *voiceenable,
                                   struct gatt_primary *prim) {
   struct btvoice_device *btvoicedev;
 
   if (!voiceenable->audioOverBle) return 0;
 
-  btvoicedev = register_btvoicedevice(device);
+  btvoicedev = register_btvoicedevice(service, device);
   if (btvoicedev == NULL) return -1;
 
   btvoicedev->id = prim->range.start;
   btvoicedev->audioOverBle = g_memdup(prim, sizeof(*prim));
   btvoicedev->voiceenable.audioOverBle = TRUE;
 
-  update_btvoicedevice(btvoicedev);
-
   return 0;
 }
 
@@ -534,11 +532,6 @@
 
   if (btvoicedev->audioOverBle != NULL) return;
 
-  if (btvoicedev->attioid != 0) {
-    btd_device_remove_attio_callback(device, btvoicedev->attioid);
-    btvoicedev->attioid = 0;
-  }
-
   if (btvoicedev->attrib != NULL) {
     g_attrib_unref(btvoicedev->attrib);
     btvoicedev->attrib = NULL;
diff --git a/profiles/audioOverBle/btvoice.h b/profiles/audioOverBle/btvoice.h
index 0cddfe3..6bf57f5 100644
--- a/profiles/audioOverBle/btvoice.h
+++ b/profiles/audioOverBle/btvoice.h
@@ -31,11 +31,15 @@
 	int	 l2capChannel;
 };
 
-int btvoice_register_audioOverBle(struct btd_device *device,
+int btvoice_register_audioOverBle(struct btd_service *service,
+						struct btd_device *device,
 						struct btvoiceenable *voiceenable,
 						struct gatt_primary *audioOverBle);
 
 
 void btvoice_unregister_audioOverBle(struct btd_device *device);
 
+int btvoice_accept(struct btd_service *service);
+int btvoice_disconnect(struct btd_service *service);
+
 #endif  // BTVOICE_H_
diff --git a/profiles/audioOverBle/manager.c b/profiles/audioOverBle/manager.c
index 7008ac4..b38ed29 100644
--- a/profiles/audioOverBle/manager.c
+++ b/profiles/audioOverBle/manager.c
@@ -67,7 +67,7 @@
 	if (audioOverBlePrim == NULL)
 		return -1;
 
-	return btvoice_register_audioOverBle(device, &voiceenable, audioOverBlePrim);
+	return btvoice_register_audioOverBle(service, device, &voiceenable, audioOverBlePrim);
 }
 
 
@@ -84,6 +84,9 @@
 	.remote_uuid	= AUD_UUID,
 	.device_probe	= btvoice_audioOverBle_probe,
 	.device_remove	= btvoice_audioOverBle_remove,
+	.accept		= btvoice_accept,
+	.disconnect	= btvoice_disconnect,
+	.auto_connect	= true,
 };
 
 static void load_config_file(GKeyFile *config)
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 2c37492..4ec35fc 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -53,7 +53,6 @@
 #include "src/service.h"
 #include "attrib/att.h"
 #include "attrib/gattrib.h"
-#include "src/attio.h"
 #include "attrib/gatt.h"
 #include "src/shared/util.h"
 
@@ -63,7 +62,6 @@
 #define BATTERY_DIR		"/tmp/batteries"
 
 struct service {
-	guint			attio_id;
 	uint16_t		value_handle;
 	int			value;
 };
@@ -72,7 +70,6 @@
 	uint16_t		id;
 	struct btd_device	*device;
 	GAttrib			*attrib;
-	guint			attioid;
 	struct gatt_primary	*batt_primary;
 
 	struct service		level;
@@ -225,17 +222,20 @@
 	}
 }
 
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+static int batt_accept(struct btd_service *service)
 {
-	struct batt_device *battdev = user_data;
+	struct batt_device *battdev = btd_service_get_user_data(service);
 	struct gatt_primary *prim = battdev->batt_primary;
-	GSList *l;
+	struct btd_device *device = btd_service_get_device(service);
+	GAttrib *attrib = btd_device_get_attrib(device);
 
 	DBG("BATT connected");
 
 	battdev->attrib = g_attrib_ref(attrib);
 	battdev->level.value = -1;
 
+	btd_service_connecting_complete(service, 0);
+
 	if (battdev->level.value_handle == 0) {
 		DBG("BATT discovering characteristics");
 
@@ -245,22 +245,24 @@
 	} else {
 		check_level(battdev);
 	}
+
+	return 0;
 }
 
-static void attio_disconnected_cb(gpointer user_data)
+static int batt_disconnect(struct btd_service *service)
 {
-	struct batt_device *battdev = user_data;
-	GSList *l;
+	struct batt_device *battdev = btd_service_get_user_data(service);
 
 	DBG("BATT disconnected");
 
-	if (battdev->level.attio_id > 0) {
-		g_attrib_unregister(battdev->attrib, battdev->level.attio_id);
-		battdev->level.attio_id = 0;
-	}
-	if (battdev->attrib)
+	if (battdev->attrib) {
 		g_attrib_unref(battdev->attrib);
-	battdev->attrib = NULL;
+		battdev->attrib = NULL;
+	}
+
+	btd_service_disconnecting_complete(service, 0);
+
+	return 0;
 }
 
 static struct batt_device *batt_register_device(struct btd_device *device,
@@ -276,17 +278,11 @@
 
 	battdev->batt_primary = g_memdup(prim, sizeof(*prim));
 
-	battdev->attioid = btd_device_add_attio_callback(device,
-							attio_connected_cb,
-							attio_disconnected_cb,
-							battdev);
-
 	return battdev;
 }
 
 static int batt_unregister_device(struct batt_device *battdev)
 {
-	btd_device_remove_attio_callback(battdev->device, battdev->attioid);
 	batt_free_device(battdev);
 
 	return 0;
@@ -317,6 +313,7 @@
 		if (battdev == NULL)
 			continue;
 
+		btd_service_set_user_data(service, battdev);
 		devices = g_slist_append(devices, battdev);
 	}
 
@@ -350,6 +347,9 @@
 	.remote_uuid	= BATT_UUID,
 	.device_probe	= batt_probe,
 	.device_remove	= batt_remove,
+	.accept		= batt_accept,
+	.disconnect	= batt_disconnect,
+	.auto_connect	= true,
 };
 
 static int batt_init(void)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
deleted file mode 100644
index e447725..0000000
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2012 Tieto Poland
- *
- *  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
-
-#include <errno.h>
-#include <stdbool.h>
-
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "gdbus/gdbus.h"
-
-#include "src/plugin.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/service.h"
-#include "src/dbus-common.h"
-#include "src/shared/util.h"
-#include "src/error.h"
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-#include "src/attio.h"
-#include "src/log.h"
-
-/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
-#define ATT_HDR_LEN 3
-
-#define ATT_TIMEOUT 30
-
-#define CYCLINGSPEED_INTERFACE		"org.bluez.CyclingSpeed1"
-#define CYCLINGSPEED_MANAGER_INTERFACE	"org.bluez.CyclingSpeedManager1"
-#define CYCLINGSPEED_WATCHER_INTERFACE	"org.bluez.CyclingSpeedWatcher1"
-
-#define WHEEL_REV_SUPPORT		0x01
-#define CRANK_REV_SUPPORT		0x02
-#define MULTI_SENSOR_LOC_SUPPORT	0x04
-
-#define WHEEL_REV_PRESENT	0x01
-#define CRANK_REV_PRESENT	0x02
-
-#define SET_CUMULATIVE_VALUE		0x01
-#define START_SENSOR_CALIBRATION	0x02
-#define UPDATE_SENSOR_LOC		0x03
-#define REQUEST_SUPPORTED_SENSOR_LOC	0x04
-#define RESPONSE_CODE			0x10
-
-#define RSP_SUCCESS		0x01
-#define RSP_NOT_SUPPORTED	0x02
-#define RSP_INVALID_PARAM	0x03
-#define RSP_FAILED		0x04
-
-struct csc;
-
-struct controlpoint_req {
-	struct csc		*csc;
-	uint8_t			opcode;
-	guint			timeout;
-	GDBusPendingReply	reply_id;
-	DBusMessage		*msg;
-
-	uint8_t			pending_location;
-};
-
-struct csc_adapter {
-	struct btd_adapter	*adapter;
-	GSList			*devices;	/* list of registered devices */
-	GSList			*watchers;
-};
-
-struct csc {
-	struct btd_device	*dev;
-	struct csc_adapter	*cadapter;
-
-	GAttrib			*attrib;
-	guint			attioid;
-	/* attio id for measurement characteristics value notifications */
-	guint			attio_measurement_id;
-	/* attio id for SC Control Point characteristics value indications */
-	guint			attio_controlpoint_id;
-
-	struct att_range	*svc_range;
-
-	uint16_t		measurement_ccc_handle;
-	uint16_t		controlpoint_val_handle;
-
-	uint16_t		feature;
-	gboolean		has_location;
-	uint8_t			location;
-	uint8_t			num_locations;
-	uint8_t			*locations;
-
-	struct controlpoint_req	*pending_req;
-};
-
-struct watcher {
-	struct csc_adapter	*cadapter;
-	guint			id;
-	char			*srv;
-	char			*path;
-};
-
-struct measurement {
-	struct csc	*csc;
-
-	bool		has_wheel_rev;
-	uint32_t	wheel_rev;
-	uint16_t	last_wheel_time;
-
-	bool		has_crank_rev;
-	uint16_t	crank_rev;
-	uint16_t	last_crank_time;
-};
-
-struct characteristic {
-	struct csc	*csc;
-	char		uuid[MAX_LEN_UUID_STR + 1];
-};
-
-static GSList *csc_adapters = NULL;
-
-static const char * const location_enum[] = {
-	"other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank",
-	"right-crank", "left-pedal", "right-pedal", "front-hub",
-	"rear-dropout", "chainstay", "rear-wheel", "rear-hub"
-};
-
-static const char *location2str(uint8_t value)
-{
-	if (value < G_N_ELEMENTS(location_enum))
-		return location_enum[value];
-
-	info("Body Sensor Location [%d] is RFU", value);
-
-	return location_enum[0];
-}
-
-static int str2location(const char *location)
-{
-	size_t i;
-
-	for (i = 0; i < G_N_ELEMENTS(location_enum); i++)
-		if (!strcmp(location_enum[i], location))
-			return i;
-
-	return -1;
-}
-
-static int cmp_adapter(gconstpointer a, gconstpointer b)
-{
-	const struct csc_adapter *cadapter = a;
-	const struct btd_adapter *adapter = b;
-
-	if (adapter == cadapter->adapter)
-		return 0;
-
-	return -1;
-}
-
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
-	const struct csc *csc = a;
-	const struct btd_device *dev = b;
-
-	if (dev == csc->dev)
-		return 0;
-
-	return -1;
-}
-
-static int cmp_watcher(gconstpointer a, gconstpointer b)
-{
-	const struct watcher *watcher = a;
-	const struct watcher *match = b;
-	int ret;
-
-	ret = g_strcmp0(watcher->srv, match->srv);
-	if (ret != 0)
-		return ret;
-
-	return g_strcmp0(watcher->path, match->path);
-}
-
-static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
-{
-	GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
-
-	if (!l)
-		return NULL;
-
-	return l->data;
-}
-
-static void destroy_watcher(gpointer user_data)
-{
-	struct watcher *watcher = user_data;
-
-	g_free(watcher->path);
-	g_free(watcher->srv);
-	g_free(watcher);
-}
-
-static struct watcher *find_watcher(GSList *list, const char *sender,
-							const char *path)
-{
-	struct watcher *match;
-	GSList *l;
-
-	match = g_new0(struct watcher, 1);
-	match->srv = g_strdup(sender);
-	match->path = g_strdup(path);
-
-	l = g_slist_find_custom(list, match, cmp_watcher);
-	destroy_watcher(match);
-
-	if (l != NULL)
-		return l->data;
-
-	return NULL;
-}
-
-static void remove_watcher(gpointer user_data)
-{
-	struct watcher *watcher = user_data;
-
-	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-}
-
-static void destroy_csc_adapter(gpointer user_data)
-{
-	struct csc_adapter *cadapter = user_data;
-
-	g_slist_free_full(cadapter->watchers, remove_watcher);
-
-	g_free(cadapter);
-}
-
-static void destroy_csc(gpointer user_data)
-{
-	struct csc *csc = user_data;
-
-	if (csc->attioid > 0)
-		btd_device_remove_attio_callback(csc->dev, csc->attioid);
-
-	if (csc->attrib != NULL) {
-		g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
-		g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
-		g_attrib_unref(csc->attrib);
-	}
-
-	btd_device_unref(csc->dev);
-	g_free(csc->svc_range);
-	g_free(csc->locations);
-	g_free(csc);
-}
-
-static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	char *msg = user_data;
-
-	if (status != 0)
-		error("%s failed", msg);
-
-	g_free(msg);
-}
-
-static gboolean controlpoint_timeout(gpointer user_data)
-{
-	struct controlpoint_req *req = user_data;
-
-	if (req->opcode == UPDATE_SENSOR_LOC) {
-		g_dbus_pending_property_error(req->reply_id,
-						ERROR_INTERFACE ".Failed",
-						"Operation failed (timeout)");
-	} else if (req->opcode == SET_CUMULATIVE_VALUE) {
-		DBusMessage *reply;
-
-		reply = btd_error_failed(req->msg,
-						"Operation failed (timeout)");
-
-		g_dbus_send_message(btd_get_dbus_connection(), reply);
-
-		dbus_message_unref(req->msg);
-	}
-
-	req->csc->pending_req = NULL;
-	g_free(req);
-
-	return FALSE;
-}
-
-static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	struct controlpoint_req *req = user_data;
-
-	if (status == 0) {
-		req->timeout = g_timeout_add_seconds(ATT_TIMEOUT,
-							controlpoint_timeout,
-							req);
-		return;
-	}
-
-	error("SC Control Point write failed (opcode=%d)", req->opcode);
-
-	if (req->opcode == UPDATE_SENSOR_LOC) {
-		g_dbus_pending_property_error(req->reply_id,
-					ERROR_INTERFACE ".Failed",
-					"Operation failed (%d)", status);
-	} else if  (req->opcode == SET_CUMULATIVE_VALUE) {
-		DBusMessage *reply;
-
-		reply = btd_error_failed(req->msg, "Operation failed");
-
-		g_dbus_send_message(btd_get_dbus_connection(), reply);
-
-		dbus_message_unref(req->msg);
-	}
-
-	req->csc->pending_req = NULL;
-	g_free(req);
-}
-
-static void read_supported_locations(struct csc *csc)
-{
-	struct controlpoint_req *req;
-
-	req = g_new0(struct controlpoint_req, 1);
-	req->csc = csc;
-	req->opcode = REQUEST_SUPPORTED_SENSOR_LOC;
-
-	csc->pending_req = req;
-
-	gatt_write_char(csc->attrib, csc->controlpoint_val_handle,
-					&req->opcode, sizeof(req->opcode),
-					controlpoint_write_cb, req);
-}
-
-static void read_feature_cb(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	struct csc *csc = user_data;
-	uint8_t value[2];
-	ssize_t vlen;
-
-	if (status) {
-		error("CSC Feature read failed: %s", att_ecode2str(status));
-		return;
-	}
-
-	vlen = dec_read_resp(pdu, len, value, sizeof(value));
-	if (vlen < 0) {
-		error("Protocol error");
-		return;
-	}
-
-	if (vlen != sizeof(value)) {
-		error("Invalid value length for CSC Feature");
-		return;
-	}
-
-	csc->feature = get_le16(value);
-
-	if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT)
-						&& (csc->locations == NULL))
-		read_supported_locations(csc);
-}
-
-static void read_location_cb(guint8 status, const guint8 *pdu,
-						guint16 len, gpointer user_data)
-{
-	struct csc *csc = user_data;
-	uint8_t value;
-	ssize_t vlen;
-
-	if (status) {
-		error("Sensor Location read failed: %s", att_ecode2str(status));
-		return;
-	}
-
-	vlen = dec_read_resp(pdu, len, &value, sizeof(value));
-	if (vlen < 0) {
-		error("Protocol error");
-		return;
-	}
-
-	if (vlen != sizeof(value)) {
-		error("Invalid value length for Sensor Location");
-		return;
-	}
-
-	csc->has_location = TRUE;
-	csc->location = value;
-
-	g_dbus_emit_property_changed(btd_get_dbus_connection(),
-					device_get_path(csc->dev),
-					CYCLINGSPEED_INTERFACE, "Location");
-}
-
-static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
-{
-	struct characteristic *ch = user_data;
-	struct gatt_desc *desc;
-	uint8_t attr_val[2];
-	char *msg = NULL;
-
-	if (status != 0) {
-		error("Discover %s descriptors failed: %s", ch->uuid,
-							att_ecode2str(status));
-		goto done;
-	}
-
-	/* There will be only one descriptor on list and it will be CCC */
-	desc = descs->data;
-
-	if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
-		ch->csc->measurement_ccc_handle = desc->handle;
-
-		if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
-			put_le16(0x0000, attr_val);
-			msg = g_strdup("Disable measurement");
-		} else {
-			put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
-							attr_val);
-			msg = g_strdup("Enable measurement");
-		}
-	} else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
-		put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
-		msg = g_strdup("Enable SC Control Point indications");
-	} else {
-		goto done;
-	}
-
-	gatt_write_char(ch->csc->attrib, desc->handle, attr_val,
-					sizeof(attr_val), char_write_cb, msg);
-
-done:
-	g_free(ch);
-}
-
-static void discover_desc(struct csc *csc, struct gatt_char *c,
-						struct gatt_char *c_next)
-{
-	struct characteristic *ch;
-	uint16_t start, end;
-	bt_uuid_t uuid;
-
-	start = c->value_handle + 1;
-
-	if (c_next != NULL) {
-		if (start == c_next->handle)
-			return;
-		end = c_next->handle - 1;
-	} else if (c->value_handle != csc->svc_range->end) {
-		end = csc->svc_range->end;
-	} else {
-		return;
-	}
-
-	ch = g_new0(struct characteristic, 1);
-	ch->csc = csc;
-	memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
-
-	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
-	gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb,
-									ch);
-}
-
-static void update_watcher(gpointer data, gpointer user_data)
-{
-	struct watcher *w = data;
-	struct measurement *m = user_data;
-	struct csc *csc = m->csc;
-	const char *path = device_get_path(csc->dev);
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	DBusMessage *msg;
-
-	msg = dbus_message_new_method_call(w->srv, w->path,
-			CYCLINGSPEED_WATCHER_INTERFACE, "MeasurementReceived");
-	if (msg == NULL)
-		return;
-
-	dbus_message_iter_init_append(msg, &iter);
-
-	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
-
-	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);
-
-	if (m->has_wheel_rev) {
-		dict_append_entry(&dict, "WheelRevolutions",
-					DBUS_TYPE_UINT32, &m->wheel_rev);
-		dict_append_entry(&dict, "LastWheelEventTime",
-					DBUS_TYPE_UINT16, &m->last_wheel_time);
-	}
-
-	if (m->has_crank_rev) {
-		dict_append_entry(&dict, "CrankRevolutions",
-					DBUS_TYPE_UINT16, &m->crank_rev);
-		dict_append_entry(&dict, "LastCrankEventTime",
-					DBUS_TYPE_UINT16, &m->last_crank_time);
-	}
-
-	dbus_message_iter_close_container(&iter, &dict);
-
-	dbus_message_set_no_reply(msg, TRUE);
-	g_dbus_send_message(btd_get_dbus_connection(), msg);
-}
-
-static void process_measurement(struct csc *csc, const uint8_t *pdu,
-								uint16_t len)
-{
-	struct measurement m;
-	uint8_t flags;
-
-	flags = *pdu;
-
-	pdu++;
-	len--;
-
-	memset(&m, 0, sizeof(m));
-
-	if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) {
-		if (len < 6) {
-			error("Wheel revolutions data fields missing");
-			return;
-		}
-
-		m.has_wheel_rev = true;
-		m.wheel_rev = get_le32(pdu);
-		m.last_wheel_time = get_le16(pdu + 4);
-		pdu += 6;
-		len -= 6;
-	}
-
-	if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) {
-		if (len < 4) {
-			error("Crank revolutions data fields missing");
-			return;
-		}
-
-		m.has_crank_rev = true;
-		m.crank_rev = get_le16(pdu);
-		m.last_crank_time = get_le16(pdu + 2);
-		pdu += 4;
-		len -= 4;
-	}
-
-	/* Notify all registered watchers */
-	m.csc = csc;
-	g_slist_foreach(csc->cadapter->watchers, update_watcher, &m);
-}
-
-static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
-							gpointer user_data)
-{
-	struct csc *csc = user_data;
-
-	/* should be at least opcode (1b) + handle (2b) */
-	if (len < 3) {
-		error("Invalid PDU received");
-		return;
-	}
-
-	process_measurement(csc, pdu + 3, len - 3);
-}
-
-static void controlpoint_property_reply(struct controlpoint_req *req,
-								uint8_t code)
-{
-	switch (code) {
-	case RSP_SUCCESS:
-		g_dbus_pending_property_success(req->reply_id);
-		break;
-
-	case RSP_NOT_SUPPORTED:
-		g_dbus_pending_property_error(req->reply_id,
-					ERROR_INTERFACE ".NotSupported",
-					"Feature is not supported");
-		break;
-
-	case RSP_INVALID_PARAM:
-		g_dbus_pending_property_error(req->reply_id,
-					ERROR_INTERFACE ".InvalidArguments",
-					"Invalid arguments in method call");
-		break;
-
-	case RSP_FAILED:
-		g_dbus_pending_property_error(req->reply_id,
-					ERROR_INTERFACE ".Failed",
-					"Operation failed");
-		break;
-
-	default:
-		g_dbus_pending_property_error(req->reply_id,
-					ERROR_INTERFACE ".Failed",
-					"Operation failed (%d)", code);
-		break;
-	}
-}
-
-static void controlpoint_method_reply(struct controlpoint_req *req,
-								uint8_t code)
-{
-	DBusMessage *reply;
-
-	switch (code) {
-	case RSP_SUCCESS:
-		reply = dbus_message_new_method_return(req->msg);
-		break;
-	case RSP_NOT_SUPPORTED:
-		reply = btd_error_not_supported(req->msg);
-		break;
-	case RSP_INVALID_PARAM:
-		reply = btd_error_invalid_args(req->msg);
-		break;
-	case RSP_FAILED:
-		reply = btd_error_failed(req->msg, "Failed");
-		break;
-	default:
-		reply = btd_error_failed(req->msg, "Unknown error");
-		break;
-	}
-
-	g_dbus_send_message(btd_get_dbus_connection(), reply);
-
-	dbus_message_unref(req->msg);
-}
-
-static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
-							gpointer user_data)
-{
-	struct csc *csc = user_data;
-	struct controlpoint_req *req = csc->pending_req;
-	uint8_t opcode;
-	uint8_t req_opcode;
-	uint8_t rsp_code;
-	uint8_t *opdu;
-	uint16_t olen;
-	size_t plen;
-
-	if (len < ATT_HDR_LEN) {
-		error("Invalid PDU received");
-		return;
-	}
-
-	/* skip ATT header */
-	pdu += ATT_HDR_LEN;
-	len -= ATT_HDR_LEN;
-
-	if (len < 1) {
-		error("Op Code missing");
-		goto done;
-	}
-
-	opcode = *pdu;
-	pdu++;
-	len--;
-
-	if (opcode != RESPONSE_CODE) {
-		DBG("Unsupported Op Code received (%d)", opcode);
-		goto done;
-	}
-
-	if (len < 2) {
-		error("Invalid Response Code PDU received");
-		goto done;
-	}
-
-	req_opcode = *pdu;
-	rsp_code = *(pdu + 1);
-	pdu += 2;
-	len -= 2;
-
-	if (req == NULL || req->opcode != req_opcode) {
-		DBG("Indication received without pending request");
-		goto done;
-	}
-
-	switch (req->opcode) {
-	case SET_CUMULATIVE_VALUE:
-		controlpoint_method_reply(req, rsp_code);
-		break;
-
-	case REQUEST_SUPPORTED_SENSOR_LOC:
-		if (rsp_code == RSP_SUCCESS) {
-			csc->num_locations = len;
-			csc->locations = g_memdup(pdu, len);
-		} else {
-			error("Failed to read Supported Sendor Locations");
-		}
-		break;
-
-	case UPDATE_SENSOR_LOC:
-		csc->location = req->pending_location;
-
-		controlpoint_property_reply(req, rsp_code);
-
-		g_dbus_emit_property_changed(btd_get_dbus_connection(),
-					device_get_path(csc->dev),
-					CYCLINGSPEED_INTERFACE, "Location");
-		break;
-	}
-
-	csc->pending_req = NULL;
-	g_source_remove(req->timeout);
-	g_free(req);
-
-done:
-	opdu = g_attrib_get_buffer(csc->attrib, &plen);
-	olen = enc_confirmation(opdu, plen);
-	if (olen > 0)
-		g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
-}
-
-static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
-{
-	struct csc *csc = user_data;
-	uint16_t feature_val_handle = 0;
-
-	if (status) {
-		error("Discover CSCS characteristics: %s",
-							att_ecode2str(status));
-		return;
-	}
-
-	for (; chars; chars = chars->next) {
-		struct gatt_char *c = chars->data;
-		struct gatt_char *c_next =
-				(chars->next ? chars->next->data : NULL);
-
-		if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
-			csc->attio_measurement_id =
-				g_attrib_register(csc->attrib,
-					ATT_OP_HANDLE_NOTIFY, c->value_handle,
-					measurement_notify_handler, csc, NULL);
-
-			discover_desc(csc, c, c_next);
-		} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
-			feature_val_handle = c->value_handle;
-		} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
-			DBG("Sensor Location supported");
-			gatt_read_char(csc->attrib, c->value_handle,
-							read_location_cb, csc);
-		} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
-			DBG("SC Control Point supported");
-			csc->controlpoint_val_handle = c->value_handle;
-
-			csc->attio_controlpoint_id = g_attrib_register(
-					csc->attrib, ATT_OP_HANDLE_IND,
-					c->value_handle,
-					controlpoint_ind_handler, csc, NULL);
-
-			discover_desc(csc, c, c_next);
-		}
-	}
-
-	if (feature_val_handle > 0)
-		gatt_read_char(csc->attrib, feature_val_handle,
-							read_feature_cb, csc);
-}
-
-static void enable_measurement(gpointer data, gpointer user_data)
-{
-	struct csc *csc = data;
-	uint16_t handle = csc->measurement_ccc_handle;
-	uint8_t value[2];
-	char *msg;
-
-	if (csc->attrib == NULL || !handle)
-		return;
-
-	put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-	msg = g_strdup("Enable measurement");
-
-	gatt_write_char(csc->attrib, handle, value, sizeof(value),
-							char_write_cb, msg);
-}
-
-static void disable_measurement(gpointer data, gpointer user_data)
-{
-	struct csc *csc = data;
-	uint16_t handle = csc->measurement_ccc_handle;
-	uint8_t value[2];
-	char *msg;
-
-	if (csc->attrib == NULL || !handle)
-		return;
-
-	put_le16(0x0000, value);
-	msg = g_strdup("Disable measurement");
-
-	gatt_write_char(csc->attrib, handle, value, sizeof(value),
-							char_write_cb, msg);
-}
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
-	struct csc *csc = user_data;
-
-	DBG("");
-
-	csc->attrib = g_attrib_ref(attrib);
-
-	gatt_discover_char(csc->attrib, csc->svc_range->start,
-						csc->svc_range->end, NULL,
-						discover_char_cb, csc);
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
-	struct csc *csc = user_data;
-
-	DBG("");
-
-	if (csc->attio_measurement_id > 0) {
-		g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
-		csc->attio_measurement_id = 0;
-	}
-
-	if (csc->attio_controlpoint_id > 0) {
-		g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
-		csc->attio_controlpoint_id = 0;
-	}
-
-	g_attrib_unref(csc->attrib);
-	csc->attrib = NULL;
-}
-
-static void watcher_exit_cb(DBusConnection *conn, void *user_data)
-{
-	struct watcher *watcher = user_data;
-	struct csc_adapter *cadapter = watcher->cadapter;
-
-	DBG("cycling watcher [%s] disconnected", watcher->path);
-
-	cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
-	g_dbus_remove_watch(conn, watcher->id);
-
-	if (g_slist_length(cadapter->watchers) == 0)
-		g_slist_foreach(cadapter->devices, disable_measurement, 0);
-}
-
-static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	struct csc_adapter *cadapter = data;
-	struct watcher *watcher;
-	const char *sender = dbus_message_get_sender(msg);
-	char *path;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	watcher = find_watcher(cadapter->watchers, sender, path);
-	if (watcher != NULL)
-		return btd_error_already_exists(msg);
-
-	watcher = g_new0(struct watcher, 1);
-	watcher->cadapter = cadapter;
-	watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
-						watcher, destroy_watcher);
-	watcher->srv = g_strdup(sender);
-	watcher->path = g_strdup(path);
-
-	if (g_slist_length(cadapter->watchers) == 0)
-		g_slist_foreach(cadapter->devices, enable_measurement, 0);
-
-	cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
-
-	DBG("cycling watcher [%s] registered", path);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	struct csc_adapter *cadapter = data;
-	struct watcher *watcher;
-	const char *sender = dbus_message_get_sender(msg);
-	char *path;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	watcher = find_watcher(cadapter->watchers, sender, path);
-	if (watcher == NULL)
-		return btd_error_does_not_exist(msg);
-
-	cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
-	g_dbus_remove_watch(conn, watcher->id);
-
-	if (g_slist_length(cadapter->watchers) == 0)
-		g_slist_foreach(cadapter->devices, disable_measurement, 0);
-
-	DBG("cycling watcher [%s] unregistered", path);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static const GDBusMethodTable cyclingspeed_manager_methods[] = {
-	{ GDBUS_METHOD("RegisterWatcher",
-			GDBUS_ARGS({ "agent", "o" }), NULL,
-			register_watcher) },
-	{ GDBUS_METHOD("UnregisterWatcher",
-			GDBUS_ARGS({ "agent", "o" }), NULL,
-			unregister_watcher) },
-	{ }
-};
-
-static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
-{
-	struct csc_adapter *cadapter;
-
-	cadapter = g_new0(struct csc_adapter, 1);
-	cadapter->adapter = adapter;
-
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
-						adapter_get_path(adapter),
-						CYCLINGSPEED_MANAGER_INTERFACE,
-						cyclingspeed_manager_methods,
-						NULL, NULL, cadapter,
-						destroy_csc_adapter)) {
-		error("D-Bus failed to register %s interface",
-						CYCLINGSPEED_MANAGER_INTERFACE);
-		destroy_csc_adapter(cadapter);
-		return -EIO;
-	}
-
-	csc_adapters = g_slist_prepend(csc_adapters, cadapter);
-
-	return 0;
-}
-
-static void csc_adapter_remove(struct btd_profile *p,
-						struct btd_adapter *adapter)
-{
-	struct csc_adapter *cadapter;
-
-	cadapter = find_csc_adapter(adapter);
-	if (cadapter == NULL)
-		return;
-
-	csc_adapters = g_slist_remove(csc_adapters, cadapter);
-
-	g_dbus_unregister_interface(btd_get_dbus_connection(),
-					adapter_get_path(cadapter->adapter),
-					CYCLINGSPEED_MANAGER_INTERFACE);
-}
-
-static gboolean property_get_location(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct csc *csc = data;
-	const char *loc;
-
-	if (!csc->has_location)
-		return FALSE;
-
-	loc = location2str(csc->location);
-
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
-
-	return TRUE;
-}
-
-static void property_set_location(const GDBusPropertyTable *property,
-					DBusMessageIter *iter,
-					GDBusPendingPropertySet id, void *data)
-{
-	struct csc *csc = data;
-	char *loc;
-	int loc_val;
-	uint8_t att_val[2];
-	struct controlpoint_req *req;
-
-	if (csc->pending_req != NULL) {
-		g_dbus_pending_property_error(id,
-					ERROR_INTERFACE ".InProgress",
-					"Operation already in progress");
-		return;
-	}
-
-	if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT)) {
-		g_dbus_pending_property_error(id,
-					ERROR_INTERFACE ".NotSupported",
-					"Feature is not supported");
-		return;
-	}
-
-	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
-		g_dbus_pending_property_error(id,
-					ERROR_INTERFACE ".InvalidArguments",
-					"Invalid arguments in method call");
-		return;
-	}
-
-	dbus_message_iter_get_basic(iter, &loc);
-
-	loc_val = str2location(loc);
-
-	if (loc_val < 0) {
-		g_dbus_pending_property_error(id,
-					ERROR_INTERFACE ".InvalidArguments",
-					"Invalid arguments in method call");
-		return;
-	}
-
-	req = g_new(struct controlpoint_req, 1);
-	req->csc = csc;
-	req->reply_id = id;
-	req->opcode = UPDATE_SENSOR_LOC;
-	req->pending_location = loc_val;
-
-	csc->pending_req = req;
-
-	att_val[0] = UPDATE_SENSOR_LOC;
-	att_val[1] = loc_val;
-
-	gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
-				sizeof(att_val), controlpoint_write_cb, req);
-}
-
-static gboolean property_exists_location(const GDBusPropertyTable *property,
-								void *data)
-{
-	struct csc *csc = data;
-
-	return csc->has_location;
-}
-
-static gboolean property_get_locations(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct csc *csc = data;
-	DBusMessageIter entry;
-	int i;
-
-	if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
-		return FALSE;
-
-	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
-					DBUS_TYPE_STRING_AS_STRING, &entry);
-	for (i = 0; i < csc->num_locations; i++) {
-		char *loc = g_strdup(location2str(csc->locations[i]));
-		dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc);
-		g_free(loc);
-	}
-
-	dbus_message_iter_close_container(iter, &entry);
-
-	return TRUE;
-}
-
-static gboolean property_exists_locations(const GDBusPropertyTable *property,
-								void *data)
-{
-	struct csc *csc = data;
-
-	return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
-}
-
-static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct csc *csc = data;
-	dbus_bool_t val;
-
-	val = !!(csc->feature & WHEEL_REV_SUPPORT);
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
-
-	return TRUE;
-}
-
-static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct csc *csc = data;
-	dbus_bool_t val;
-
-	val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
-
-	return TRUE;
-}
-
-static const GDBusPropertyTable cyclingspeed_device_properties[] = {
-	{ "Location", "s", property_get_location, property_set_location,
-						property_exists_location },
-	{ "SupportedLocations", "as", property_get_locations, NULL,
-						property_exists_locations },
-	{ "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
-	{ "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
-	{ }
-};
-
-static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
-						DBusMessage *msg, void *data)
-{
-	struct csc *csc = data;
-	dbus_uint32_t value;
-	struct controlpoint_req *req;
-	uint8_t att_val[5]; /* uint8 opcode + uint32 value */
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	if (csc->pending_req != NULL)
-		return btd_error_in_progress(msg);
-
-	req = g_new(struct controlpoint_req, 1);
-	req->csc = csc;
-	req->opcode = SET_CUMULATIVE_VALUE;
-	req->msg = dbus_message_ref(msg);
-
-	csc->pending_req = req;
-
-	att_val[0] = SET_CUMULATIVE_VALUE;
-	put_le32(value, att_val + 1);
-
-	gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
-		sizeof(att_val), controlpoint_write_cb, req);
-
-	return NULL;
-}
-
-static const GDBusMethodTable cyclingspeed_device_methods[] = {
-	{ GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions",
-				GDBUS_ARGS({ "value", "u" }), NULL,
-						set_cumulative_wheel_rev) },
-	{ }
-};
-
-static int csc_device_probe(struct btd_service *service)
-{
-	struct btd_device *device = btd_service_get_device(service);
-	struct btd_adapter *adapter;
-	struct csc_adapter *cadapter;
-	struct csc *csc;
-	struct gatt_primary *prim;
-
-	prim = btd_device_get_primary(device, CYCLING_SC_UUID);
-	if (prim == NULL)
-		return -EINVAL;
-
-	adapter = device_get_adapter(device);
-
-	cadapter = find_csc_adapter(adapter);
-	if (cadapter == NULL)
-		return -1;
-
-	csc = g_new0(struct csc, 1);
-	csc->dev = btd_device_ref(device);
-	csc->cadapter = cadapter;
-
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
-						device_get_path(device),
-						CYCLINGSPEED_INTERFACE,
-						cyclingspeed_device_methods,
-						NULL,
-						cyclingspeed_device_properties,
-						csc, destroy_csc)) {
-		error("D-Bus failed to register %s interface",
-						CYCLINGSPEED_INTERFACE);
-		destroy_csc(csc);
-		return -EIO;
-	}
-
-	csc->svc_range = g_new0(struct att_range, 1);
-	csc->svc_range->start = prim->range.start;
-	csc->svc_range->end = prim->range.end;
-
-	cadapter->devices = g_slist_prepend(cadapter->devices, csc);
-
-	csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
-						attio_disconnected_cb, csc);
-
-	return 0;
-}
-
-static void csc_device_remove(struct btd_service *service)
-{
-	struct btd_device *device = btd_service_get_device(service);
-	struct btd_adapter *adapter;
-	struct csc_adapter *cadapter;
-	struct csc *csc;
-	GSList *l;
-
-	adapter = device_get_adapter(device);
-
-	cadapter = find_csc_adapter(adapter);
-	if (cadapter == NULL)
-		return;
-
-	l = g_slist_find_custom(cadapter->devices, device, cmp_device);
-	if (l == NULL)
-		return;
-
-	csc = l->data;
-
-	cadapter->devices = g_slist_remove(cadapter->devices, csc);
-
-	g_dbus_unregister_interface(btd_get_dbus_connection(),
-						device_get_path(device),
-						CYCLINGSPEED_INTERFACE);
-}
-
-static struct btd_profile cscp_profile = {
-	.name		= "Cycling Speed and Cadence GATT Driver",
-	.remote_uuid	= CYCLING_SC_UUID,
-
-	.adapter_probe	= csc_adapter_probe,
-	.adapter_remove	= csc_adapter_remove,
-
-	.device_probe	= csc_device_probe,
-	.device_remove	= csc_device_remove,
-};
-
-static int cyclingspeed_init(void)
-{
-	return btd_profile_register(&cscp_profile);
-}
-
-static void cyclingspeed_exit(void)
-{
-	btd_profile_unregister(&cscp_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION,
-					BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
-					cyclingspeed_init, cyclingspeed_exit)
diff --git a/profiles/deviceinfo/deviceinfo.c b/profiles/deviceinfo/deviceinfo.c
index 0c48f00..fa94efe 100644
--- a/profiles/deviceinfo/deviceinfo.c
+++ b/profiles/deviceinfo/deviceinfo.c
@@ -111,17 +111,17 @@
 	gatt_db_service_foreach_char(attr, handle_characteristic, device);
 }
 
-static int deviceinfo_driver_probe(struct btd_service *service)
+static int deviceinfo_probe(struct btd_service *service)
 {
 	return 0;
 }
 
-static void deviceinfo_driver_remove(struct btd_service *service)
+static void deviceinfo_remove(struct btd_service *service)
 {
 }
 
 
-static int deviceinfo_driver_accept(struct btd_service *service)
+static int deviceinfo_accept(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
 	struct gatt_db *db = btd_device_get_gatt_db(device);
@@ -136,6 +136,15 @@
 	gatt_db_foreach_service(db, &deviceinfo_uuid,
 					foreach_deviceinfo_service, device);
 
+	btd_service_connecting_complete(service, 0);
+
+	return 0;
+}
+
+static int deviceinfo_disconnect(struct btd_service *service)
+{
+	btd_service_disconnecting_complete(service, 0);
+
 	return 0;
 }
 
@@ -143,9 +152,10 @@
 	.name		= "deviceinfo",
 	.remote_uuid	= DEVICE_INFORMATION_UUID,
 	.external	= true,
-	.device_probe	= deviceinfo_driver_probe,
-	.device_remove	= deviceinfo_driver_remove,
-	.accept		= deviceinfo_driver_accept,
+	.device_probe	= deviceinfo_probe,
+	.device_remove	= deviceinfo_remove,
+	.accept		= deviceinfo_accept,
+	.disconnect	= deviceinfo_disconnect,
 };
 
 static int deviceinfo_init(void)
diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index 35b996c..47c8c25 100644
--- a/profiles/gap/gas.c
+++ b/profiles/gap/gas.c
@@ -57,8 +57,6 @@
 	struct gatt_db_attribute *attr;
 };
 
-static GSList *devices;
-
 static void gas_free(struct gas *gas)
 {
 	gatt_db_unref(gas->db);
@@ -67,14 +65,6 @@
 	g_free(gas);
 }
 
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
-	const struct gas *gas = a;
-	const struct btd_device *device = b;
-
-	return gas->device == device ? 0 : -1;
-}
-
 static char *name2utf8(const uint8_t *name, uint16_t len)
 {
 	char utf8_name[HCI_MAX_NAME_LENGTH + 2];
@@ -205,19 +195,17 @@
 	gatt_db_service_foreach_char(gas->attr, handle_characteristic, gas);
 }
 
-static int gap_driver_probe(struct btd_service *service)
+static int gap_probe(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
-	struct gas *gas;
-	GSList *l;
+	struct gas *gas = btd_service_get_user_data(service);
 	char addr[18];
 
 	ba2str(device_get_address(device), addr);
 	DBG("GAP profile probe (%s)", addr);
 
 	/* Ignore, if we were probed for this device already */
-	l = g_slist_find_custom(devices, device, cmp_device);
-	if (l) {
+	if (gas) {
 		error("Profile probed twice for the same device!");
 		return -1;
 	}
@@ -227,30 +215,26 @@
 		return -1;
 
 	gas->device = btd_device_ref(device);
-	devices = g_slist_append(devices, gas);
+	btd_service_set_user_data(service, gas);
 
 	return 0;
 }
 
-static void gap_driver_remove(struct btd_service *service)
+static void gap_remove(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
 	struct gas *gas;
-	GSList *l;
 	char addr[18];
 
 	ba2str(device_get_address(device), addr);
 	DBG("GAP profile remove (%s)", addr);
 
-	l = g_slist_find_custom(devices, device, cmp_device);
-	if (!l) {
+	gas = btd_service_get_user_data(service);
+	if (!gas) {
 		error("GAP service not handled by profile");
 		return;
 	}
 
-	gas = l->data;
-
-	devices = g_slist_remove(devices, gas);
 	gas_free(gas);
 }
 
@@ -267,32 +251,32 @@
 	handle_gap_service(gas);
 }
 
-static int gap_driver_accept(struct btd_service *service)
+static void gas_reset(struct gas *gas)
+{
+	gas->attr = NULL;
+	gatt_db_unref(gas->db);
+	gas->db = NULL;
+	bt_gatt_client_unref(gas->client);
+	gas->client = NULL;
+}
+
+static int gap_accept(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
 	struct gatt_db *db = btd_device_get_gatt_db(device);
 	struct bt_gatt_client *client = btd_device_get_gatt_client(device);
-	struct gas *gas;
-	GSList *l;
+	struct gas *gas = btd_service_get_user_data(service);
 	char addr[18];
 	bt_uuid_t gap_uuid;
 
 	ba2str(device_get_address(device), addr);
 	DBG("GAP profile accept (%s)", addr);
 
-	l = g_slist_find_custom(devices, device, cmp_device);
-	if (!l) {
+	if (!gas) {
 		error("GAP service not handled by profile");
 		return -1;
 	}
 
-	gas = l->data;
-
-	/* Clean-up any old client/db and acquire the new ones */
-	gas->attr = NULL;
-	gatt_db_unref(gas->db);
-	bt_gatt_client_unref(gas->client);
-
 	gas->db = gatt_db_ref(db);
 	gas->client = bt_gatt_client_ref(client);
 
@@ -300,24 +284,40 @@
 	bt_uuid16_create(&gap_uuid, GAP_UUID16);
 	gatt_db_foreach_service(db, &gap_uuid, foreach_gap_service, gas);
 
+	if (!gas->attr) {
+		error("GAP attribute not found");
+		gas_reset(gas);
+		return -1;
+	}
+
+	btd_service_connecting_complete(service, 0);
+
+	return 0;
+}
+
+static int gap_disconnect(struct btd_service *service)
+{
+	struct gas *gas = btd_service_get_user_data(service);
+
+	gas_reset(gas);
+
+	btd_service_disconnecting_complete(service, 0);
+
 	return 0;
 }
 
 static struct btd_profile gap_profile = {
 	.name		= "gap-profile",
 	.remote_uuid	= GAP_UUID,
-	.device_probe	= gap_driver_probe,
-	.device_remove	= gap_driver_remove,
-	.accept		= gap_driver_accept
+	.device_probe	= gap_probe,
+	.device_remove	= gap_remove,
+	.accept		= gap_accept,
+	.disconnect	= gap_disconnect,
 };
 
 static int gap_init(void)
 {
-	devices = NULL;
-
-	btd_profile_register(&gap_profile);
-
-	return 0;
+	return btd_profile_register(&gap_profile);
 }
 
 static void gap_exit(void)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
deleted file mode 100644
index 9e8c499..0000000
--- a/profiles/heartrate/heartrate.c
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2012 Tieto Poland
- *
- *  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
-
-#include <errno.h>
-#include <stdbool.h>
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "gdbus/gdbus.h"
-
-#include "src/plugin.h"
-#include "src/adapter.h"
-#include "src/dbus-common.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/shared/util.h"
-#include "src/service.h"
-#include "src/error.h"
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-#include "src/attio.h"
-#include "src/log.h"
-
-#define HEART_RATE_INTERFACE		"org.bluez.HeartRate1"
-#define HEART_RATE_MANAGER_INTERFACE	"org.bluez.HeartRateManager1"
-#define HEART_RATE_WATCHER_INTERFACE	"org.bluez.HeartRateWatcher1"
-
-#define HR_VALUE_FORMAT		0x01
-#define SENSOR_CONTACT_DETECTED	0x02
-#define SENSOR_CONTACT_SUPPORT	0x04
-#define ENERGY_EXP_STATUS	0x08
-#define RR_INTERVAL		0x10
-
-struct heartrate_adapter {
-	struct btd_adapter	*adapter;
-	GSList			*devices;
-	GSList			*watchers;
-};
-
-struct heartrate {
-	struct btd_device		*dev;
-	struct heartrate_adapter	*hradapter;
-	GAttrib				*attrib;
-	guint				attioid;
-	guint				attionotid;
-
-	struct att_range		*svc_range;	/* primary svc range */
-
-	uint16_t			measurement_ccc_handle;
-	uint16_t			hrcp_val_handle;
-
-	gboolean			has_location;
-	uint8_t				location;
-};
-
-struct watcher {
-	struct heartrate_adapter	*hradapter;
-	guint				id;
-	char				*srv;
-	char				*path;
-};
-
-struct measurement {
-	struct heartrate	*hr;
-	uint16_t		value;
-	gboolean		has_energy;
-	uint16_t		energy;
-	gboolean		has_contact;
-	gboolean		contact;
-	uint16_t		num_interval;
-	uint16_t		*interval;
-};
-
-static GSList *heartrate_adapters = NULL;
-
-static const char * const location_enum[] = {
-	"other",
-	"chest",
-	"wrist",
-	"finger",
-	"hand",
-	"earlobe",
-	"foot",
-};
-
-static const char *location2str(uint8_t value)
-{
-	 if (value < G_N_ELEMENTS(location_enum))
-		return location_enum[value];
-
-	error("Body Sensor Location [%d] is RFU", value);
-
-	return NULL;
-}
-
-static int cmp_adapter(gconstpointer a, gconstpointer b)
-{
-	const struct heartrate_adapter *hradapter = a;
-	const struct btd_adapter *adapter = b;
-
-	if (adapter == hradapter->adapter)
-		return 0;
-
-	return -1;
-}
-
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
-	const struct heartrate *hr = a;
-	const struct btd_device *dev = b;
-
-	if (dev == hr->dev)
-		return 0;
-
-	return -1;
-}
-
-static int cmp_watcher(gconstpointer a, gconstpointer b)
-{
-	const struct watcher *watcher = a;
-	const struct watcher *match = b;
-	int ret;
-
-	ret = g_strcmp0(watcher->srv, match->srv);
-	if (ret != 0)
-		return ret;
-
-	return g_strcmp0(watcher->path, match->path);
-}
-
-static struct heartrate_adapter *
-find_heartrate_adapter(struct btd_adapter *adapter)
-{
-	GSList *l = g_slist_find_custom(heartrate_adapters, adapter,
-								cmp_adapter);
-	if (!l)
-		return NULL;
-
-	return l->data;
-}
-
-static void destroy_watcher(gpointer user_data)
-{
-	struct watcher *watcher = user_data;
-
-	g_free(watcher->path);
-	g_free(watcher->srv);
-	g_free(watcher);
-}
-
-static struct watcher *find_watcher(GSList *list, const char *sender,
-							const char *path)
-{
-	struct watcher *match;
-	GSList *l;
-
-	match = g_new0(struct watcher, 1);
-	match->srv = g_strdup(sender);
-	match->path = g_strdup(path);
-
-	l = g_slist_find_custom(list, match, cmp_watcher);
-	destroy_watcher(match);
-
-	if (l != NULL)
-		return l->data;
-
-	return NULL;
-}
-
-static void destroy_heartrate(gpointer user_data)
-{
-	struct heartrate *hr = user_data;
-
-	if (hr->attioid > 0)
-		btd_device_remove_attio_callback(hr->dev, hr->attioid);
-
-	if (hr->attrib != NULL) {
-		g_attrib_unregister(hr->attrib, hr->attionotid);
-		g_attrib_unref(hr->attrib);
-	}
-
-	btd_device_unref(hr->dev);
-	g_free(hr->svc_range);
-	g_free(hr);
-}
-
-static void remove_watcher(gpointer user_data)
-{
-	struct watcher *watcher = user_data;
-
-	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-}
-
-static void destroy_heartrate_adapter(gpointer user_data)
-{
-	struct heartrate_adapter *hradapter = user_data;
-
-	g_slist_free_full(hradapter->watchers, remove_watcher);
-
-	g_free(hradapter);
-}
-
-static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
-						guint16 len, gpointer user_data)
-{
-	struct heartrate *hr = user_data;
-	uint8_t value;
-	ssize_t vlen;
-
-	if (status != 0) {
-		error("Body Sensor Location read failed: %s",
-							att_ecode2str(status));
-		return;
-	}
-
-	vlen = dec_read_resp(pdu, len, &value, sizeof(value));
-	if (vlen < 0) {
-		error("Protocol error");
-		return;
-	}
-
-	if (vlen != sizeof(value)) {
-		error("Invalid length for Body Sensor Location");
-		return;
-	}
-
-	hr->has_location = TRUE;
-	hr->location = value;
-}
-
-static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	char *msg = user_data;
-
-	if (status != 0)
-		error("%s failed", msg);
-
-	g_free(msg);
-}
-
-static void update_watcher(gpointer data, gpointer user_data)
-{
-	struct watcher *w = data;
-	struct measurement *m = user_data;
-	struct heartrate *hr = m->hr;
-	const char *path = device_get_path(hr->dev);
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	DBusMessage *msg;
-
-	msg = dbus_message_new_method_call(w->srv, w->path,
-			HEART_RATE_WATCHER_INTERFACE, "MeasurementReceived");
-	if (msg == NULL)
-		return;
-
-	dbus_message_iter_init_append(msg, &iter);
-
-	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
-
-	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);
-
-	dict_append_entry(&dict, "Value", DBUS_TYPE_UINT16, &m->value);
-
-	if (m->has_energy)
-		dict_append_entry(&dict, "Energy", DBUS_TYPE_UINT16,
-								&m->energy);
-
-	if (m->has_contact)
-		dict_append_entry(&dict, "Contact", DBUS_TYPE_BOOLEAN,
-								&m->contact);
-
-	if (m->num_interval > 0)
-		dict_append_array(&dict, "Interval", DBUS_TYPE_UINT16,
-						&m->interval, m->num_interval);
-
-	dbus_message_iter_close_container(&iter, &dict);
-
-	dbus_message_set_no_reply(msg, TRUE);
-	g_dbus_send_message(btd_get_dbus_connection(), msg);
-}
-
-static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
-								uint16_t len)
-{
-	struct measurement m;
-	uint8_t flags;
-
-	flags = *pdu;
-
-	pdu++;
-	len--;
-
-	memset(&m, 0, sizeof(m));
-
-	if (flags & HR_VALUE_FORMAT) {
-		if (len < 2) {
-			error("Heart Rate Measurement field missing");
-			return;
-		}
-
-		m.value = get_le16(pdu);
-		pdu += 2;
-		len -= 2;
-	} else {
-		if (len < 1) {
-			error("Heart Rate Measurement field missing");
-			return;
-		}
-
-		m.value = *pdu;
-		pdu++;
-		len--;
-	}
-
-	if (flags & ENERGY_EXP_STATUS) {
-		if (len < 2) {
-			error("Energy Expended field missing");
-			return;
-		}
-
-		m.has_energy = TRUE;
-		m.energy = get_le16(pdu);
-		pdu += 2;
-		len -= 2;
-	}
-
-	if (flags & RR_INTERVAL) {
-		int i;
-
-		if (len == 0 || (len % 2 != 0)) {
-			error("RR-Interval field malformed");
-			return;
-		}
-
-		m.num_interval = len / 2;
-		m.interval = g_new(uint16_t, m.num_interval);
-
-		for (i = 0; i < m.num_interval; pdu += 2, i++)
-			m.interval[i] = get_le16(pdu);
-	}
-
-	if (flags & SENSOR_CONTACT_SUPPORT) {
-		m.has_contact = TRUE;
-		m.contact = !!(flags & SENSOR_CONTACT_DETECTED);
-	}
-
-	/* Notify all registered watchers */
-	m.hr = hr;
-	g_slist_foreach(hr->hradapter->watchers, update_watcher, &m);
-
-	g_free(m.interval);
-}
-
-static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
-{
-	struct heartrate *hr = user_data;
-
-	/* should be at least opcode (1b) + handle (2b) */
-	if (len < 3) {
-		error("Invalid PDU received");
-		return;
-	}
-
-	process_measurement(hr, pdu + 3, len - 3);
-}
-
-static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data)
-{
-	struct heartrate *hr = user_data;
-	struct gatt_desc *desc;
-	uint8_t attr_val[2];
-	char *msg;
-
-	if (status != 0) {
-		error("Discover Heart Rate Measurement descriptors failed: %s",
-							att_ecode2str(status));
-		return;
-	}
-
-	/* There will be only one descriptor on list and it will be CCC */
-	desc = descs->data;
-
-	hr->measurement_ccc_handle = desc->handle;
-
-	if (g_slist_length(hr->hradapter->watchers) == 0) {
-		put_le16(0x0000, attr_val);
-		msg = g_strdup("Disable measurement");
-	} else {
-		put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
-		msg = g_strdup("Enable measurement");
-	}
-
-	gatt_write_char(hr->attrib, desc->handle, attr_val, sizeof(attr_val),
-							char_write_cb, msg);
-}
-
-static void discover_measurement_ccc(struct heartrate *hr,
-				struct gatt_char *c, struct gatt_char *c_next)
-{
-	uint16_t start, end;
-	bt_uuid_t uuid;
-
-	start = c->value_handle + 1;
-
-	if (c_next != NULL) {
-		if (start == c_next->handle)
-			return;
-		end = c_next->handle - 1;
-	} else if (c->value_handle != hr->svc_range->end) {
-		end = hr->svc_range->end;
-	} else {
-		return;
-	}
-
-	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
-	gatt_discover_desc(hr->attrib, start, end, &uuid, discover_ccc_cb, hr);
-}
-
-static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
-{
-	struct heartrate *hr = user_data;
-
-	if (status) {
-		error("Discover HRS characteristics failed: %s",
-							att_ecode2str(status));
-		return;
-	}
-
-	for (; chars; chars = chars->next) {
-		struct gatt_char *c = chars->data;
-
-		if (g_strcmp0(c->uuid, HEART_RATE_MEASUREMENT_UUID) == 0) {
-			struct gatt_char *c_next =
-				(chars->next ? chars->next->data : NULL);
-
-			hr->attionotid = g_attrib_register(hr->attrib,
-						ATT_OP_HANDLE_NOTIFY,
-						c->value_handle,
-						notify_handler, hr, NULL);
-
-			discover_measurement_ccc(hr, c, c_next);
-		} else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
-			DBG("Body Sensor Location supported");
-
-			gatt_read_char(hr->attrib, c->value_handle,
-						read_sensor_location_cb, hr);
-		} else if (g_strcmp0(c->uuid,
-					HEART_RATE_CONTROL_POINT_UUID) == 0) {
-			DBG("Heart Rate Control Point supported");
-			hr->hrcp_val_handle = c->value_handle;
-		}
-	}
-}
-
-static void enable_measurement(gpointer data, gpointer user_data)
-{
-	struct heartrate *hr = data;
-	uint16_t handle = hr->measurement_ccc_handle;
-	uint8_t value[2];
-	char *msg;
-
-	if (hr->attrib == NULL || !handle)
-		return;
-
-	put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-	msg = g_strdup("Enable measurement");
-
-	gatt_write_char(hr->attrib, handle, value, sizeof(value),
-							char_write_cb, msg);
-}
-
-static void disable_measurement(gpointer data, gpointer user_data)
-{
-	struct heartrate *hr = data;
-	uint16_t handle = hr->measurement_ccc_handle;
-	uint8_t value[2];
-	char *msg;
-
-	if (hr->attrib == NULL || !handle)
-		return;
-
-	put_le16(0x0000, value);
-	msg = g_strdup("Disable measurement");
-
-	gatt_write_char(hr->attrib, handle, value, sizeof(value),
-							char_write_cb, msg);
-}
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
-	struct heartrate *hr = user_data;
-
-	DBG("");
-
-	hr->attrib = g_attrib_ref(attrib);
-
-	gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end,
-						NULL, discover_char_cb, hr);
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
-	struct heartrate *hr = user_data;
-
-	DBG("");
-
-	if (hr->attionotid > 0) {
-		g_attrib_unregister(hr->attrib, hr->attionotid);
-		hr->attionotid = 0;
-	}
-
-	g_attrib_unref(hr->attrib);
-	hr->attrib = NULL;
-}
-
-static void watcher_exit_cb(DBusConnection *conn, void *user_data)
-{
-	struct watcher *watcher = user_data;
-	struct heartrate_adapter *hradapter = watcher->hradapter;
-
-	DBG("heartrate watcher [%s] disconnected", watcher->path);
-
-	hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
-	g_dbus_remove_watch(conn, watcher->id);
-
-	if (g_slist_length(hradapter->watchers) == 0)
-		g_slist_foreach(hradapter->devices, disable_measurement, 0);
-}
-
-static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	struct heartrate_adapter *hradapter = data;
-	struct watcher *watcher;
-	const char *sender = dbus_message_get_sender(msg);
-	char *path;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	watcher = find_watcher(hradapter->watchers, sender, path);
-	if (watcher != NULL)
-		return btd_error_already_exists(msg);
-
-	watcher = g_new0(struct watcher, 1);
-	watcher->hradapter = hradapter;
-	watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
-						watcher, destroy_watcher);
-	watcher->srv = g_strdup(sender);
-	watcher->path = g_strdup(path);
-
-	if (g_slist_length(hradapter->watchers) == 0)
-		g_slist_foreach(hradapter->devices, enable_measurement, 0);
-
-	hradapter->watchers = g_slist_prepend(hradapter->watchers, watcher);
-
-	DBG("heartrate watcher [%s] registered", path);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	struct heartrate_adapter *hradapter = data;
-	struct watcher *watcher;
-	const char *sender = dbus_message_get_sender(msg);
-	char *path;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	watcher = find_watcher(hradapter->watchers, sender, path);
-	if (watcher == NULL)
-		return btd_error_does_not_exist(msg);
-
-	hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
-	g_dbus_remove_watch(conn, watcher->id);
-
-	if (g_slist_length(hradapter->watchers) == 0)
-		g_slist_foreach(hradapter->devices, disable_measurement, 0);
-
-	DBG("heartrate watcher [%s] unregistered", path);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static const GDBusMethodTable heartrate_manager_methods[] = {
-	{ GDBUS_METHOD("RegisterWatcher",
-			GDBUS_ARGS({ "agent", "o" }), NULL,
-			register_watcher) },
-	{ GDBUS_METHOD("UnregisterWatcher",
-			GDBUS_ARGS({ "agent", "o" }), NULL,
-			unregister_watcher) },
-	{ }
-};
-
-static gboolean property_get_location(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct heartrate *hr = data;
-	char *loc;
-
-	if (!hr->has_location)
-		return FALSE;
-
-	loc = g_strdup(location2str(hr->location));
-
-	if (loc == NULL)
-		return FALSE;
-
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
-
-	g_free(loc);
-
-	return TRUE;
-}
-
-static gboolean property_exists_location(const GDBusPropertyTable *property,
-								void *data)
-{
-	struct heartrate *hr = data;
-
-	if (!hr->has_location || location2str(hr->location) == NULL)
-		return FALSE;
-
-	return TRUE;
-}
-
-static gboolean property_get_reset_supported(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct heartrate *hr = data;
-	dbus_bool_t has_reset = !!hr->hrcp_val_handle;
-
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &has_reset);
-
-	return TRUE;
-}
-
-static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	struct heartrate *hr = data;
-	uint8_t value;
-	char *vmsg;
-
-	if (!hr->hrcp_val_handle)
-		return btd_error_not_supported(msg);
-
-	if (!hr->attrib)
-		return btd_error_not_available(msg);
-
-	value = 0x01;
-	vmsg = g_strdup("Reset Control Point");
-	gatt_write_char(hr->attrib, hr->hrcp_val_handle, &value,
-					sizeof(value), char_write_cb, vmsg);
-
-	DBG("Energy Expended Value has been reset");
-
-	return dbus_message_new_method_return(msg);
-}
-
-static const GDBusMethodTable heartrate_device_methods[] = {
-	{ GDBUS_METHOD("Reset", NULL, NULL, hrcp_reset) },
-	{ }
-};
-
-static const GDBusPropertyTable heartrate_device_properties[] = {
-	{ "Location", "s", property_get_location, NULL,
-						property_exists_location },
-	{ "ResetSupported", "b", property_get_reset_supported },
-	{ }
-};
-
-static int heartrate_adapter_register(struct btd_adapter *adapter)
-{
-	struct heartrate_adapter *hradapter;
-
-	hradapter = g_new0(struct heartrate_adapter, 1);
-	hradapter->adapter = adapter;
-
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
-						adapter_get_path(adapter),
-						HEART_RATE_MANAGER_INTERFACE,
-						heartrate_manager_methods,
-						NULL, NULL, hradapter,
-						destroy_heartrate_adapter)) {
-		error("D-Bus failed to register %s interface",
-						HEART_RATE_MANAGER_INTERFACE);
-		destroy_heartrate_adapter(hradapter);
-		return -EIO;
-	}
-
-	heartrate_adapters = g_slist_prepend(heartrate_adapters, hradapter);
-
-	return 0;
-}
-
-static void heartrate_adapter_unregister(struct btd_adapter *adapter)
-{
-	struct heartrate_adapter *hradapter;
-
-	hradapter = find_heartrate_adapter(adapter);
-	if (hradapter == NULL)
-		return;
-
-	heartrate_adapters = g_slist_remove(heartrate_adapters, hradapter);
-
-	g_dbus_unregister_interface(btd_get_dbus_connection(),
-					adapter_get_path(hradapter->adapter),
-					HEART_RATE_MANAGER_INTERFACE);
-}
-
-static int heartrate_device_register(struct btd_device *device,
-						struct gatt_primary *prim)
-{
-	struct btd_adapter *adapter;
-	struct heartrate_adapter *hradapter;
-	struct heartrate *hr;
-
-	adapter = device_get_adapter(device);
-
-	hradapter = find_heartrate_adapter(adapter);
-
-	if (hradapter == NULL)
-		return -1;
-
-	hr = g_new0(struct heartrate, 1);
-	hr->dev = btd_device_ref(device);
-	hr->hradapter = hradapter;
-
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
-						device_get_path(device),
-						HEART_RATE_INTERFACE,
-						heartrate_device_methods,
-						NULL,
-						heartrate_device_properties,
-						hr, destroy_heartrate)) {
-		error("D-Bus failed to register %s interface",
-						HEART_RATE_INTERFACE);
-		destroy_heartrate(hr);
-		return -EIO;
-	}
-
-	hr->svc_range = g_new0(struct att_range, 1);
-	hr->svc_range->start = prim->range.start;
-	hr->svc_range->end = prim->range.end;
-
-	hradapter->devices = g_slist_prepend(hradapter->devices, hr);
-
-	hr->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
-						attio_disconnected_cb, hr);
-
-	return 0;
-}
-
-static void heartrate_device_unregister(struct btd_device *device)
-{
-	struct btd_adapter *adapter;
-	struct heartrate_adapter *hradapter;
-	struct heartrate *hr;
-	GSList *l;
-
-	adapter = device_get_adapter(device);
-
-	hradapter = find_heartrate_adapter(adapter);
-	if (hradapter == NULL)
-		return;
-
-	l = g_slist_find_custom(hradapter->devices, device, cmp_device);
-	if (l == NULL)
-		return;
-
-	hr = l->data;
-
-	hradapter->devices = g_slist_remove(hradapter->devices, hr);
-
-	g_dbus_unregister_interface(btd_get_dbus_connection(),
-				device_get_path(device), HEART_RATE_INTERFACE);
-}
-
-static int heartrate_adapter_probe(struct btd_profile *p,
-						struct btd_adapter *adapter)
-{
-	return heartrate_adapter_register(adapter);
-}
-
-static void heartrate_adapter_remove(struct btd_profile *p,
-						struct btd_adapter *adapter)
-{
-	heartrate_adapter_unregister(adapter);
-}
-
-static int heartrate_device_probe(struct btd_service *service)
-{
-	struct btd_device *device = btd_service_get_device(service);
-	struct gatt_primary *prim;
-
-	prim = btd_device_get_primary(device, HEART_RATE_UUID);
-	if (prim == NULL)
-		return -EINVAL;
-
-	return heartrate_device_register(device, prim);
-}
-
-static void heartrate_device_remove(struct btd_service *service)
-{
-	struct btd_device *device = btd_service_get_device(service);
-
-	heartrate_device_unregister(device);
-}
-
-static struct btd_profile hrp_profile = {
-	.name		= "Heart Rate GATT Driver",
-	.remote_uuid	= HEART_RATE_UUID,
-
-	.device_probe	= heartrate_device_probe,
-	.device_remove	= heartrate_device_remove,
-
-	.adapter_probe	= heartrate_adapter_probe,
-	.adapter_remove	= heartrate_adapter_remove,
-};
-
-static int heartrate_init(void)
-{
-	return btd_profile_register(&hrp_profile);
-}
-
-static void heartrate_exit(void)
-{
-	btd_profile_unregister(&hrp_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(heartrate, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
-					heartrate_init, heartrate_exit)
diff --git a/profiles/input/hog-lib.c b/profiles/input/hog-lib.c
index 1df1799..e376c2b 100644
--- a/profiles/input/hog-lib.c
+++ b/profiles/input/hog-lib.c
@@ -315,8 +315,6 @@
 		error("bt_uhid_send: %s (%d)", strerror(-err), -err);
 		return;
 	}
-
-	DBG("HoG report (%u bytes)", ev.u.input.size);
 }
 
 static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
@@ -1020,7 +1018,7 @@
 		return;
 	}
 
-	strcpy((char *) ev.u.create.name, hog->name);
+	strncpy((char *) ev.u.create.name, hog->name, sizeof(ev.u.create.name));
 	ev.u.create.vendor = hog->vendor;
 	ev.u.create.product = hog->product;
 	ev.u.create.version = hog->version;
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index 837a1ad..233929d 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -54,7 +54,6 @@
 #include "suspend.h"
 #include "attrib/att.h"
 #include "attrib/gattrib.h"
-#include "src/attio.h"
 #include "attrib/gatt.h"
 #include "hog-lib.h"
 
@@ -69,24 +68,6 @@
 static gboolean suspend_supported = FALSE;
 static struct queue *devices = NULL;
 
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
-	struct hog_device *dev = user_data;
-
-	DBG("HoG connected");
-
-	bt_hog_attach(dev->hog, attrib);
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
-	struct hog_device *dev = user_data;
-
-	DBG("HoG disconnected");
-
-	bt_hog_detach(dev->hog);
-}
-
 static struct hog_device *hog_device_new(struct btd_device *device,
 						struct gatt_primary *prim)
 {
@@ -115,15 +96,6 @@
 
 	dev->device = btd_device_ref(device);
 
-	/*
-	 * TODO: Remove attio callback and use .accept once using
-	 * bt_gatt_client.
-	 */
-	dev->attioid = btd_device_add_attio_callback(device,
-							attio_connected_cb,
-							attio_disconnected_cb,
-							dev);
-
 	if (!devices)
 		devices = queue_new();
 
@@ -142,7 +114,6 @@
 		devices = NULL;
 	}
 
-	btd_device_remove_attio_callback(dev->device, dev->attioid);
 	btd_device_unref(dev->device);
 	bt_hog_unref(dev->hog);
 	free(dev);
@@ -215,11 +186,39 @@
 	hog_device_free(dev);
 }
 
+static int hog_accept(struct btd_service *service)
+{
+	struct hog_device *dev = btd_service_get_user_data(service);
+	struct btd_device *device = btd_service_get_device(service);
+	GAttrib *attrib = btd_device_get_attrib(device);
+
+	/* TODO: Replace GAttrib with bt_gatt_client */
+	bt_hog_attach(dev->hog, attrib);
+
+	btd_service_connecting_complete(service, 0);
+
+	return 0;
+}
+
+static int hog_disconnect(struct btd_service *service)
+{
+	struct hog_device *dev = btd_service_get_user_data(service);
+
+	bt_hog_detach(dev->hog);
+
+	btd_service_disconnecting_complete(service, 0);
+
+	return 0;
+}
+
 static struct btd_profile hog_profile = {
 	.name		= "input-hog",
 	.remote_uuid	= HOG_UUID,
 	.device_probe	= hog_probe,
 	.device_remove	= hog_remove,
+	.accept		= hog_accept,
+	.disconnect	= hog_disconnect,
+	.auto_connect	= true,
 };
 
 static int hog_init(void)
diff --git a/profiles/network/server.c b/profiles/network/server.c
index 1bff9f8..e69ffaf 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -430,7 +430,6 @@
 static void confirm_event(GIOChannel *chan, gpointer user_data)
 {
 	struct network_adapter *na = user_data;
-	struct network_server *ns;
 	bdaddr_t src, dst;
 	char address[18];
 	GError *err = NULL;
@@ -454,8 +453,7 @@
 		goto drop;
 	}
 
-	ns = find_server(na->servers, BNEP_SVC_NAP);
-	if (!ns || !ns->record_id || !ns->bridge)
+	if (!na->servers)
 		goto drop;
 
 	na->setup = g_new0(struct network_session, 1);
diff --git a/profiles/oad/oad.c b/profiles/oad/oad.c
index 08b8df4..ad09863 100644
--- a/profiles/oad/oad.c
+++ b/profiles/oad/oad.c
@@ -53,7 +53,6 @@
 #include "src/service.h"
 #include "attrib/att.h"
 #include "attrib/gattrib.h"
-#include "src/attio.h"
 #include "attrib/gatt.h"
 #include "src/shared/util.h"
 
@@ -128,7 +127,6 @@
 	uint16_t		id;
 	struct btd_device	*device;
 	GAttrib			*attrib;
-	guint			attioid;
 	struct gatt_primary	*oad_primary;
 
 	struct oad_service	identity;
@@ -758,11 +756,12 @@
 	}
 }
 
-static void oad_attio_connected_cb(GAttrib *attrib, gpointer user_data)
+static int oad_accept(struct btd_service *service)
 {
-	struct oad_device *oaddev = user_data;
+	struct oad_device *oaddev = btd_service_get_user_data(service);
 	struct gatt_primary *prim = oaddev->oad_primary;
-	GSList *l;
+	struct btd_device *device = btd_service_get_device(service);
+	GAttrib *attrib = btd_device_get_attrib(device);
 
 	DBG("OAD connected");
 
@@ -770,6 +769,8 @@
 	oaddev->identity.notify_enabled = 0;
 	oaddev->block.notify_enabled = 0;
 
+	btd_service_connecting_complete(service, 0);
+
 	if (oaddev->identity.ccc_handle == 0 || oaddev->block.ccc_handle == 0 ||
 	    oaddev->identity.value_handle == 0 || oaddev->block.value_handle == 0) {
 		DBG("OAD discovering characteristics");
@@ -781,12 +782,13 @@
 			oad_enable_notify(oaddev);
 		}
 	}
+
+	return 0;
 }
 
-static void oad_attio_disconnected_cb(gpointer user_data)
+static int oad_disconnect(struct btd_service *service)
 {
-	struct oad_device *oaddev = user_data;
-	GSList *l;
+	struct oad_device *oaddev = btd_service_get_user_data(service);
 
 	DBG("OAD disconnected");
 
@@ -798,9 +800,14 @@
 		g_attrib_unregister(oaddev->attrib, oaddev->block.attio_id);
 		oaddev->block.attio_id = 0;
 	}
-	if (oaddev->attrib)
+	if (oaddev->attrib) {
 		g_attrib_unref(oaddev->attrib);
-	oaddev->attrib = NULL;
+		oaddev->attrib = NULL;
+	}
+
+	btd_service_disconnecting_complete(service, 0);
+
+	return 0;
 }
 
 static struct oad_device *oad_register_device(struct btd_device *device, struct gatt_primary *prim)
@@ -815,9 +822,6 @@
 
 	oaddev->oad_primary = g_memdup(prim, sizeof(*prim));
 
-	oaddev->attioid = btd_device_add_attio_callback(device,
-		oad_attio_connected_cb, oad_attio_disconnected_cb, oaddev);
-
 	return oaddev;
 }
 
@@ -827,7 +831,6 @@
 		g_source_remove(oaddev->tid);
 		oaddev->tid = 0;
 	}
-	btd_device_remove_attio_callback(oaddev->device, oaddev->attioid);
 	oad_free_device(oaddev);
 
 	return 0;
@@ -858,6 +861,7 @@
 		if (oaddev == NULL)
 			continue;
 
+		btd_service_set_user_data(service, oaddev);
 		oad_devices = g_slist_append(oad_devices, oaddev);
 	}
 
@@ -891,6 +895,9 @@
 	.remote_uuid	= OAD_UUID,
 	.device_probe	= oad_probe,
 	.device_remove	= oad_remove,
+	.accept		= oad_accept,
+	.disconnect	= oad_disconnect,
+	.auto_connect	= true,
 };
 
 static int oad_init(void)
diff --git a/profiles/scanparam/scan.c b/profiles/scanparam/scan.c
index d3ca762..0ff4a43 100644
--- a/profiles/scanparam/scan.c
+++ b/profiles/scanparam/scan.c
@@ -62,8 +62,6 @@
 	guint refresh_cb_id;
 };
 
-static GSList *devices;
-
 static void scan_free(struct scan *scan)
 {
 	bt_gatt_client_unregister_notify(scan->client, scan->refresh_cb_id);
@@ -73,14 +71,6 @@
 	g_free(scan);
 }
 
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
-	const struct scan *scan = a;
-	const struct btd_device *device = b;
-
-	return scan->device == device ? 0 : -1;
-}
-
 static void write_scan_params(struct scan *scan)
 {
 	uint8_t value[4];
@@ -175,33 +165,32 @@
 	gatt_db_service_foreach_char(scan->attr, handle_characteristic, scan);
 }
 
+static void scan_reset(struct scan *scan)
+{
+	scan->attr = NULL;
+	gatt_db_unref(scan->db);
+	scan->db = NULL;
+	bt_gatt_client_unref(scan->client);
+	scan->client = NULL;
+}
+
 static int scan_param_accept(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
 	struct gatt_db *db = btd_device_get_gatt_db(device);
 	struct bt_gatt_client *client = btd_device_get_gatt_client(device);
 	bt_uuid_t scan_parameters_uuid;
-	struct scan *scan;
-	GSList *l;
+	struct scan *scan = btd_service_get_user_data(service);
 	char addr[18];
 
 	ba2str(device_get_address(device), addr);
 	DBG("Scan Parameters Client Driver profile accept (%s)", addr);
 
-	l = g_slist_find_custom(devices, device, cmp_device);
-	if (!l) {
+	if (!scan) {
 		error("Scan Parameters service not handled by profile");
 		return -1;
 	}
 
-	scan = l->data;
-
-	/* Clean-up any old client/db and acquire the new ones */
-	scan->attr = NULL;
-	gatt_db_unref(scan->db);
-	bt_gatt_client_unref(scan->client);
-
-
 	scan->db = gatt_db_ref(db);
 	scan->client = bt_gatt_client_ref(client);
 
@@ -209,6 +198,25 @@
 	gatt_db_foreach_service(db, &scan_parameters_uuid,
 					foreach_scan_param_service, scan);
 
+	if (!scan->attr) {
+		error("Scan Parameters attribute not found");
+		scan_reset(scan);
+		return -1;
+	}
+
+	btd_service_connecting_complete(service, 0);
+
+	return 0;
+}
+
+static int scan_param_disconnect(struct btd_service *service)
+{
+	struct scan *scan = btd_service_get_user_data(service);
+
+	scan_reset(scan);
+
+	btd_service_disconnecting_complete(service, 0);
+
 	return 0;
 }
 
@@ -216,21 +224,17 @@
 {
 	struct btd_device *device = btd_service_get_device(service);
 	struct scan *scan;
-	GSList *l;
 	char addr[18];
 
 	ba2str(device_get_address(device), addr);
 	DBG("GAP profile remove (%s)", addr);
 
-	l = g_slist_find_custom(devices, device, cmp_device);
-	if (!l) {
+	scan = btd_service_get_user_data(service);
+	if (!scan) {
 		error("GAP service not handled by profile");
 		return;
 	}
 
-	scan = l->data;
-
-	devices = g_slist_remove(devices, scan);
 	scan_free(scan);
 }
 
@@ -238,16 +242,15 @@
 {
 	struct btd_device *device = btd_service_get_device(service);
 	struct scan *scan;
-	GSList *l;
 	char addr[18];
 
 	ba2str(device_get_address(device), addr);
 	DBG("Scan Parameters Client Driver profile probe (%s)", addr);
 
 	/* Ignore, if we were probed for this device already */
-	l = g_slist_find_custom(devices, device, cmp_device);
-	if (l) {
-		error("Profile probed twice for the same device!");
+	scan = btd_service_get_user_data(service);
+	if (scan) {
+		error("Profile probed twice for the same service!");
 		return -1;
 	}
 
@@ -256,7 +259,7 @@
 		return -1;
 
 	scan->device = btd_device_ref(device);
-	devices = g_slist_append(devices, scan);
+	btd_service_set_user_data(service, scan);
 	return 0;
 }
 
@@ -266,6 +269,7 @@
 	.device_probe = scan_param_probe,
 	.device_remove = scan_param_remove,
 	.accept = scan_param_accept,
+	.disconnect = scan_param_disconnect,
 };
 
 static int scan_param_init(void)
diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c
deleted file mode 100644
index b0fc3e0..0000000
--- a/profiles/thermometer/thermometer.c
+++ /dev/null
@@ -1,1321 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- *
- *  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
-
-#include <stdbool.h>
-#include <errno.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "gdbus/gdbus.h"
-
-#include "src/plugin.h"
-#include "src/dbus-common.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/service.h"
-#include "src/shared/util.h"
-#include "src/error.h"
-#include "src/log.h"
-#include "attrib/gattrib.h"
-#include "src/attio.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-
-#define THERMOMETER_INTERFACE		"org.bluez.Thermometer1"
-#define THERMOMETER_MANAGER_INTERFACE	"org.bluez.ThermometerManager1"
-#define THERMOMETER_WATCHER_INTERFACE	"org.bluez.ThermometerWatcher1"
-
-/* Temperature measurement flag fields */
-#define TEMP_UNITS		0x01
-#define TEMP_TIME_STAMP		0x02
-#define TEMP_TYPE		0x04
-
-#define FLOAT_MAX_MANTISSA	16777216 /* 2^24 */
-
-#define VALID_RANGE_DESC_SIZE	4
-#define TEMPERATURE_TYPE_SIZE	1
-#define MEASUREMENT_INTERVAL_SIZE	2
-
-struct thermometer_adapter {
-	struct btd_adapter	*adapter;
-	GSList			*devices;
-	GSList			*fwatchers;	/* Final measurements */
-	GSList			*iwatchers;	/* Intermediate measurements */
-};
-
-struct thermometer {
-	struct btd_device		*dev;		/* Device reference */
-	struct thermometer_adapter	*tadapter;
-	GAttrib				*attrib;	/* GATT connection */
-	struct att_range		*svc_range;	/* Thermometer range */
-	guint				attioid;	/* Att watcher id */
-	/* attio id for Temperature Measurement value indications */
-	guint				attio_measurement_id;
-	/* attio id for Intermediate Temperature value notifications */
-	guint				attio_intermediate_id;
-	/* attio id for Measurement Interval value indications */
-	guint				attio_interval_id;
-	gboolean			intermediate;
-	uint8_t				type;
-	uint16_t			interval;
-	uint16_t			max;
-	uint16_t			min;
-	gboolean			has_type;
-	gboolean			has_interval;
-
-	uint16_t			measurement_ccc_handle;
-	uint16_t			intermediate_ccc_handle;
-	uint16_t			interval_val_handle;
-};
-
-struct characteristic {
-	struct thermometer	*t;	/* Thermometer where the char belongs */
-	char			uuid[MAX_LEN_UUID_STR + 1];
-};
-
-struct watcher {
-	struct thermometer_adapter	*tadapter;
-	guint				id;
-	char				*srv;
-	char				*path;
-};
-
-struct measurement {
-	struct thermometer	*t;
-	int16_t			exp;
-	int32_t			mant;
-	uint64_t		time;
-	gboolean		suptime;
-	char			*unit;
-	char			*type;
-	char			*value;
-};
-
-struct tmp_interval_data {
-	struct thermometer	*thermometer;
-	uint16_t		interval;
-};
-
-static GSList *thermometer_adapters = NULL;
-
-static const char * const temp_type[] = {
-	"<reserved>",
-	"armpit",
-	"body",
-	"ear",
-	"finger",
-	"intestines",
-	"mouth",
-	"rectum",
-	"toe",
-	"tympanum"
-};
-
-static const char *temptype2str(uint8_t value)
-{
-	 if (value > 0 && value < G_N_ELEMENTS(temp_type))
-		return temp_type[value];
-
-	error("Temperature type %d reserved for future use", value);
-	return NULL;
-}
-
-static void destroy_watcher(gpointer user_data)
-{
-	struct watcher *watcher = user_data;
-
-	g_free(watcher->path);
-	g_free(watcher->srv);
-	g_free(watcher);
-}
-
-static void remove_watcher(gpointer user_data)
-{
-	struct watcher *watcher = user_data;
-
-	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-}
-
-static void destroy_thermometer(gpointer user_data)
-{
-	struct thermometer *t = user_data;
-
-	if (t->attioid > 0)
-		btd_device_remove_attio_callback(t->dev, t->attioid);
-
-	if (t->attrib != NULL) {
-		g_attrib_unregister(t->attrib, t->attio_measurement_id);
-		g_attrib_unregister(t->attrib, t->attio_intermediate_id);
-		g_attrib_unregister(t->attrib, t->attio_interval_id);
-		g_attrib_unref(t->attrib);
-	}
-
-	btd_device_unref(t->dev);
-	g_free(t->svc_range);
-	g_free(t);
-}
-
-static void destroy_thermometer_adapter(gpointer user_data)
-{
-	struct thermometer_adapter *tadapter = user_data;
-
-	if (tadapter->devices != NULL)
-		g_slist_free_full(tadapter->devices, destroy_thermometer);
-
-	if (tadapter->fwatchers != NULL)
-		g_slist_free_full(tadapter->fwatchers, remove_watcher);
-
-	g_free(tadapter);
-}
-
-static int cmp_adapter(gconstpointer a, gconstpointer b)
-{
-	const struct thermometer_adapter *tadapter = a;
-	const struct btd_adapter *adapter = b;
-
-	if (adapter == tadapter->adapter)
-		return 0;
-
-	return -1;
-}
-
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
-	const struct thermometer *t = a;
-	const struct btd_device *dev = b;
-
-	if (dev == t->dev)
-		return 0;
-
-	return -1;
-}
-
-static int cmp_watcher(gconstpointer a, gconstpointer b)
-{
-	const struct watcher *watcher = a;
-	const struct watcher *match = b;
-	int ret;
-
-	ret = g_strcmp0(watcher->srv, match->srv);
-	if (ret != 0)
-		return ret;
-
-	return g_strcmp0(watcher->path, match->path);
-}
-
-static struct thermometer_adapter *
-find_thermometer_adapter(struct btd_adapter *adapter)
-{
-	GSList *l = g_slist_find_custom(thermometer_adapters, adapter,
-								cmp_adapter);
-	if (!l)
-		return NULL;
-
-	return l->data;
-}
-
-static void change_property(struct thermometer *t, const char *name,
-							gpointer value) {
-	if (g_strcmp0(name, "Intermediate") == 0) {
-		gboolean *intermediate = value;
-		if (t->intermediate == *intermediate)
-			return;
-
-		t->intermediate = *intermediate;
-	} else if (g_strcmp0(name, "Interval") == 0) {
-		uint16_t *interval = value;
-		if (t->has_interval && t->interval == *interval)
-			return;
-
-		t->has_interval = TRUE;
-		t->interval = *interval;
-	} else if (g_strcmp0(name, "Maximum") == 0) {
-		uint16_t *max = value;
-		if (t->max == *max)
-			return;
-
-		t->max = *max;
-	} else if (g_strcmp0(name, "Minimum") == 0) {
-		uint16_t *min = value;
-		if (t->min == *min)
-			return;
-
-		t->min = *min;
-	} else {
-		DBG("%s is not a thermometer property", name);
-		return;
-	}
-
-	g_dbus_emit_property_changed(btd_get_dbus_connection(),
-						device_get_path(t->dev),
-						THERMOMETER_INTERFACE, name);
-}
-
-static void update_watcher(gpointer data, gpointer user_data)
-{
-	struct watcher *w = data;
-	struct measurement *m = user_data;
-	const char *path = device_get_path(m->t->dev);
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	DBusMessage *msg;
-
-	msg = dbus_message_new_method_call(w->srv, w->path,
-				THERMOMETER_WATCHER_INTERFACE,
-				"MeasurementReceived");
-	if (msg == NULL)
-		return;
-
-	dbus_message_iter_init_append(msg, &iter);
-
-	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
-
-	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);
-
-	dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
-	dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
-	dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
-
-	if (m->suptime)
-		dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
-
-	dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
-	dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
-
-	dbus_message_iter_close_container(&iter, &dict);
-
-	dbus_message_set_no_reply(msg, TRUE);
-	g_dbus_send_message(btd_get_dbus_connection(), msg);
-}
-
-static void recv_measurement(struct thermometer *t, struct measurement *m)
-{
-	GSList *wlist;
-
-	m->t = t;
-
-	if (g_strcmp0(m->value, "intermediate") == 0)
-		wlist = t->tadapter->iwatchers;
-	else
-		wlist = t->tadapter->fwatchers;
-
-	g_slist_foreach(wlist, update_watcher, m);
-}
-
-static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
-						uint16_t len, gboolean final)
-{
-	struct measurement m;
-	const char *type = NULL;
-	uint8_t flags;
-	uint32_t raw;
-
-	/* skip opcode and handle */
-	pdu += 3;
-	len -= 3;
-
-	if (len < 1) {
-		DBG("Mandatory flags are not provided");
-		return;
-	}
-
-	memset(&m, 0, sizeof(m));
-
-	flags = *pdu;
-
-	if (flags & TEMP_UNITS)
-		m.unit = "fahrenheit";
-	else
-		m.unit = "celsius";
-
-	pdu++;
-	len--;
-
-	if (len < 4) {
-		DBG("Mandatory temperature measurement value is not provided");
-		return;
-	}
-
-	raw = get_le32(pdu);
-	m.mant = raw & 0x00FFFFFF;
-	m.exp = ((int32_t) raw) >> 24;
-
-	if (m.mant & 0x00800000) {
-		/* convert to C2 negative value */
-		m.mant = m.mant - FLOAT_MAX_MANTISSA;
-	}
-
-	pdu += 4;
-	len -= 4;
-
-	if (flags & TEMP_TIME_STAMP) {
-		struct tm ts;
-		time_t time;
-
-		if (len < 7) {
-			DBG("Time stamp is not provided");
-			return;
-		}
-
-		ts.tm_year = get_le16(pdu) - 1900;
-		ts.tm_mon = *(pdu + 2) - 1;
-		ts.tm_mday = *(pdu + 3);
-		ts.tm_hour = *(pdu + 4);
-		ts.tm_min = *(pdu + 5);
-		ts.tm_sec = *(pdu + 6);
-		ts.tm_isdst = -1;
-
-		time = mktime(&ts);
-		m.time = (uint64_t) time;
-		m.suptime = TRUE;
-
-		pdu += 7;
-		len -= 7;
-	}
-
-	if (flags & TEMP_TYPE) {
-		if (len < 1) {
-			DBG("Temperature type is not provided");
-			return;
-		}
-
-		type = temptype2str(*pdu);
-	} else if (t->has_type) {
-		type = temptype2str(t->type);
-	}
-
-	m.type = g_strdup(type);
-	m.value = final ? "final" : "intermediate";
-
-	recv_measurement(t, &m);
-	g_free(m.type);
-}
-
-
-static void measurement_ind_handler(const uint8_t *pdu, uint16_t len,
-							gpointer user_data)
-{
-	struct thermometer *t = user_data;
-	uint8_t *opdu;
-	uint16_t olen;
-	size_t plen;
-
-	if (len < 3) {
-		DBG("Bad pdu received");
-		return;
-	}
-
-	proc_measurement(t, pdu, len, TRUE);
-
-	opdu = g_attrib_get_buffer(t->attrib, &plen);
-	olen = enc_confirmation(opdu, plen);
-
-	if (olen > 0)
-		g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
-}
-
-static void intermediate_notify_handler(const uint8_t *pdu, uint16_t len,
-							gpointer user_data)
-{
-	struct thermometer *t = user_data;
-
-	if (len < 3) {
-		DBG("Bad pdu received");
-		return;
-	}
-
-	proc_measurement(t, pdu, len, FALSE);
-}
-
-static void interval_ind_handler(const uint8_t *pdu, uint16_t len,
-							gpointer user_data)
-{
-	struct thermometer *t = user_data;
-	uint16_t interval;
-	uint8_t *opdu;
-	uint16_t olen;
-	size_t plen;
-
-	if (len < 5) {
-		DBG("Bad pdu received");
-		return;
-	}
-
-	interval = get_le16(pdu + 3);
-	change_property(t, "Interval", &interval);
-
-	opdu = g_attrib_get_buffer(t->attrib, &plen);
-	olen = enc_confirmation(opdu, plen);
-
-	if (olen > 0)
-		g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
-}
-
-static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	struct thermometer *t = user_data;
-	uint8_t value[VALID_RANGE_DESC_SIZE];
-	uint16_t max, min;
-	ssize_t vlen;
-
-	if (status != 0) {
-		DBG("Valid Range descriptor read failed: %s",
-							att_ecode2str(status));
-		return;
-	}
-
-	vlen = dec_read_resp(pdu, len, value, sizeof(value));
-	if (vlen < 0) {
-		DBG("Protocol error\n");
-		return;
-	}
-
-	if (vlen < 4) {
-		DBG("Invalid range received");
-		return;
-	}
-
-	min = get_le16(&value[0]);
-	max = get_le16(&value[2]);
-
-	if (min == 0 || min > max) {
-		DBG("Invalid range");
-		return;
-	}
-
-	change_property(t, "Maximum", &max);
-	change_property(t, "Minimum", &min);
-}
-
-static void write_ccc_cb(guint8 status, const guint8 *pdu,
-						guint16 len, gpointer user_data)
-{
-	char *msg = user_data;
-
-	if (status != 0)
-		error("%s failed", msg);
-
-	g_free(msg);
-}
-
-static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid,
-								uint16_t handle)
-{
-	uint8_t atval[2];
-	uint16_t val;
-	char *msg;
-
-	if (uuid == GATT_CHARAC_VALID_RANGE_UUID) {
-		if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0)
-			gatt_read_char(ch->t->attrib, handle,
-						valid_range_desc_cb, ch->t);
-		return;
-	}
-
-	if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
-		return;
-
-	if (g_strcmp0(ch->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
-		ch->t->measurement_ccc_handle = handle;
-
-		if (g_slist_length(ch->t->tadapter->fwatchers) == 0) {
-			val = 0x0000;
-			msg = g_strdup("Disable Temperature Measurement ind");
-		} else {
-			val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
-			msg = g_strdup("Enable Temperature Measurement ind");
-		}
-	} else if (g_strcmp0(ch->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
-		ch->t->intermediate_ccc_handle = handle;
-
-		if (g_slist_length(ch->t->tadapter->iwatchers) == 0) {
-			val = 0x0000;
-			msg = g_strdup("Disable Intermediate Temperature noti");
-		} else {
-			val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
-			msg = g_strdup("Enable Intermediate Temperature noti");
-		}
-	} else if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
-		val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
-		msg = g_strdup("Enable Measurement Interval indication");
-	} else {
-		return;
-	}
-
-	put_le16(val, atval);
-	gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval),
-							write_ccc_cb, msg);
-}
-
-static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
-{
-	struct characteristic *ch = user_data;
-
-	if (status != 0) {
-		error("Discover all characteristic descriptors failed [%s]: %s",
-					ch->uuid, att_ecode2str(status));
-		goto done;
-	}
-
-	for ( ; descs; descs = descs->next) {
-		struct gatt_desc *desc = descs->data;
-
-		process_thermometer_desc(ch, desc->uuid16, desc->handle);
-	}
-
-done:
-	g_free(ch);
-}
-
-static void discover_desc(struct thermometer *t, struct gatt_char *c,
-						struct gatt_char *c_next)
-{
-	struct characteristic *ch;
-	uint16_t start, end;
-
-	start = c->value_handle + 1;
-
-	if (c_next != NULL) {
-		if (start == c_next->handle)
-			return;
-		end = c_next->handle - 1;
-	} else if (c->value_handle != t->svc_range->end) {
-		end = t->svc_range->end;
-	} else {
-		return;
-	}
-
-	ch = g_new0(struct characteristic, 1);
-	ch->t = t;
-	memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
-
-	gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch);
-}
-
-static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	struct thermometer *t = user_data;
-	uint8_t value[TEMPERATURE_TYPE_SIZE];
-	ssize_t vlen;
-
-	if (status != 0) {
-		DBG("Temperature Type value read failed: %s",
-							att_ecode2str(status));
-		return;
-	}
-
-	vlen = dec_read_resp(pdu, len, value, sizeof(value));
-	if (vlen < 0) {
-		DBG("Protocol error.");
-		return;
-	}
-
-	if (vlen != 1) {
-		DBG("Invalid length for Temperature type");
-		return;
-	}
-
-	t->has_type = TRUE;
-	t->type = value[0];
-}
-
-static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	struct thermometer *t = user_data;
-	uint8_t value[MEASUREMENT_INTERVAL_SIZE];
-	uint16_t interval;
-	ssize_t vlen;
-
-	if (status != 0) {
-		DBG("Measurement Interval value read failed: %s",
-							att_ecode2str(status));
-		return;
-	}
-
-	vlen = dec_read_resp(pdu, len, value, sizeof(value));
-	if (vlen < 0) {
-		DBG("Protocol error\n");
-		return;
-	}
-
-	if (vlen < 2) {
-		DBG("Invalid Interval received");
-		return;
-	}
-
-	interval = get_le16(&value[0]);
-	change_property(t, "Interval", &interval);
-}
-
-static void process_thermometer_char(struct thermometer *t,
-				struct gatt_char *c, struct gatt_char *c_next)
-{
-	if (g_strcmp0(c->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
-		gboolean intermediate = TRUE;
-		change_property(t, "Intermediate", &intermediate);
-
-		t->attio_intermediate_id = g_attrib_register(t->attrib,
-					ATT_OP_HANDLE_NOTIFY, c->value_handle,
-					intermediate_notify_handler, t, NULL);
-
-		discover_desc(t, c, c_next);
-	} else if (g_strcmp0(c->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
-
-		t->attio_measurement_id = g_attrib_register(t->attrib,
-					ATT_OP_HANDLE_IND, c->value_handle,
-					measurement_ind_handler, t, NULL);
-
-		discover_desc(t, c, c_next);
-	} else if (g_strcmp0(c->uuid, TEMPERATURE_TYPE_UUID) == 0) {
-		gatt_read_char(t->attrib, c->value_handle,
-							read_temp_type_cb, t);
-	} else if (g_strcmp0(c->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
-		bool need_desc = false;
-
-		gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t);
-
-		if (c->properties & GATT_CHR_PROP_WRITE) {
-			t->interval_val_handle = c->value_handle;
-			need_desc = true;
-		}
-
-		if (c->properties & GATT_CHR_PROP_INDICATE) {
-			t->attio_interval_id = g_attrib_register(t->attrib,
-					ATT_OP_HANDLE_IND, c->value_handle,
-					interval_ind_handler, t, NULL);
-			need_desc = true;
-		}
-
-		if (need_desc)
-			discover_desc(t, c, c_next);
-	}
-}
-
-static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
-								void *user_data)
-{
-	struct thermometer *t = user_data;
-	GSList *l;
-
-	if (status != 0) {
-		error("Discover thermometer characteristics: %s",
-							att_ecode2str(status));
-		return;
-	}
-
-	for (l = characteristics; l; l = l->next) {
-		struct gatt_char *c = l->data;
-		struct gatt_char *c_next = (l->next ? l->next->data : NULL);
-
-		process_thermometer_char(t, c, c_next);
-	}
-}
-
-static void write_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
-							gpointer user_data)
-{
-	struct tmp_interval_data *data = user_data;
-
-	if (status != 0) {
-		error("Interval Write Request failed %s",
-							att_ecode2str(status));
-		goto done;
-	}
-
-	if (!dec_write_resp(pdu, len)) {
-		error("Interval Write Request: protocol error");
-		goto done;
-	}
-
-	change_property(data->thermometer, "Interval", &data->interval);
-
-done:
-	g_free(user_data);
-}
-
-static void enable_final_measurement(gpointer data, gpointer user_data)
-{
-	struct thermometer *t = data;
-	uint16_t handle = t->measurement_ccc_handle;
-	uint8_t value[2];
-	char *msg;
-
-	if (t->attrib == NULL || !handle)
-		return;
-
-	put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
-	msg = g_strdup("Enable Temperature Measurement indications");
-
-	gatt_write_char(t->attrib, handle, value, sizeof(value),
-							write_ccc_cb, msg);
-}
-
-static void enable_intermediate_measurement(gpointer data, gpointer user_data)
-{
-	struct thermometer *t = data;
-	uint16_t handle = t->intermediate_ccc_handle;
-	uint8_t value[2];
-	char *msg;
-
-	if (t->attrib == NULL || !handle)
-		return;
-
-	put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-	msg = g_strdup("Enable Intermediate Temperature notifications");
-
-	gatt_write_char(t->attrib, handle, value, sizeof(value),
-							write_ccc_cb, msg);
-}
-
-static void disable_final_measurement(gpointer data, gpointer user_data)
-{
-	struct thermometer *t = data;
-	uint16_t handle = t->measurement_ccc_handle;
-	uint8_t value[2];
-	char *msg;
-
-	if (t->attrib == NULL || !handle)
-		return;
-
-	put_le16(0x0000, value);
-	msg = g_strdup("Disable Temperature Measurement indications");
-
-	gatt_write_char(t->attrib, handle, value, sizeof(value),
-							write_ccc_cb, msg);
-}
-
-static void disable_intermediate_measurement(gpointer data, gpointer user_data)
-{
-	struct thermometer *t = data;
-	uint16_t handle = t->intermediate_ccc_handle;
-	uint8_t value[2];
-	char *msg;
-
-	if (t->attrib == NULL || !handle)
-		return;
-
-	put_le16(0x0000, value);
-	msg = g_strdup("Disable Intermediate Temperature notifications");
-
-	gatt_write_char(t->attrib, handle, value, sizeof(value),
-							write_ccc_cb, msg);
-}
-
-static void remove_int_watcher(struct thermometer_adapter *tadapter,
-							struct watcher *w)
-{
-	if (!g_slist_find(tadapter->iwatchers, w))
-		return;
-
-	tadapter->iwatchers = g_slist_remove(tadapter->iwatchers, w);
-
-	if (g_slist_length(tadapter->iwatchers) == 0)
-		g_slist_foreach(tadapter->devices,
-					disable_intermediate_measurement, 0);
-}
-
-static void watcher_exit(DBusConnection *conn, void *user_data)
-{
-	struct watcher *watcher = user_data;
-	struct thermometer_adapter *tadapter = watcher->tadapter;
-
-	DBG("Thermometer watcher %s disconnected", watcher->path);
-
-	remove_int_watcher(tadapter, watcher);
-
-	tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
-	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-
-	if (g_slist_length(tadapter->fwatchers) == 0)
-		g_slist_foreach(tadapter->devices,
-					disable_final_measurement, 0);
-}
-
-static struct watcher *find_watcher(GSList *list, const char *sender,
-							const char *path)
-{
-	struct watcher *match;
-	GSList *l;
-
-	match = g_new0(struct watcher, 1);
-	match->srv = g_strdup(sender);
-	match->path = g_strdup(path);
-
-	l = g_slist_find_custom(list, match, cmp_watcher);
-	destroy_watcher(match);
-
-	if (l != NULL)
-		return l->data;
-
-	return NULL;
-}
-
-static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	const char *sender = dbus_message_get_sender(msg);
-	struct thermometer_adapter *tadapter = data;
-	struct watcher *watcher;
-	char *path;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	watcher = find_watcher(tadapter->fwatchers, sender, path);
-	if (watcher != NULL)
-		return btd_error_already_exists(msg);
-
-	DBG("Thermometer watcher %s registered", path);
-
-	watcher = g_new0(struct watcher, 1);
-	watcher->srv = g_strdup(sender);
-	watcher->path = g_strdup(path);
-	watcher->tadapter = tadapter;
-	watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
-						watcher, destroy_watcher);
-
-	if (g_slist_length(tadapter->fwatchers) == 0)
-		g_slist_foreach(tadapter->devices, enable_final_measurement, 0);
-
-	tadapter->fwatchers = g_slist_prepend(tadapter->fwatchers, watcher);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	const char *sender = dbus_message_get_sender(msg);
-	struct thermometer_adapter *tadapter = data;
-	struct watcher *watcher;
-	char *path;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	watcher = find_watcher(tadapter->fwatchers, sender, path);
-	if (watcher == NULL)
-		return btd_error_does_not_exist(msg);
-
-	DBG("Thermometer watcher %s unregistered", path);
-
-	remove_int_watcher(tadapter, watcher);
-
-	tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
-	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-
-	if (g_slist_length(tadapter->fwatchers) == 0)
-		g_slist_foreach(tadapter->devices,
-					disable_final_measurement, 0);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	const char *sender = dbus_message_get_sender(msg);
-	struct thermometer_adapter *ta = data;
-	struct watcher *watcher;
-	char *path;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	watcher = find_watcher(ta->fwatchers, sender, path);
-	if (watcher == NULL)
-		return btd_error_does_not_exist(msg);
-
-	if (find_watcher(ta->iwatchers, sender, path))
-		return btd_error_already_exists(msg);
-
-	DBG("Intermediate measurement watcher %s registered", path);
-
-	if (g_slist_length(ta->iwatchers) == 0)
-		g_slist_foreach(ta->devices,
-					enable_intermediate_measurement, 0);
-
-	ta->iwatchers = g_slist_prepend(ta->iwatchers, watcher);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
-								void *data)
-{
-	const char *sender = dbus_message_get_sender(msg);
-	struct thermometer_adapter *ta = data;
-	struct watcher *watcher;
-	char *path;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-							DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	watcher = find_watcher(ta->iwatchers, sender, path);
-	if (watcher == NULL)
-		return btd_error_does_not_exist(msg);
-
-	DBG("Intermediate measurement %s unregistered", path);
-
-	remove_int_watcher(ta, watcher);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static gboolean property_get_intermediate(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct thermometer *t = data;
-	dbus_bool_t val;
-
-	val = !!t->intermediate;
-
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
-
-	return TRUE;
-}
-
-static gboolean property_get_interval(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct thermometer *t = data;
-
-	if (!t->has_interval)
-		return FALSE;
-
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->interval);
-
-	return TRUE;
-}
-
-static void property_set_interval(const GDBusPropertyTable *property,
-					DBusMessageIter *iter,
-					GDBusPendingPropertySet id, void *data)
-{
-	struct thermometer *t = data;
-	struct tmp_interval_data *interval_data;
-	uint16_t val;
-	uint8_t atval[2];
-
-	if (t->interval_val_handle == 0) {
-		g_dbus_pending_property_error(id,
-					ERROR_INTERFACE ".NotSupported",
-					"Operation is not supported");
-		return;
-	}
-
-	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
-		g_dbus_pending_property_error(id,
-					ERROR_INTERFACE ".InvalidArguments",
-					"Invalid arguments in method call");
-		return;
-	}
-
-	dbus_message_iter_get_basic(iter, &val);
-
-	if (val < t->min || val > t->max) {
-		g_dbus_pending_property_error(id,
-					ERROR_INTERFACE ".InvalidArguments",
-					"Invalid arguments in method call");
-		return;
-	}
-
-	put_le16(val, &atval[0]);
-
-	interval_data = g_new0(struct tmp_interval_data, 1);
-	interval_data->thermometer = t;
-	interval_data->interval = val;
-	gatt_write_char(t->attrib, t->interval_val_handle, atval, sizeof(atval),
-					write_interval_cb, interval_data);
-
-	g_dbus_pending_property_success(id);
-}
-
-static gboolean property_exists_interval(const GDBusPropertyTable *property,
-								void *data)
-{
-	struct thermometer *t = data;
-
-	return t->has_interval;
-}
-
-static gboolean property_get_maximum(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct thermometer *t = data;
-
-	if (!t->has_interval)
-		return FALSE;
-
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->max);
-
-	return TRUE;
-}
-
-static gboolean property_get_minimum(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct thermometer *t = data;
-
-	if (!t->has_interval)
-		return FALSE;
-
-	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->min);
-
-	return TRUE;
-}
-
-static const GDBusPropertyTable thermometer_properties[] = {
-	{ "Intermediate", "b", property_get_intermediate },
-	{ "Interval", "q", property_get_interval, property_set_interval,
-						property_exists_interval },
-	{ "Maximum", "q", property_get_maximum, NULL,
-						property_exists_interval },
-	{ "Minimum", "q", property_get_minimum, NULL,
-						property_exists_interval },
-	{ }
-};
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
-	struct thermometer *t = user_data;
-
-	t->attrib = g_attrib_ref(attrib);
-
-	gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
-					NULL, configure_thermometer_cb, t);
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
-	struct thermometer *t = user_data;
-
-	DBG("GATT Disconnected");
-
-	if (t->attio_measurement_id > 0) {
-		g_attrib_unregister(t->attrib, t->attio_measurement_id);
-		t->attio_measurement_id = 0;
-	}
-
-	if (t->attio_intermediate_id > 0) {
-		g_attrib_unregister(t->attrib, t->attio_intermediate_id);
-		t->attio_intermediate_id = 0;
-	}
-
-	if (t->attio_interval_id > 0) {
-		g_attrib_unregister(t->attrib, t->attio_interval_id);
-		t->attio_interval_id = 0;
-	}
-
-	g_attrib_unref(t->attrib);
-	t->attrib = NULL;
-}
-
-static int thermometer_register(struct btd_device *device,
-						struct gatt_primary *tattr)
-{
-	const char *path = device_get_path(device);
-	struct thermometer *t;
-	struct btd_adapter *adapter;
-	struct thermometer_adapter *tadapter;
-
-	adapter = device_get_adapter(device);
-
-	tadapter = find_thermometer_adapter(adapter);
-
-	if (tadapter == NULL)
-		return -1;
-
-	t = g_new0(struct thermometer, 1);
-	t->dev = btd_device_ref(device);
-	t->tadapter = tadapter;
-	t->svc_range = g_new0(struct att_range, 1);
-	t->svc_range->start = tattr->range.start;
-	t->svc_range->end = tattr->range.end;
-
-	tadapter->devices = g_slist_prepend(tadapter->devices, t);
-
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
-				path, THERMOMETER_INTERFACE,
-				NULL, NULL, thermometer_properties,
-				t, destroy_thermometer)) {
-		error("D-Bus failed to register %s interface",
-							THERMOMETER_INTERFACE);
-		destroy_thermometer(t);
-		return -EIO;
-	}
-
-	t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
-						attio_disconnected_cb, t);
-	return 0;
-}
-
-static void thermometer_unregister(struct btd_device *device)
-{
-	struct thermometer *t;
-	struct btd_adapter *adapter;
-	struct thermometer_adapter *tadapter;
-	GSList *l;
-
-	adapter = device_get_adapter(device);
-
-	tadapter = find_thermometer_adapter(adapter);
-
-	if (tadapter == NULL)
-		return;
-
-	l = g_slist_find_custom(tadapter->devices, device, cmp_device);
-	if (l == NULL)
-		return;
-
-	t = l->data;
-
-	tadapter->devices = g_slist_remove(tadapter->devices, t);
-
-	g_dbus_unregister_interface(btd_get_dbus_connection(),
-				device_get_path(t->dev), THERMOMETER_INTERFACE);
-}
-
-static const GDBusMethodTable thermometer_manager_methods[] = {
-	{ GDBUS_METHOD("RegisterWatcher",
-			GDBUS_ARGS({ "agent", "o" }), NULL,
-			register_watcher) },
-	{ GDBUS_METHOD("UnregisterWatcher",
-			GDBUS_ARGS({ "agent", "o" }), NULL,
-			unregister_watcher) },
-	{ GDBUS_METHOD("EnableIntermediateMeasurement",
-			GDBUS_ARGS({ "agent", "o" }), NULL,
-			enable_intermediate) },
-	{ GDBUS_METHOD("DisableIntermediateMeasurement",
-			GDBUS_ARGS({ "agent", "o" }), NULL,
-			disable_intermediate) },
-	{ }
-};
-
-static int thermometer_adapter_register(struct btd_adapter *adapter)
-{
-	struct thermometer_adapter *tadapter;
-
-	tadapter = g_new0(struct thermometer_adapter, 1);
-	tadapter->adapter = adapter;
-
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
-						adapter_get_path(adapter),
-						THERMOMETER_MANAGER_INTERFACE,
-						thermometer_manager_methods,
-						NULL, NULL, tadapter,
-						destroy_thermometer_adapter)) {
-		error("D-Bus failed to register %s interface",
-						THERMOMETER_MANAGER_INTERFACE);
-		destroy_thermometer_adapter(tadapter);
-		return -EIO;
-	}
-
-	thermometer_adapters = g_slist_prepend(thermometer_adapters, tadapter);
-
-	return 0;
-}
-
-static void thermometer_adapter_unregister(struct btd_adapter *adapter)
-{
-	struct thermometer_adapter *tadapter;
-
-	tadapter = find_thermometer_adapter(adapter);
-	if (tadapter == NULL)
-		return;
-
-	thermometer_adapters = g_slist_remove(thermometer_adapters, tadapter);
-
-	g_dbus_unregister_interface(btd_get_dbus_connection(),
-					adapter_get_path(tadapter->adapter),
-					THERMOMETER_MANAGER_INTERFACE);
-}
-
-static int thermometer_device_probe(struct btd_service *service)
-{
-	struct btd_device *device = btd_service_get_device(service);
-	struct gatt_primary *tattr;
-
-	tattr = btd_device_get_primary(device, HEALTH_THERMOMETER_UUID);
-	if (tattr == NULL)
-		return -EINVAL;
-
-	return thermometer_register(device, tattr);
-}
-
-static void thermometer_device_remove(struct btd_service *service)
-{
-	struct btd_device *device = btd_service_get_device(service);
-
-	thermometer_unregister(device);
-}
-
-static int thermometer_adapter_probe(struct btd_profile *p,
-						struct btd_adapter *adapter)
-{
-	return thermometer_adapter_register(adapter);
-}
-
-static void thermometer_adapter_remove(struct btd_profile *p,
-						struct btd_adapter *adapter)
-{
-	thermometer_adapter_unregister(adapter);
-}
-
-static struct btd_profile thermometer_profile = {
-	.name		= "Health Thermometer GATT driver",
-	.remote_uuid	= HEALTH_THERMOMETER_UUID,
-	.device_probe	= thermometer_device_probe,
-	.device_remove	= thermometer_device_remove,
-	.adapter_probe	= thermometer_adapter_probe,
-	.adapter_remove	= thermometer_adapter_remove
-};
-
-static int thermometer_init(void)
-{
-	return btd_profile_register(&thermometer_profile);
-}
-
-static void thermometer_exit(void)
-{
-	btd_profile_unregister(&thermometer_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
-					thermometer_init, thermometer_exit)
diff --git a/profiles/time/server.c b/profiles/time/server.c
deleted file mode 100644
index 2289c6a..0000000
--- a/profiles/time/server.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2011  Nokia Corporation
- *  Copyright (C) 2011  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
-
-#include <time.h>
-#include <errno.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/plugin.h"
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-#include "attrib/att-database.h"
-#include "src/shared/util.h"
-#include "src/attrib-server.h"
-#include "attrib/gatt-service.h"
-#include "src/log.h"
-
-#define CURRENT_TIME_SVC_UUID		0x1805
-#define REF_TIME_UPDATE_SVC_UUID	0x1806
-
-#define LOCAL_TIME_INFO_CHR_UUID	0x2A0F
-#define TIME_UPDATE_CTRL_CHR_UUID	0x2A16
-#define TIME_UPDATE_STAT_CHR_UUID	0x2A17
-#define CT_TIME_CHR_UUID		0x2A2B
-
-enum {
-	UPDATE_RESULT_SUCCESSFUL = 0,
-	UPDATE_RESULT_CANCELED = 1,
-	UPDATE_RESULT_NO_CONN = 2,
-	UPDATE_RESULT_ERROR = 3,
-	UPDATE_RESULT_TIMEOUT = 4,
-	UPDATE_RESULT_NOT_ATTEMPTED = 5,
-};
-
-enum {
-	UPDATE_STATE_IDLE = 0,
-	UPDATE_STATE_PENDING = 1,
-};
-
-enum {
-	GET_REFERENCE_UPDATE = 1,
-	CANCEL_REFERENCE_UPDATE = 2,
-};
-
-static int encode_current_time(uint8_t value[10])
-{
-	struct timespec tp;
-	struct tm tm;
-
-	if (clock_gettime(CLOCK_REALTIME, &tp) == -1) {
-		int err = -errno;
-
-		error("clock_gettime: %s", strerror(-err));
-		return err;
-	}
-
-	if (localtime_r(&tp.tv_sec, &tm) == NULL) {
-		error("localtime_r() failed");
-		/* localtime_r() does not set errno */
-		return -EINVAL;
-	}
-
-	put_le16(1900 + tm.tm_year, &value[0]); /* Year */
-	value[2] = tm.tm_mon + 1; /* Month */
-	value[3] = tm.tm_mday; /* Day */
-	value[4] = tm.tm_hour; /* Hours */
-	value[5] = tm.tm_min; /* Minutes */
-	value[6] = tm.tm_sec; /* Seconds */
-	value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */
-	/* From Time Profile spec: "The number of 1/256 fractions of a second."
-	 * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions.
-	 * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */
-	value[8] = tp.tv_nsec / 3906250; /* Fractions256 */
-	value[9] = 0x00; /* Adjust Reason */
-
-	return 0;
-}
-
-static uint8_t current_time_read(struct attribute *a,
-				 struct btd_device *device, gpointer user_data)
-{
-	struct btd_adapter *adapter = user_data;
-	uint8_t value[10];
-
-	if (encode_current_time(value) < 0)
-		return ATT_ECODE_IO;
-
-	attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
-
-	return 0;
-}
-
-static uint8_t local_time_info_read(struct attribute *a,
-				struct btd_device *device, gpointer user_data)
-{
-	struct btd_adapter *adapter = user_data;
-	uint8_t value[2];
-
-	DBG("a=%p", a);
-
-	tzset();
-
-	/* Convert POSIX "timezone" (seconds West of GMT) to Time Profile
-	 * format (offset from UTC in number of 15 minutes increments). */
-	value[0] = (uint8_t) (-1 * timezone / (60 * 15));
-
-	/* FIXME: POSIX "daylight" variable only indicates whether there
-	 * is DST for the local time or not. The offset is unknown. */
-	value[1] = daylight ? 0xff : 0x00;
-
-	attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
-
-	return 0;
-}
-
-static gboolean register_current_time_service(struct btd_adapter *adapter)
-{
-	bt_uuid_t uuid;
-
-	bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID);
-
-	/* Current Time service */
-	return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
-				/* CT Time characteristic */
-				GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID,
-				GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
-							GATT_CHR_PROP_NOTIFY,
-				GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
-						current_time_read, adapter,
-
-				/* Local Time Information characteristic */
-				GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID,
-				GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
-				GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
-						local_time_info_read, adapter,
-
-				GATT_OPT_INVALID);
-}
-
-static uint8_t time_update_control(struct attribute *a,
-						struct btd_device *device,
-						gpointer user_data)
-{
-	DBG("handle 0x%04x", a->handle);
-
-	if (a->len != 1)
-		DBG("Invalid control point value size: %zu", a->len);
-
-	switch (a->data[0]) {
-	case GET_REFERENCE_UPDATE:
-		DBG("Get Reference Update");
-		break;
-	case CANCEL_REFERENCE_UPDATE:
-		DBG("Cancel Reference Update");
-		break;
-	default:
-		DBG("Unknown command: 0x%02x", a->data[0]);
-	}
-
-	return 0;
-}
-
-static uint8_t time_update_status(struct attribute *a,
-						struct btd_device *device,
-						gpointer user_data)
-{
-	struct btd_adapter *adapter = user_data;
-	uint8_t value[2];
-
-	DBG("handle 0x%04x", a->handle);
-
-	value[0] = UPDATE_STATE_IDLE;
-	value[1] = UPDATE_RESULT_SUCCESSFUL;
-	attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
-
-	return 0;
-}
-
-static gboolean register_ref_time_update_service(struct btd_adapter *adapter)
-{
-	bt_uuid_t uuid;
-
-	bt_uuid16_create(&uuid, REF_TIME_UPDATE_SVC_UUID);
-
-	/* Reference Time Update service */
-	return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
-				/* Time Update control point */
-				GATT_OPT_CHR_UUID16, TIME_UPDATE_CTRL_CHR_UUID,
-				GATT_OPT_CHR_PROPS,
-					GATT_CHR_PROP_WRITE_WITHOUT_RESP,
-				GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
-						time_update_control, adapter,
-
-				/* Time Update status */
-				GATT_OPT_CHR_UUID16, TIME_UPDATE_STAT_CHR_UUID,
-				GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
-				GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
-						time_update_status, adapter,
-
-				GATT_OPT_INVALID);
-}
-
-static int time_server_init(struct btd_profile *p, struct btd_adapter *adapter)
-{
-	const char *path = adapter_get_path(adapter);
-
-	DBG("path %s", path);
-
-	if (!register_current_time_service(adapter)) {
-		error("Current Time Service could not be registered");
-		return -EIO;
-	}
-
-	if (!register_ref_time_update_service(adapter)) {
-		error("Reference Time Update Service could not be registered");
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static void time_server_exit(struct btd_profile *p,
-						struct btd_adapter *adapter)
-{
-	const char *path = adapter_get_path(adapter);
-
-	DBG("path %s", path);
-}
-
-struct btd_profile time_profile = {
-	.name		= "gatt-time-server",
-	.adapter_probe	= time_server_init,
-	.adapter_remove	= time_server_exit,
-};
-
-static int time_init(void)
-{
-	return btd_profile_register(&time_profile);
-}
-
-static void time_exit(void)
-{
-	btd_profile_unregister(&time_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(time, VERSION,
-			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
-			time_init, time_exit)
diff --git a/src/adapter.c b/src/adapter.c
index 3b21afa..e7cb86b 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -3141,6 +3141,125 @@
 	return param;
 }
 
+static int generate_and_write_irk(uint8_t *irk, GKeyFile *key_file,
+							const char *filename)
+{
+	struct bt_crypto *crypto;
+	char str_irk_out[33];
+	gsize length = 0;
+	char *str;
+	int i;
+
+	crypto = bt_crypto_new();
+	if (!crypto) {
+		error("Failed to open crypto");
+		return -1;
+	}
+
+	if (!bt_crypto_random_bytes(crypto, irk, 16)) {
+		error("Failed to generate IRK");
+		bt_crypto_unref(crypto);
+		return -1;
+	}
+
+	bt_crypto_unref(crypto);
+
+	for (i = 0; i < 16; i++)
+		sprintf(str_irk_out + (i * 2), "%02x", irk[i]);
+
+	str_irk_out[32] = '\0';
+	info("Generated IRK successfully");
+
+	g_key_file_set_string(key_file, "General", "IdentityResolvingKey",
+								str_irk_out);
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+	DBG("Generated IRK written to file");
+	return 0;
+}
+
+static int load_irk(struct btd_adapter *adapter, uint8_t *irk)
+{
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char address[18];
+	char *str_irk;
+	int ret;
+
+	ba2str(&adapter->bdaddr, address);
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/identity", address);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	str_irk = g_key_file_get_string(key_file, "General",
+						"IdentityResolvingKey", NULL);
+	if (!str_irk) {
+		info("No IRK for %s, creating new IRK", address);
+		ret = generate_and_write_irk(irk, key_file, filename);
+		g_key_file_free(key_file);
+		return ret;
+	}
+
+	g_key_file_free(key_file);
+
+	if (strlen(str_irk) != 32 || str2buf(str_irk, irk, 16)) {
+		/* TODO re-create new IRK here? */
+		error("Invalid IRK format, disabling privacy");
+		g_free(str_irk);
+		return -1;
+	}
+
+	g_free(str_irk);
+	DBG("Successfully read IRK from file");
+	return 0;
+}
+
+static void set_privacy_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id, "Failed to set privacy: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	DBG("Successfuly set privacy for index %u", adapter->dev_id);
+}
+
+static int set_privacy(struct btd_adapter *adapter, uint8_t privacy)
+{
+	struct mgmt_cp_set_privacy cp;
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (privacy) {
+		uint8_t irk[16];
+
+		if (load_irk(adapter, irk) == 0) {
+			cp.privacy = privacy;
+			memcpy(cp.irk, irk, 16);
+		}
+	}
+
+	DBG("sending set privacy command for index %u", adapter->dev_id);
+	DBG("setting privacy mode 0x%02x for index %u", cp.privacy,
+							adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PRIVACY,
+				adapter->dev_id, sizeof(cp), &cp,
+				set_privacy_complete, adapter, NULL) > 0)
+		return 0;
+
+	btd_error(adapter->dev_id, "Failed to set privacy for index %u",
+							adapter->dev_id);
+
+	return -1;
+}
+
 static void load_link_keys_complete(uint8_t status, uint16_t length,
 					const void *param, void *user_data)
 {
@@ -5488,8 +5607,11 @@
 	 * supports this we can make the non-zero check conditional.
 	 */
 	if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&
-					!(eir_data.flags & EIR_BREDR_UNSUP))
+					!(eir_data.flags & EIR_BREDR_UNSUP)) {
 		device_set_bredr_support(dev);
+		/* Update last seen for BR/EDR in case its flag is set */
+		device_update_last_seen(dev, BDADDR_BREDR);
+	}
 
 	if (eir_data.name != NULL && eir_data.name_complete)
 		device_store_cached_name(dev, eir_data.name);
@@ -5549,6 +5671,9 @@
 	if (eir_data.sd_list)
 		device_set_service_data(dev, eir_data.sd_list);
 
+	if (bdaddr_type != BDADDR_BREDR)
+		device_set_flags(dev, eir_data.flags);
+
 	eir_data_free(&eir_data);
 
 	/*
@@ -6177,7 +6302,7 @@
 		return;
 	}
 
-	err = device_confirm_passkey(device, btohl(ev->value),
+	err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
 							ev->confirm_hint);
 	if (err < 0) {
 		btd_error(adapter->dev_id,
@@ -6251,7 +6376,7 @@
 		return;
 	}
 
-	err = device_request_passkey(device);
+	err = device_request_passkey(device, ev->addr.type);
 	if (err < 0) {
 		btd_error(adapter->dev_id,
 				"device_request_passkey: %s", strerror(-err));
@@ -6290,7 +6415,8 @@
 
 	DBG("passkey %06u entered %u", passkey, ev->entered);
 
-	err = device_notify_passkey(device, passkey, ev->entered);
+	err = device_notify_passkey(device, ev->addr.type, passkey,
+								ev->entered);
 	if (err < 0)
 		btd_error(adapter->dev_id,
 				"device_notify_passkey: %s", strerror(-err));
@@ -7904,6 +8030,9 @@
 	if (missing_settings & MGMT_SETTING_SECURE_CONN)
 		set_mode(adapter, MGMT_OP_SET_SECURE_CONN, 0x01);
 
+	if (adapter->supported_settings & MGMT_SETTING_PRIVACY)
+		set_privacy(adapter, main_opts.privacy);
+
 	if (main_opts.fast_conn &&
 			(missing_settings & MGMT_SETTING_FAST_CONNECTABLE))
 		set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01);
@@ -8088,7 +8217,7 @@
 
 	adapter = btd_adapter_new(index);
 	if (!adapter) {
-		btd_error(adapter->dev_id,
+		btd_error(index,
 			"Unable to create new adapter for index %u", index);
 		return;
 	}
diff --git a/src/advertising.c b/src/advertising.c
index d2019de..e5cd31c 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -288,7 +288,7 @@
 
 	while (dbus_message_iter_get_arg_type(&entries)
 						== DBUS_TYPE_DICT_ENTRY) {
-		DBusMessageIter value, entry;
+		DBusMessageIter value, entry, array;
 		uint16_t manuf_id;
 		uint8_t *manuf_data;
 		int len;
@@ -297,15 +297,17 @@
 		dbus_message_iter_get_basic(&entry, &manuf_id);
 
 		dbus_message_iter_next(&entry);
-		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
-			goto fail;
-
 		dbus_message_iter_recurse(&entry, &value);
 
-		if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+		if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
 			goto fail;
 
-		dbus_message_iter_get_fixed_array(&value, &manuf_data, &len);
+		dbus_message_iter_recurse(&value, &array);
+
+		if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+			goto fail;
+
+		dbus_message_iter_get_fixed_array(&array, &manuf_data, &len);
 
 		DBG("Adding ManufacturerData for %04x", manuf_id);
 
@@ -340,7 +342,7 @@
 
 	while (dbus_message_iter_get_arg_type(&entries)
 						== DBUS_TYPE_DICT_ENTRY) {
-		DBusMessageIter value, entry;
+		DBusMessageIter value, entry, array;
 		const char *uuid_str;
 		bt_uuid_t uuid;
 		uint8_t *service_data;
@@ -353,15 +355,17 @@
 			goto fail;
 
 		dbus_message_iter_next(&entry);
-		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
-			goto fail;
-
 		dbus_message_iter_recurse(&entry, &value);
 
-		if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+		if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
 			goto fail;
 
-		dbus_message_iter_get_fixed_array(&value, &service_data, &len);
+		dbus_message_iter_recurse(&value, &array);
+
+		if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+			goto fail;
+
+		dbus_message_iter_get_fixed_array(&array, &service_data, &len);
 
 		DBG("Adding ServiceData for %s", uuid_str);
 
diff --git a/src/device.c b/src/device.c
index 5198c6d..6ed9509 100644
--- a/src/device.c
+++ b/src/device.c
@@ -60,7 +60,6 @@
 #include "adapter.h"
 #include "gatt-database.h"
 #include "attrib/gattrib.h"
-#include "attio.h"
 #include "device.h"
 #include "gatt-client.h"
 #include "profile.h"
@@ -80,6 +79,7 @@
 
 #define DISCONNECT_TIMER	2
 #define DISCOVERY_TIMER		1
+#define INVALID_FLAGS		0xff
 
 #ifndef MIN
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
@@ -127,6 +127,7 @@
 	auth_type_t type;
 	struct agent *agent;
 	struct btd_device *device;
+	uint8_t addr_type;
 	uint32_t passkey;
 	char *pincode;
 	gboolean secure;
@@ -150,13 +151,6 @@
 	GSList *current;
 };
 
-struct attio_data {
-	guint id;
-	attio_connect_cb cfunc;
-	attio_disconnect_cb dcfunc;
-	gpointer user_data;
-};
-
 struct svc_callback {
 	unsigned int id;
 	guint idle_id;
@@ -165,15 +159,6 @@
 	void *user_data;
 };
 
-typedef void (*attio_error_cb) (const GError *gerr, gpointer user_data);
-typedef void (*attio_success_cb) (gpointer user_data);
-
-struct att_callbacks {
-	attio_error_cb err;		/* Callback for error */
-	attio_success_cb success;	/* Callback for success */
-	gpointer user_data;
-};
-
 /* Per-bearer (LE or BR/EDR) device state */
 struct bearer_state {
 	bool paired;
@@ -200,6 +185,7 @@
 	GSList		*svc_callbacks;
 	GSList		*eir_uuids;
 	struct bt_ad	*ad;
+	uint8_t         ad_flags[1];
 	char		name[MAX_NAME_LENGTH + 1];
 	char		*alias;
 	uint32_t	class;
@@ -225,8 +211,6 @@
 	DBusMessage	*connect;		/* connect message */
 	DBusMessage	*disconnect;		/* disconnect message */
 	GAttrib		*attrib;
-	GSList		*attios;
-	GSList		*attios_offline;
 
 	struct bt_att *att;			/* The new ATT transport */
 	uint16_t att_mtu;			/* The ATT MTU */
@@ -625,8 +609,6 @@
 
 	g_slist_free_full(device->uuids, g_free);
 	g_slist_free_full(device->primaries, g_free);
-	g_slist_free_full(device->attios, g_free);
-	g_slist_free_full(device->attios_offline, g_free);
 	g_slist_free_full(device->svc_callbacks, svc_dev_remove);
 
 	/* Reset callbacks since the device is going to be freed */
@@ -964,6 +946,31 @@
 	return TRUE;
 }
 
+static gboolean dev_property_flags_exist(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_device *device = data;
+
+	return device->ad_flags[0] != INVALID_FLAGS;
+}
+
+static gboolean
+dev_property_get_flags(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	uint8_t *flags = device->ad_flags;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_BYTE_AS_STRING, &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+					&flags,	sizeof(device->ad_flags));
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
 static gboolean dev_property_get_trusted(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
@@ -1786,6 +1793,16 @@
 	time_t bredr_last = NVAL_TIME, le_last = NVAL_TIME;
 	time_t current = time(NULL);
 
+	/* Prefer bonded bearer in case only one is bonded */
+	if (dev->bredr_state.bonded && !dev->le_state.bonded )
+		return BDADDR_BREDR;
+	else if (!dev->bredr_state.bonded && dev->le_state.bonded)
+		return dev->bdaddr_type;
+
+	/* If the address is random it can only be connected over LE */
+	if (dev->bdaddr_type == BDADDR_LE_RANDOM)
+		return dev->bdaddr_type;
+
 	if (dev->bredr_seen) {
 		bredr_last = current - dev->bredr_seen;
 		if (bredr_last > SEEN_TRESHHOLD)
@@ -1807,7 +1824,11 @@
 	if (dev->le && (!dev->bredr || bredr_last == NVAL_TIME))
 		return dev->bdaddr_type;
 
-	if (bredr_last < le_last)
+	/*
+	 * Prefer BR/EDR if time is the same since it might be from an
+	 * advertisement with BR/EDR flag set.
+	 */
+	if (bredr_last <= le_last)
 		return BDADDR_BREDR;
 
 	return dev->bdaddr_type;
@@ -1819,9 +1840,18 @@
 	struct btd_device *dev = user_data;
 	uint8_t bdaddr_type;
 
-	if (dev->bredr_state.connected)
-		bdaddr_type = dev->bdaddr_type;
-	else if (dev->le_state.connected && dev->bredr)
+	if (dev->bredr_state.connected) {
+		/*
+		 * Check if services have been resolved and there is at list
+		 * one connected before switching to connect LE.
+		 */
+		if (dev->bredr_state.svc_resolved &&
+			find_service_with_state(dev->services,
+						BTD_SERVICE_STATE_CONNECTED))
+			bdaddr_type = dev->bdaddr_type;
+		else
+			bdaddr_type = BDADDR_BREDR;
+	} else if (dev->le_state.connected && dev->bredr)
 		bdaddr_type = BDADDR_BREDR;
 	else
 		bdaddr_type = select_conn_bearer(dev);
@@ -2079,6 +2109,7 @@
 
 	sprintf(handle, "%04hx", handle_num);
 
+	gatt_db_attribute_get_service_uuid(service, &uuid);
 	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
 	sprintf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", start,
 								end, uuid_str);
@@ -2355,6 +2386,35 @@
 	}
 }
 
+static void bonding_request_free(struct bonding_req *bonding)
+{
+	if (!bonding)
+		return;
+
+	if (bonding->listener_id)
+		g_dbus_remove_watch(dbus_conn, bonding->listener_id);
+
+	if (bonding->msg)
+		dbus_message_unref(bonding->msg);
+
+	if (bonding->cb_iter)
+		g_free(bonding->cb_iter);
+
+	if (bonding->agent) {
+		agent_cancel(bonding->agent);
+		agent_unref(bonding->agent);
+		bonding->agent = NULL;
+	}
+
+	if (bonding->retry_timer)
+		g_source_remove(bonding->retry_timer);
+
+	if (bonding->device)
+		bonding->device->bonding = NULL;
+
+	g_free(bonding);
+}
+
 static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
 								void *data)
 {
@@ -2425,8 +2485,10 @@
 							BDADDR_BREDR, io_cap);
 	}
 
-	if (err < 0)
+	if (err < 0) {
+		bonding_request_free(device->bonding);
 		return btd_error_failed(msg, strerror(-err));
+	}
 
 	return NULL;
 }
@@ -2467,35 +2529,6 @@
 	}
 }
 
-static void bonding_request_free(struct bonding_req *bonding)
-{
-	if (!bonding)
-		return;
-
-	if (bonding->listener_id)
-		g_dbus_remove_watch(dbus_conn, bonding->listener_id);
-
-	if (bonding->msg)
-		dbus_message_unref(bonding->msg);
-
-	if (bonding->cb_iter)
-		g_free(bonding->cb_iter);
-
-	if (bonding->agent) {
-		agent_cancel(bonding->agent);
-		agent_unref(bonding->agent);
-		bonding->agent = NULL;
-	}
-
-	if (bonding->retry_timer)
-		g_source_remove(bonding->retry_timer);
-
-	if (bonding->device)
-		bonding->device->bonding = NULL;
-
-	g_free(bonding);
-}
-
 static void device_cancel_bonding(struct btd_device *device, uint8_t status)
 {
 	struct bonding_req *bonding = device->bonding;
@@ -2568,16 +2601,15 @@
 						dev_property_exists_modalias },
 	{ "Adapter", "o", dev_property_get_adapter },
 	{ "ManufacturerData", "a{qv}", dev_property_get_manufacturer_data,
-				NULL, dev_property_manufacturer_data_exist,
-				G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+				NULL, dev_property_manufacturer_data_exist },
 	{ "ServiceData", "a{sv}", dev_property_get_service_data,
-				NULL, dev_property_service_data_exist,
-				G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+				NULL, dev_property_service_data_exist },
 	{ "TxPower", "n", dev_property_get_tx_power, NULL,
-					dev_property_exists_tx_power,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+					dev_property_exists_tx_power },
+	{ "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL },
+	{ "AdvertisingFlags", "ay", dev_property_get_flags, NULL,
+					dev_property_flags_exist,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL},
 
 	{ }
 };
@@ -3598,6 +3630,8 @@
 		return NULL;
 	}
 
+	memset(device->ad_flags, INVALID_FLAGS, sizeof(device->ad_flags));
+
 	device->ad = bt_ad_new();
 	if (!device->ad) {
 		device_free(device);
@@ -4119,7 +4153,10 @@
 		return NULL;
 	}
 
-	if (profile->auto_connect)
+	/* Only set auto connect if profile has set the flag and can really
+	 * accept connections.
+	 */
+	if (profile->auto_connect && profile->accept)
 		device_set_auto_connect(device, TRUE);
 
 	return service;
@@ -4568,25 +4605,16 @@
 	return device->auto_connect;
 }
 
-static void attio_connected(gpointer data, gpointer user_data)
+static void disconnect_gatt_service(gpointer data, gpointer user_data)
 {
-	struct attio_data *attio = data;
-	GAttrib *attrib = user_data;
+	struct btd_service *service = data;
+	struct btd_profile *profile = btd_service_get_profile(service);
 
-	DBG("");
+	/* Ignore if profile cannot accept connections */
+	if (!profile->accept)
+		return;
 
-	if (attio->cfunc)
-		attio->cfunc(attrib, attio->user_data);
-}
-
-static void attio_disconnected(gpointer data, gpointer user_data)
-{
-	struct attio_data *attio = data;
-
-	DBG("");
-
-	if (attio->dcfunc)
-		attio->dcfunc(attio->user_data);
+	btd_service_disconnect(service);
 }
 
 static void att_disconnected_cb(int err, void *user_data)
@@ -4600,7 +4628,7 @@
 
 	DBG("%s (%d)", strerror(err), err);
 
-	g_slist_foreach(device->attios, attio_disconnected, NULL);
+	g_slist_foreach(device->services, disconnect_gatt_service, NULL);
 
 	btd_gatt_client_disconnected(device->client_dbus);
 
@@ -4696,9 +4724,6 @@
 
 	bt_gatt_client_set_debug(device->client, gatt_debug, NULL, NULL);
 
-	/* Notify attio so it can react to notifications */
-	g_slist_foreach(device->attios, attio_connected, device->attrib);
-
 	/*
 	 * Notify notify existing service about the new connection so they can
 	 * react to notifications while discovering services
@@ -4804,7 +4829,7 @@
 	}
 
 	dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU);
-	attrib = g_attrib_new(io, dev->att_mtu, false);
+	attrib = g_attrib_new(io, BT_ATT_DEFAULT_LE_MTU, false);
 	if (!attrib) {
 		error("Unable to create new GAttrib instance");
 		return false;
@@ -4835,12 +4860,12 @@
 	dst = device_get_address(dev);
 	ba2str(dst, dstaddr);
 
-	gatt_client_init(dev);
-	gatt_server_init(dev, btd_gatt_database_get_db(database));
-
 	if (gatt_db_isempty(dev->db))
 		load_gatt_db(dev, srcaddr, dstaddr);
 
+	gatt_client_init(dev);
+	gatt_server_init(dev, btd_gatt_database_get_db(database));
+
 	/*
 	 * Remove the device from the connect_list and give the passive
 	 * scanning another chance to be restarted in case there are
@@ -4853,8 +4878,7 @@
 
 static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
 {
-	struct att_callbacks *attcb = user_data;
-	struct btd_device *device = attcb->user_data;
+	struct btd_device *device = user_data;
 	DBusMessage *reply;
 	uint8_t io_cap;
 	int err = 0;
@@ -4865,8 +4889,20 @@
 	if (gerr) {
 		DBG("%s", gerr->message);
 
-		if (attcb->err)
-			attcb->err(gerr, user_data);
+		if (g_error_matches(gerr, BT_IO_ERROR, ECONNABORTED))
+			goto done;
+
+		if (device_get_auto_connect(device)) {
+			DBG("Enabling automatic connections");
+			adapter_connect_list_add(device->adapter, device);
+		}
+
+		if (device->browse) {
+			browse_request_complete(device->browse,
+						device->bdaddr_type,
+						-ECONNABORTED);
+			device->browse = NULL;
+		}
 
 		err = -ECONNABORTED;
 		goto done;
@@ -4875,9 +4911,6 @@
 	if (!device_attach_att(device, io))
 		goto done;
 
-	if (attcb->success)
-		attcb->success(user_data);
-
 	if (!device->bonding)
 		goto done;
 
@@ -4910,28 +4943,11 @@
 		dbus_message_unref(device->connect);
 		device->connect = NULL;
 	}
-
-	g_free(attcb);
-}
-
-static void att_error_cb(const GError *gerr, gpointer user_data)
-{
-	struct att_callbacks *attcb = user_data;
-	struct btd_device *device = attcb->user_data;
-
-	if (g_error_matches(gerr, BT_IO_ERROR, ECONNABORTED))
-		return;
-
-	if (device_get_auto_connect(device)) {
-		DBG("Enabling automatic connections");
-		adapter_connect_list_add(device->adapter, device);
-	}
 }
 
 int device_connect_le(struct btd_device *dev)
 {
 	struct btd_adapter *adapter = dev->adapter;
-	struct att_callbacks *attcb;
 	BtIOSecLevel sec_level;
 	GIOChannel *io;
 	GError *gerr = NULL;
@@ -4945,10 +4961,6 @@
 
 	DBG("Connection attempt to: %s", addr);
 
-	attcb = g_new0(struct att_callbacks, 1);
-	attcb->err = att_error_cb;
-	attcb->user_data = dev;
-
 	if (dev->le_state.paired)
 		sec_level = BT_IO_SEC_MEDIUM;
 	else
@@ -4958,7 +4970,7 @@
 	 * This connection will help us catch any PDUs that comes before
 	 * pairing finishes
 	 */
-	io = bt_io_connect(att_connect_cb, attcb, NULL, &gerr,
+	io = bt_io_connect(att_connect_cb, dev, NULL, &gerr,
 			BT_IO_OPT_SOURCE_BDADDR,
 			btd_adapter_get_address(adapter),
 			BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
@@ -4980,7 +4992,6 @@
 
 		error("ATT bt_io_connect(%s): %s", addr, gerr->message);
 		g_error_free(gerr);
-		g_free(attcb);
 		return -EIO;
 	}
 
@@ -4990,21 +5001,6 @@
 	return 0;
 }
 
-static void att_browse_error_cb(const GError *gerr, gpointer user_data)
-{
-	struct att_callbacks *attcb = user_data;
-	struct btd_device *device = attcb->user_data;
-	struct browse_req *req = device->browse;
-
-	device->browse = NULL;
-	browse_request_complete(req, device->bdaddr_type, -ECONNABORTED);
-}
-
-static void att_browse_cb(gpointer user_data)
-{
-	DBG("ATT connection successful");
-}
-
 static struct browse_req *browse_request_new(struct btd_device *device,
 							DBusMessage *msg)
 {
@@ -5038,7 +5034,6 @@
 static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
 {
 	struct btd_adapter *adapter = device->adapter;
-	struct att_callbacks *attcb;
 	struct browse_req *req;
 
 	req = browse_request_new(device, msg);
@@ -5061,13 +5056,8 @@
 		return 0;
 	}
 
-	attcb = g_new0(struct att_callbacks, 1);
-	attcb->err = att_browse_error_cb;
-	attcb->success = att_browse_cb;
-	attcb->user_data = device;
-
 	device->att_io = bt_io_connect(att_connect_cb,
-				attcb, NULL, NULL,
+				device, NULL, NULL,
 				BT_IO_OPT_SOURCE_BDADDR,
 				btd_adapter_get_address(adapter),
 				BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
@@ -5080,7 +5070,6 @@
 	if (device->att_io == NULL) {
 		device->browse = NULL;
 		browse_request_free(req);
-		g_free(attcb);
 		return -EIO;
 	}
 
@@ -5336,6 +5325,22 @@
 						DEVICE_INTERFACE, "TxPower");
 }
 
+void device_set_flags(struct btd_device *device, uint8_t flags)
+{
+	if (!device)
+		return;
+
+	DBG("flags %d", flags);
+
+	if (device->ad_flags[0] == flags)
+		return;
+
+	device->ad_flags[0] = flags;
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+					DEVICE_INTERFACE, "AdvertisingFlags");
+}
+
 static gboolean start_discovery(gpointer user_data)
 {
 	struct btd_device *device = user_data;
@@ -5691,7 +5696,7 @@
 		return;
 
 	btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
-							device->bdaddr_type,
+							auth->addr_type,
 							err ? FALSE : TRUE);
 
 	agent_unref(device->authr->agent);
@@ -5712,7 +5717,7 @@
 		passkey = INVALID_PASSKEY;
 
 	btd_adapter_passkey_reply(device->adapter, &device->bdaddr,
-						device->bdaddr_type, passkey);
+						auth->addr_type, passkey);
 
 	agent_unref(device->authr->agent);
 	device->authr->agent = NULL;
@@ -5730,7 +5735,9 @@
 }
 
 static struct authentication_req *new_auth(struct btd_device *device,
-					auth_type_t type, gboolean secure)
+						uint8_t addr_type,
+						auth_type_t type,
+						gboolean secure)
 {
 	struct authentication_req *auth;
 	struct agent *agent;
@@ -5758,6 +5765,7 @@
 	auth->agent = agent;
 	auth->device = device;
 	auth->type = type;
+	auth->addr_type = addr_type;
 	auth->secure = secure;
 	device->authr = auth;
 
@@ -5769,7 +5777,7 @@
 	struct authentication_req *auth;
 	int err;
 
-	auth = new_auth(device, AUTH_TYPE_PINCODE, secure);
+	auth = new_auth(device, BDADDR_BREDR, AUTH_TYPE_PINCODE, secure);
 	if (!auth)
 		return -EPERM;
 
@@ -5783,12 +5791,12 @@
 	return err;
 }
 
-int device_request_passkey(struct btd_device *device)
+int device_request_passkey(struct btd_device *device, uint8_t type)
 {
 	struct authentication_req *auth;
 	int err;
 
-	auth = new_auth(device, AUTH_TYPE_PASSKEY, FALSE);
+	auth = new_auth(device, type, AUTH_TYPE_PASSKEY, FALSE);
 	if (!auth)
 		return -EPERM;
 
@@ -5802,14 +5810,13 @@
 	return err;
 }
 
-int device_confirm_passkey(struct btd_device *device, uint32_t passkey,
-							uint8_t confirm_hint)
-
+int device_confirm_passkey(struct btd_device *device, uint8_t type,
+					int32_t passkey, uint8_t confirm_hint)
 {
 	struct authentication_req *auth;
 	int err;
 
-	auth = new_auth(device, AUTH_TYPE_CONFIRM, FALSE);
+	auth = new_auth(device, type, AUTH_TYPE_CONFIRM, FALSE);
 	if (!auth)
 		return -EPERM;
 
@@ -5830,8 +5837,8 @@
 	return err;
 }
 
-int device_notify_passkey(struct btd_device *device, uint32_t passkey,
-							uint8_t entered)
+int device_notify_passkey(struct btd_device *device, uint8_t type,
+					uint32_t passkey, uint8_t entered)
 {
 	struct authentication_req *auth;
 	int err;
@@ -5841,7 +5848,7 @@
 		if (auth->type != AUTH_TYPE_NOTIFY_PASSKEY)
 			return -EPERM;
 	} else {
-		auth = new_auth(device, AUTH_TYPE_NOTIFY_PASSKEY, FALSE);
+		auth = new_auth(device, type, AUTH_TYPE_NOTIFY_PASSKEY, FALSE);
 		if (!auth)
 			return -EPERM;
 	}
@@ -5861,7 +5868,7 @@
 	struct authentication_req *auth;
 	int err;
 
-	auth = new_auth(device, AUTH_TYPE_NOTIFY_PINCODE, secure);
+	auth = new_auth(device, BDADDR_BREDR, AUTH_TYPE_NOTIFY_PINCODE, secure);
 	if (!auth)
 		return -EPERM;
 
@@ -5970,6 +5977,14 @@
 	return device->client;
 }
 
+void *btd_device_get_attrib(struct btd_device *device)
+{
+	if (!device)
+		return NULL;
+
+	return device->attrib;
+}
+
 struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device)
 {
 	if (!device)
@@ -6109,91 +6124,6 @@
 	store_device_info(device);
 }
 
-static gboolean notify_attios(gpointer user_data)
-{
-	struct btd_device *device = user_data;
-
-	DBG("");
-
-	if (device->attrib == NULL)
-		return FALSE;
-
-	g_slist_foreach(device->attios_offline, attio_connected, device->attrib);
-	device->attios = g_slist_concat(device->attios, device->attios_offline);
-	device->attios_offline = NULL;
-
-	return FALSE;
-}
-
-guint btd_device_add_attio_callback(struct btd_device *device,
-						attio_connect_cb cfunc,
-						attio_disconnect_cb dcfunc,
-						gpointer user_data)
-{
-	struct attio_data *attio;
-	static guint attio_id = 0;
-
-	DBG("%p registered ATT connection callback", device);
-
-	attio = g_new0(struct attio_data, 1);
-	attio->id = ++attio_id;
-	attio->cfunc = cfunc;
-	attio->dcfunc = dcfunc;
-	attio->user_data = user_data;
-
-	device_set_auto_connect(device, TRUE);
-
-	/* Check if there is no GAttrib associated to the device created by a
-	 * incoming connection */
-	if (!device->attrib)
-		device->attrib = attrib_from_device(device);
-
-	if (device->attrib && cfunc) {
-		device->attios_offline = g_slist_append(device->attios_offline,
-									attio);
-		g_idle_add(notify_attios, device);
-		return attio->id;
-	}
-
-	device->attios = g_slist_append(device->attios, attio);
-
-	return attio->id;
-}
-
-static int attio_id_cmp(gconstpointer a, gconstpointer b)
-{
-	const struct attio_data *attio = a;
-	guint id = GPOINTER_TO_UINT(b);
-
-	return attio->id - id;
-}
-
-gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)
-{
-	struct attio_data *attio;
-	GSList *l;
-
-	l = g_slist_find_custom(device->attios, GUINT_TO_POINTER(id),
-								attio_id_cmp);
-	if (l) {
-		attio = l->data;
-		device->attios = g_slist_remove(device->attios, attio);
-	} else {
-		l = g_slist_find_custom(device->attios_offline,
-					GUINT_TO_POINTER(id), attio_id_cmp);
-		if (!l)
-			return FALSE;
-
-		attio = l->data;
-		device->attios_offline = g_slist_remove(device->attios_offline,
-									attio);
-	}
-
-	g_free(attio);
-
-	return TRUE;
-}
-
 void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
 			uint16_t vendor, uint16_t product, uint16_t version)
 {
diff --git a/src/device.h b/src/device.h
index 35f0e96..bc5535c 100644
--- a/src/device.h
+++ b/src/device.h
@@ -70,6 +70,7 @@
 struct gatt_db *btd_device_get_gatt_db(struct btd_device *device);
 struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device);
 struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device);
+void *btd_device_get_attrib(struct btd_device *device);
 void btd_device_gatt_set_service_changed(struct btd_device *device,
 						uint16_t start, uint16_t end);
 bool device_attach_att(struct btd_device *dev, GIOChannel *io);
@@ -96,6 +97,7 @@
 							int8_t delta_threshold);
 void device_set_rssi(struct btd_device *device, int8_t rssi);
 void device_set_tx_power(struct btd_device *device, int8_t tx_power);
+void device_set_flags(struct btd_device *device, uint8_t flags);
 bool btd_device_is_connected(struct btd_device *dev);
 uint8_t btd_device_get_bdaddr_type(struct btd_device *dev);
 bool device_is_retrying(struct btd_device *device);
@@ -109,11 +111,11 @@
 long device_bonding_last_duration(struct btd_device *device);
 void device_bonding_restart_timer(struct btd_device *device);
 int device_request_pincode(struct btd_device *device, gboolean secure);
-int device_request_passkey(struct btd_device *device);
-int device_confirm_passkey(struct btd_device *device, uint32_t passkey,
-							uint8_t confirm_hint);
-int device_notify_passkey(struct btd_device *device, uint32_t passkey,
-							uint8_t entered);
+int device_request_passkey(struct btd_device *device, uint8_t type);
+int device_confirm_passkey(struct btd_device *device, uint8_t type,
+					int32_t passkey, uint8_t confirm_hint);
+int device_notify_passkey(struct btd_device *device, uint8_t type,
+					uint32_t passkey, uint8_t entered);
 int device_notify_pincode(struct btd_device *device, gboolean secure,
 							const char *pincode);
 void device_cancel_authentication(struct btd_device *device, gboolean aborted);
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 7abb306..114981c 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -184,7 +184,7 @@
 {
 	gboolean *ret = user_data;
 
-	if (err || length == 0) {
+	if (err) {
 		*ret = FALSE;
 		return;
 	}
@@ -297,7 +297,7 @@
 }
 
 static void async_dbus_op_reply(struct async_dbus_op *op, int err,
-				const uint8_t *value, size_t length)
+				const uint8_t *value, ssize_t length)
 {
 	const struct queue_entry *entry;
 	DBusMessage *reply;
@@ -309,7 +309,7 @@
 
 		if (err) {
 			reply = err > 0 ? create_gatt_dbus_error(msg, err) :
-				btd_error_failed(msg, strerror(err));
+				btd_error_failed(msg, strerror(-err));
 			goto send_reply;
 		}
 
@@ -319,7 +319,7 @@
 			return;
 		}
 
-		if (value)
+		if (length >= 0)
 			message_append_byte_array(reply, value, length);
 
 send_reply:
@@ -489,7 +489,7 @@
 	}
 
 done:
-	async_dbus_op_reply(op, err, NULL, 0);
+	async_dbus_op_reply(op, err, NULL, -1);
 }
 
 static void write_cb(bool success, uint8_t att_ecode, void *user_data)
@@ -613,22 +613,17 @@
 }
 
 static const GDBusPropertyTable descriptor_properties[] = {
-	{ "UUID", "s", descriptor_get_uuid, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Characteristic", "o", descriptor_get_characteristic, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "UUID", "s", descriptor_get_uuid },
+	{ "Characteristic", "o", descriptor_get_characteristic, },
+	{ "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists },
 	{ }
 };
 
 static const GDBusMethodTable descriptor_methods[] = {
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
-					GDBUS_ARGS({ "options", "a{sv}" }),
+	{ GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
 					GDBUS_ARGS({ "value", "ay" }),
 					descriptor_read_value) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
-					GDBUS_ARGS({ "value", "ay" },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
 						{ "options", "a{sv}" }),
 					NULL,
 					descriptor_write_value) },
@@ -1131,7 +1126,7 @@
 	else
 		err = att_ecode ? att_ecode : -ENOENT;
 
-	async_dbus_op_reply(op, err, NULL, 0);
+	async_dbus_op_reply(op, err, NULL, -1);
 }
 
 static void register_notify_cb(uint16_t att_ecode, void *user_data)
@@ -1251,34 +1246,27 @@
 }
 
 static const GDBusPropertyTable characteristic_properties[] = {
-	{ "UUID", "s", characteristic_get_uuid, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Service", "o", characteristic_get_service, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "UUID", "s", characteristic_get_uuid, NULL, NULL },
+	{ "Service", "o", characteristic_get_service, NULL, NULL },
 	{ "Value", "ay", characteristic_get_value, NULL,
-					characteristic_value_exists,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+					characteristic_value_exists },
 	{ "Notifying", "b", characteristic_get_notifying, NULL,
-					characteristic_notifying_exists,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Flags", "as", characteristic_get_flags, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+					characteristic_notifying_exists },
+	{ "Flags", "as", characteristic_get_flags, NULL, NULL },
 	{ }
 };
 
 static const GDBusMethodTable characteristic_methods[] = {
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
-					GDBUS_ARGS({ "options", "a{sv}" }),
+	{ GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
 					GDBUS_ARGS({ "value", "ay" }),
 					characteristic_read_value) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
-					GDBUS_ARGS({ "value", "ay" },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
 						{ "options", "a{sv}" }),
 					NULL,
 					characteristic_write_value) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL,
+	{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
 					characteristic_start_notify) },
-	{ GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL,
+	{ GDBUS_METHOD("StopNotify", NULL, NULL,
 					characteristic_stop_notify) },
 	{ }
 };
@@ -1411,12 +1399,9 @@
 }
 
 static const GDBusPropertyTable service_properties[] = {
-	{ "UUID", "s", service_get_uuid, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Device", "o", service_get_device, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Primary", "b", service_get_primary, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "UUID", "s", service_get_uuid },
+	{ "Device", "o", service_get_device },
+	{ "Primary", "b", service_get_primary },
 	{ }
 };
 
@@ -1584,12 +1569,6 @@
 
 static void create_services(struct btd_gatt_client *client)
 {
-	/* Don't attempt to create any objects if experimental is disabled */
-	if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) {
-		info("GATT service objects disabled");
-		return;
-	}
-
 	DBG("Exporting objects for GATT services: %s", client->devaddr);
 
 	gatt_db_foreach_service(client->db, NULL, export_service, client);
diff --git a/src/gatt-database.c b/src/gatt-database.c
index bf1925b..0877b25 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -149,6 +149,10 @@
 	struct queue *ccc_states;
 };
 
+typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
+							void *user_data);
+typedef void (*btd_gatt_database_destroy_t) (void *data);
+
 struct ccc_state {
 	uint16_t handle;
 	uint8_t value[2];
@@ -827,28 +831,6 @@
 	return ccc;
 }
 
-struct gatt_db_attribute *
-btd_gatt_database_add_ccc(struct btd_gatt_database *database,
-				uint16_t service_handle,
-				btd_gatt_database_ccc_write_t write_callback,
-				void *user_data,
-				btd_gatt_database_destroy_t destroy)
-{
-	struct gatt_db_attribute *service;
-
-	if (!database || !service_handle)
-		return NULL;
-
-	service = gatt_db_get_attribute(database->db, service_handle);
-	if (!service) {
-		error("No service exists with handle: 0x%04x", service_handle);
-		return NULL;
-	}
-
-	return service_add_ccc(service, database, write_callback, user_data,
-								destroy);
-}
-
 static void populate_gatt_service(struct btd_gatt_database *database)
 {
 	bt_uuid_t uuid;
@@ -897,6 +879,7 @@
 	struct notify *notify = user_data;
 	struct ccc_state *ccc;
 	struct btd_device *device;
+	struct bt_gatt_server *server;
 
 	ccc = find_ccc_state(device_state, notify->ccc_handle);
 	if (!ccc)
@@ -909,7 +892,14 @@
 						&device_state->bdaddr,
 						device_state->bdaddr_type);
 	if (!device)
+		goto remove;
+
+	server = btd_device_get_gatt_server(device);
+	if (!server) {
+		if (!device_is_paired(device, device_state->bdaddr_type))
+			goto remove;
 		return;
+	}
 
 	/*
 	 * TODO: If the device is not connected but bonded, send the
@@ -917,19 +907,23 @@
 	 */
 	if (!notify->indicate) {
 		DBG("GATT server sending notification");
-		bt_gatt_server_send_notification(
-					btd_device_get_gatt_server(device),
+		bt_gatt_server_send_notification(server,
 					notify->handle, notify->value,
 					notify->len);
 		return;
 	}
 
 	DBG("GATT server sending indication");
-	bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
-							notify->handle,
-							notify->value,
+	bt_gatt_server_send_indication(server, notify->handle, notify->value,
 							notify->len, conf_cb,
 							NULL, NULL);
+
+	return;
+
+remove:
+	/* Remove device state if device no longer exists or is not paired */
+	if (queue_remove(notify->database->device_states, device_state))
+		device_state_free(device_state);
 }
 
 static void send_notification_to_devices(struct btd_gatt_database *database,
@@ -2579,11 +2573,11 @@
 }
 
 static const GDBusMethodTable manager_methods[] = {
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterApplication",
-			GDBUS_ARGS({ "application", "o" },
-			{ "options", "a{sv}" }), NULL,
-			manager_register_app) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterApplication",
+	{ GDBUS_ASYNC_METHOD("RegisterApplication",
+					GDBUS_ARGS({ "application", "o" },
+						{ "options", "a{sv}" }),
+					NULL, manager_register_app) },
+	{ GDBUS_ASYNC_METHOD("UnregisterApplication",
 					GDBUS_ARGS({ "application", "o" }),
 					NULL, manager_unregister_app) },
 	{ }
diff --git a/src/gatt-database.h b/src/gatt-database.h
index 163b601..0d9106b 100644
--- a/src/gatt-database.h
+++ b/src/gatt-database.h
@@ -23,14 +23,3 @@
 void btd_gatt_database_destroy(struct btd_gatt_database *database);
 
 struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database);
-
-typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
-							void *user_data);
-typedef void (*btd_gatt_database_destroy_t) (void *data);
-
-struct gatt_db_attribute *
-btd_gatt_database_add_ccc(struct btd_gatt_database *database,
-				uint16_t service_handle,
-				btd_gatt_database_ccc_write_t write_callback,
-				void *user_data,
-				btd_gatt_database_destroy_t destroy);
diff --git a/src/hcid.h b/src/hcid.h
index 60e2b0a..0b785ee 100644
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -35,6 +35,8 @@
 	uint16_t	autoto;
 	uint32_t	pairto;
 	uint32_t	discovto;
+	uint8_t		privacy;
+
 	gboolean	reverse_sdp;
 	gboolean	name_resolv;
 	gboolean	debug_keys;
diff --git a/src/main.c b/src/main.c
index c85bf19..21ca830 100644
--- a/src/main.c
+++ b/src/main.c
@@ -89,6 +89,7 @@
 	"DebugKeys",
 	"ControllerMode",
 	"MultiProfile",
+	"Privacy",
 };
 
 GKeyFile *btd_get_main_conf(void)
@@ -255,6 +256,26 @@
 		main_opts.autoto = val;
 	}
 
+	str = g_key_file_get_string(config, "General", "Privacy", &err);
+	if (err) {
+		DBG("%s", err->message);
+		g_clear_error(&err);
+		main_opts.privacy = 0x00;
+	} else {
+		DBG("privacy=%s", str);
+
+		if (!strcmp(str, "device"))
+			main_opts.privacy = 0x01;
+		else if (!strcmp(str, "off"))
+			main_opts.privacy = 0x00;
+		else {
+			DBG("Invalid privacy option: %s", str);
+			main_opts.privacy = 0x00;
+		}
+
+		g_free(str);
+	}
+
 	str = g_key_file_get_string(config, "General", "Name", &err);
 	if (err) {
 		DBG("%s", err->message);
diff --git a/src/main.conf b/src/main.conf
index 372fd8c..d9cd9e0 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -64,6 +64,13 @@
 # 'false'.
 #FastConnectable = false
 
+# Default privacy setting.
+# Enables use of private address.
+# Possible values: "off", "device", "network"
+# "network" option not supported currently
+# Defaults to "off"
+# Privacy = off
+
 #[Policy]
 #
 # The ReconnectUUIDs defines the set of remote services that should try
@@ -71,7 +78,7 @@
 # timeout). The policy plugin should contain a sane set of values by
 # default, but this list can be overridden here. By setting the list to
 # empty the reconnection feature gets disabled.
-#ReconnectUUIDs=00001112-0000-1000-8000-00805f9b34fb, 0000111f-0000-1000-8000-00805f9b34fb, 0000110a-0000-1000-8000-00805f9b34fb
+#ReconnectUUIDs=00001112-0000-1000-8000-00805f9b34fb,0000111f-0000-1000-8000-00805f9b34fb,0000110a-0000-1000-8000-00805f9b34fb
 
 # ReconnectAttempts define the number of attempts to reconnect after a link
 # lost. Setting the value to 0 disables reconnecting feature.
@@ -81,7 +88,7 @@
 # attempts.
 # If the number of attempts defined in ReconnectAttempts is bigger than the
 # set of intervals the last interval is repeated until the last attempt.
-#ReconnectIntervals=1, 2, 4, 8, 16, 32, 64
+#ReconnectIntervals=1,2,4,8,16,32,64
 
 # AutoEnable defines option to enable all controllers when they are found.
 # This includes adapters present on start as well as adapters that are plugged
diff --git a/src/profile.c b/src/profile.c
index c81a9f9..7c5318c 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -1047,7 +1047,7 @@
 									conn);
 	}
 
-	if (conn->service && service_accept(conn->service) < 0)
+	if (conn->service && service_set_connecting(conn->service) < 0)
 		goto drop;
 
 	if (send_new_connection(ext, conn))
diff --git a/src/sdpd-server.c b/src/sdpd-server.c
index c863508..54de393 100644
--- a/src/sdpd-server.c
+++ b/src/sdpd-server.c
@@ -164,7 +164,7 @@
 	}
 
 	len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
-	if (len != sizeof(sdp_pdu_hdr_t)) {
+	if (len < 0 || (unsigned int) len < sizeof(sdp_pdu_hdr_t)) {
 		sdp_svcdb_collect_all(sk);
 		return FALSE;
 	}
diff --git a/src/service.c b/src/service.c
index 0da14ab..207ffae 100644
--- a/src/service.c
+++ b/src/service.c
@@ -197,7 +197,7 @@
 	}
 
 	if (!service->profile->accept)
-		goto done;
+		return -ENOSYS;
 
 	err = service->profile->accept(service);
 	if (!err)
@@ -209,7 +209,27 @@
 	return err;
 
 done:
+	if (service->state == BTD_SERVICE_STATE_DISCONNECTED)
+		change_state(service, BTD_SERVICE_STATE_CONNECTING, 0);
+	return 0;
+}
+
+int service_set_connecting(struct btd_service *service)
+{
+	switch (service->state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		return -EINVAL;
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		break;
+	case BTD_SERVICE_STATE_CONNECTING:
+	case BTD_SERVICE_STATE_CONNECTED:
+		return 0;
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		return -EBUSY;
+	}
+
 	change_state(service, BTD_SERVICE_STATE_CONNECTING, 0);
+
 	return 0;
 }
 
diff --git a/src/service.h b/src/service.h
index c1f97f6..6f1edfb 100644
--- a/src/service.h
+++ b/src/service.h
@@ -49,6 +49,7 @@
 void service_remove(struct btd_service *service);
 
 int service_accept(struct btd_service *service);
+int service_set_connecting(struct btd_service *service);
 
 /* Connection control API */
 int btd_service_connect(struct btd_service *service);
diff --git a/src/shared/att.c b/src/shared/att.c
index f1e0f59..3071b51 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -603,13 +603,20 @@
 	security = bt_att_get_security(att);
 
 	if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION &&
-					security < BT_ATT_SECURITY_MEDIUM)
+					security < BT_ATT_SECURITY_MEDIUM) {
 		security = BT_ATT_SECURITY_MEDIUM;
-	else if (ecode == BT_ATT_ERROR_AUTHENTICATION &&
-					security < BT_ATT_SECURITY_HIGH)
-		security = BT_ATT_SECURITY_HIGH;
-	else
+	} else if (ecode == BT_ATT_ERROR_AUTHENTICATION) {
+		if (security < BT_ATT_SECURITY_MEDIUM)
+			security = BT_ATT_SECURITY_MEDIUM;
+		else if (security < BT_ATT_SECURITY_HIGH)
+			security = BT_ATT_SECURITY_HIGH;
+		else if (security < BT_ATT_SECURITY_FIPS)
+			security = BT_ATT_SECURITY_FIPS;
+		else
+			return false;
+	} else {
 		return false;
+	}
 
 	return bt_att_set_security(att, security);
 }
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index dab2b2f..3df8998 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -49,6 +49,10 @@
 #define BTSNOOP_OPCODE_VENDOR_DIAG	11
 #define BTSNOOP_OPCODE_SYSTEM_NOTE	12
 #define BTSNOOP_OPCODE_USER_LOGGING	13
+#define BTSNOOP_OPCODE_CTRL_OPEN	14
+#define BTSNOOP_OPCODE_CTRL_CLOSE	15
+#define BTSNOOP_OPCODE_CTRL_COMMAND	16
+#define BTSNOOP_OPCODE_CTRL_EVENT	17
 
 #define BTSNOOP_MAX_PACKET_SIZE		(1486 + 4)
 
@@ -64,6 +68,7 @@
 #define BTSNOOP_BUS_SDIO	6
 #define BTSNOOP_BUS_SPI		7
 #define BTSNOOP_BUS_I2C		8
+#define BTSNOOP_BUS_SMD		9
 
 struct btsnoop_opcode_new_index {
 	uint8_t  type;
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 8fd8a45..4386692 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -631,9 +631,11 @@
 		return false;
 
 	handle = gatt_db_attribute_get_handle(attr);
-	bt_gatt_client_read_value(client, handle, ext_prop_read_cb,
+
+	if (!bt_gatt_client_read_value(client, handle, ext_prop_read_cb,
 							discovery_op_ref(op),
-							discovery_op_unref);
+							discovery_op_unref))
+		return false;
 
 	return true;
 }
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index a0a5b26..6b39bb1 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -797,7 +797,7 @@
 		goto done;
 	}
 
-	success = false;
+	success = true;
 
 done:
 	discovery_op_complete(op, success, att_ecode);
diff --git a/src/shared/util.h b/src/shared/util.h
index ff705d0..7d28ae9 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -108,6 +108,11 @@
 uint8_t util_get_uid(unsigned int *bitmap, uint8_t max);
 void util_clear_uid(unsigned int *bitmap, uint8_t id);
 
+static inline int8_t get_s8(const void *ptr)
+{
+	return *((int8_t *) ptr);
+}
+
 static inline uint8_t get_u8(const void *ptr)
 {
 	return *((uint8_t *) ptr);
diff --git a/test/example-advertisement b/test/example-advertisement
index ce6e40f..7da0c7b 100755
--- a/test/example-advertisement
+++ b/test/example-advertisement
@@ -65,10 +65,10 @@
                                                     signature='s')
         if self.manufacturer_data is not None:
             properties['ManufacturerData'] = dbus.Dictionary(
-                self.manufacturer_data, signature='qay')
+                self.manufacturer_data, signature='qv')
         if self.service_data is not None:
             properties['ServiceData'] = dbus.Dictionary(self.service_data,
-                                                        signature='say')
+                                                        signature='sv')
         if self.include_tx_power is not None:
             properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
         return {LE_ADVERTISEMENT_IFACE: properties}
@@ -88,13 +88,13 @@
 
     def add_manufacturer_data(self, manuf_code, data):
         if not self.manufacturer_data:
-            self.manufacturer_data = dict()
-        self.manufacturer_data[manuf_code] = data
+            self.manufacturer_data = dbus.Dictionary({}, signature='qv')
+        self.manufacturer_data[manuf_code] = dbus.Array(data, signature='y')
 
     def add_service_data(self, uuid, data):
         if not self.service_data:
-            self.service_data = dict()
-        self.service_data[uuid] = data
+            self.service_data = dbus.Dictionary({}, signature='sv')
+        self.service_data[uuid] = dbus.Array(data, signature='y')
 
     @dbus.service.method(DBUS_PROP_IFACE,
                          in_signature='s',
diff --git a/test/example-gatt-client b/test/example-gatt-client
index 4d1df2f..b4bbaa9 100755
--- a/test/example-gatt-client
+++ b/test/example-gatt-client
@@ -193,14 +193,17 @@
     om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), DBUS_OM_IFACE)
     om.connect_to_signal('InterfacesRemoved', interfaces_removed_cb)
 
+    print('Getting objects...')
     objects = om.GetManagedObjects()
     chrcs = []
 
+    # List characteristics found
     for path, interfaces in objects.items():
         if GATT_CHRC_IFACE not in interfaces.keys():
             continue
         chrcs.append(path)
 
+    # List sevices found
     for path, interfaces in objects.items():
         if GATT_SERVICE_IFACE not in interfaces.keys():
             continue
diff --git a/test/example-gatt-server b/test/example-gatt-server
index 84905f3..24aaff9 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -42,6 +42,9 @@
 
 
 class Application(dbus.service.Object):
+    """
+    org.bluez.GattApplication1 interface implementation
+    """
     def __init__(self, bus):
         self.path = '/'
         self.services = []
@@ -74,6 +77,9 @@
 
 
 class Service(dbus.service.Object):
+    """
+    org.bluez.GattService1 interface implementation
+    """
     PATH_BASE = '/org/bluez/example/service'
 
     def __init__(self, bus, index, uuid, primary):
@@ -121,6 +127,9 @@
 
 
 class Characteristic(dbus.service.Object):
+    """
+    org.bluez.GattCharacteristic1 interface implementation
+    """
     def __init__(self, bus, index, uuid, flags, service):
         self.path = service.path + '/char' + str(index)
         self.bus = bus
@@ -195,6 +204,9 @@
 
 
 class Descriptor(dbus.service.Object):
+    """
+    org.bluez.GattDescriptor1 interface implementation
+    """
     def __init__(self, bus, index, uuid, flags, characteristic):
         self.path = characteristic.path + '/desc' + str(index)
         self.bus = bus
@@ -222,7 +234,7 @@
         if interface != GATT_DESC_IFACE:
             raise InvalidArgsException()
 
-        return self.get_properties()[GATT_CHRC_IFACE]
+        return self.get_properties()[GATT_DESC_IFACE]
 
     @dbus.service.method(GATT_DESC_IFACE,
                         in_signature='a{sv}',
@@ -426,7 +438,7 @@
     TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0'
 
     def __init__(self, bus, index):
-        Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
+        Service.__init__(self, bus, index, self.TEST_SVC_UUID, True)
         self.add_characteristic(TestCharacteristic(bus, 0, self))
         self.add_characteristic(TestEncryptCharacteristic(bus, 1, self))
         self.add_characteristic(TestSecureCharacteristic(bus, 2, self))
@@ -523,11 +535,11 @@
                 CharacteristicUserDescriptionDescriptor(bus, 3, self))
 
     def ReadValue(self, options):
-        print('TestCharacteristic Read: ' + repr(self.value))
+        print('TestEncryptCharacteristic Read: ' + repr(self.value))
         return self.value
 
     def WriteValue(self, value, options):
-        print('TestCharacteristic Write: ' + repr(value))
+        print('TestEncryptCharacteristic Write: ' + repr(value))
         self.value = value
 
 class TestEncryptDescriptor(Descriptor):
@@ -564,16 +576,16 @@
                 ['secure-read', 'secure-write'],
                 service)
         self.value = []
-        self.add_descriptor(TestEncryptDescriptor(bus, 2, self))
+        self.add_descriptor(TestSecureDescriptor(bus, 2, self))
         self.add_descriptor(
                 CharacteristicUserDescriptionDescriptor(bus, 3, self))
 
     def ReadValue(self, options):
-        print('TestCharacteristic Read: ' + repr(self.value))
+        print('TestSecureCharacteristic Read: ' + repr(self.value))
         return self.value
 
     def WriteValue(self, value, options):
-        print('TestCharacteristic Write: ' + repr(value))
+        print('TestSecureCharacteristic Write: ' + repr(value))
         self.value = value
 
 
@@ -636,6 +648,8 @@
 
     mainloop = GObject.MainLoop()
 
+    print('Registering GATT application...')
+
     service_manager.RegisterApplication(app.get_path(), {},
                                     reply_handler=register_app_cb,
                                     error_handler=register_app_error_cb)
diff --git a/tools/btattach.c b/tools/btattach.c
index ec63ec9..9d1868a 100644
--- a/tools/btattach.c
+++ b/tools/btattach.c
@@ -187,7 +187,7 @@
 		"Usage:\n");
 	printf("\tbtattach [options]\n");
 	printf("options:\n"
-		"\t-B, --bredr <device>   Attach BR/EDR controller\n"
+		"\t-B, --bredr <device>   Attach Primary controller\n"
 		"\t-A, --amp <device>     Attach AMP controller\n"
 		"\t-P, --protocol <proto> Specify protocol type\n"
 		"\t-S, --speed <baudrate> Specify which baudrate to use\n"
@@ -217,6 +217,9 @@
 	{ "intel", HCI_UART_INTEL },
 	{ "bcm",   HCI_UART_BCM   },
 	{ "qca",   HCI_UART_QCA   },
+	{ "ag6xx", HCI_UART_AG6XX },
+	{ "nokia", HCI_UART_NOKIA },
+	{ "mrvl",  HCI_UART_MRVL  },
 	{ }
 };
 
@@ -300,7 +303,7 @@
 		unsigned long flags;
 		int fd;
 
-		printf("Attaching BR/EDR controller to %s\n", bredr_path);
+		printf("Attaching Primary controller to %s\n", bredr_path);
 
 		flags = (1 << HCI_UART_RESET_ON_INIT);
 
diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 4c86dcb..d5facc5 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -223,18 +223,14 @@
 	}
 }
 
-static bool load_identity(uint16_t index, struct mgmt_irk_info *irk)
+static bool load_identity(const char *path, struct mgmt_irk_info *irk)
 {
-	char identity_path[PATH_MAX];
 	char *addr, *key;
 	unsigned int type;
 	int n;
 	FILE *fp;
 
-	snprintf(identity_path, sizeof(identity_path),
-			"/sys/kernel/debug/bluetooth/hci%u/identity", index);
-
-	fp = fopen(identity_path, "r");
+	fp = fopen(path, "r");
 	if (!fp) {
 		error("Failed to open identity file: %s", strerror(errno));
 		return false;
@@ -1394,6 +1390,56 @@
 	noninteractive_quit(EXIT_SUCCESS);
 }
 
+static void ext_info_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_ext_info *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t supported_settings, current_settings;
+	char addr[18];
+
+	if (status != 0) {
+		error("Reading hci%u info failed with status 0x%02x (%s)",
+					index, status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small info reply (%u bytes)", len);
+		goto done;
+	}
+
+	print("hci%u:\tPrimary controller", index);
+
+	ba2str(&rp->bdaddr, addr);
+	print("\taddr %s version %u manufacturer %u",
+			addr, rp->version, le16_to_cpu(rp->manufacturer));
+
+	supported_settings = le32_to_cpu(rp->supported_settings);
+	print("\tsupported settings: %s", settings2str(supported_settings));
+
+	current_settings = le32_to_cpu(rp->current_settings);
+	print("\tcurrent settings: %s", settings2str(current_settings));
+
+	if (supported_settings & MGMT_SETTING_CONFIGURATION) {
+		if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+					index, 0, NULL, config_options_rsp,
+					UINT_TO_PTR(index), NULL)) {
+			error("Unable to send read_config cmd");
+			goto done;
+		}
+		return;
+	}
+
+done:
+	pending_index--;
+
+	if (pending_index > 0)
+		return;
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
 static void index_rsp(uint8_t status, uint16_t len, const void *param,
 							void *user_data)
 {
@@ -1498,10 +1544,10 @@
 		switch (rp->entry[i].type) {
 		case 0x00:
 			print("Primary controller (hci%u,%s)", index, busstr);
-			if (!mgmt_send(mgmt, MGMT_OP_READ_INFO,
-						index, 0, NULL, info_rsp,
+			if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INFO,
+						index, 0, NULL, ext_info_rsp,
 						UINT_TO_PTR(index), NULL)) {
-				error("Unable to send read_info cmd");
+				error("Unable to send read_ext_info cmd");
 				return noninteractive_quit(EXIT_FAILURE);
 			}
 			pending_index++;
@@ -2762,6 +2808,7 @@
 static struct option irks_options[] = {
 	{ "help",	0, 0, 'h' },
 	{ "local",	1, 0, 'l' },
+	{ "file",	1, 0, 'f' },
 	{ 0, 0, 0, 0 }
 };
 
@@ -2772,6 +2819,7 @@
 	struct mgmt_cp_load_irks *cp;
 	uint8_t buf[sizeof(*cp) + 23 * MAX_IRKS];
 	uint16_t count, local_index;
+	char path[PATH_MAX];
 	int opt;
 
 	if (index == MGMT_INDEX_NONE)
@@ -2780,7 +2828,7 @@
 	cp = (void *) buf;
 	count = 0;
 
-	while ((opt = getopt_long(argc, argv, "+l:h",
+	while ((opt = getopt_long(argc, argv, "+l:f:h",
 					irks_options, NULL)) != -1) {
 		switch (opt) {
 		case 'l':
@@ -2794,13 +2842,29 @@
 				local_index = atoi(optarg + 3);
 			else
 				local_index = atoi(optarg);
-			if (!load_identity(local_index, &cp->irks[count])) {
+			snprintf(path, sizeof(path),
+				"/sys/kernel/debug/bluetooth/hci%u/identity",
+				local_index);
+			if (!load_identity(path, &cp->irks[count])) {
 				error("Unable to load identity");
 				optind = 0;
 				return noninteractive_quit(EXIT_FAILURE);
 			}
 			count++;
 			break;
+		case 'f':
+			if (count >= MAX_IRKS) {
+				error("Number of IRKs exceeded");
+				optind = 0;
+				return noninteractive_quit(EXIT_FAILURE);
+			}
+			if (!load_identity(optarg, &cp->irks[count])) {
+				error("Unable to load identities");
+				optind = 0;
+				return noninteractive_quit(EXIT_FAILURE);
+			}
+			count++;
+			break;
 		case 'h':
 			irks_usage();
 			optind = 0;
@@ -3904,7 +3968,9 @@
 		"\t -g, --general-discov      \"general-discoverable\" flag\n"
 		"\t -l, --limited-discov      \"limited-discoverable\" flag\n"
 		"\t -m, --managed-flags       \"managed-flags\" flag\n"
-		"\t -p, --tx-power            \"tx-power\" flag");
+		"\t -p, --tx-power            \"tx-power\" flag\n"\
+		"\t -a, --appearance          \"appearance\" flag\n"\
+		"\t -n, --local-name          \"local-name\" flag");
 }
 
 static struct option advsize_options[] = {
@@ -3914,6 +3980,8 @@
 	{ "limited-discov",	0, 0, 'l' },
 	{ "managed-flags",	0, 0, 'm' },
 	{ "tx-power",		0, 0, 'p' },
+	{ "appearance",		0, 0, 'a' },
+	{ "local-name",		0, 0, 'n' },
 	{ 0, 0, 0, 0}
 };
 
@@ -3925,7 +3993,7 @@
 	uint32_t flags = 0;
 	int opt;
 
-	while ((opt = getopt_long(argc, argv, "+cglmph",
+	while ((opt = getopt_long(argc, argv, "+cglmphna",
 						advsize_options, NULL)) != -1) {
 		switch (opt) {
 		case 'c':
@@ -3943,6 +4011,12 @@
 		case 'p':
 			flags |= MGMT_ADV_FLAG_TX_POWER;
 			break;
+		case 'a':
+			flags |= MGMT_ADV_FLAG_APPEARANCE;
+			break;
+		case 'n':
+			flags |= MGMT_ADV_FLAG_LOCAL_NAME;
+			break;
 		default:
 			advsize_usage();
 			return noninteractive_quit(EXIT_FAILURE);
@@ -4007,6 +4081,8 @@
 		"\t -c, --connectable         \"connectable\" flag\n"
 		"\t -g, --general-discov      \"general-discoverable\" flag\n"
 		"\t -l, --limited-discov      \"limited-discoverable\" flag\n"
+		"\t -n, --scan-rsp-local-name \"local-name\" flag\n"
+		"\t -a, --scan-rsp-appearance \"appearance\" flag\n"
 		"\t -m, --managed-flags       \"managed-flags\" flag\n"
 		"\t -p, --tx-power            \"tx-power\" flag\n"
 		"e.g.:\n"
@@ -4088,7 +4164,7 @@
 	bool quit = true;
 	uint32_t flags = 0;
 
-	while ((opt = getopt_long(argc, argv, "+u:d:s:t:D:cglmph",
+	while ((opt = getopt_long(argc, argv, "+u:d:s:t:D:cglmphna",
 						add_adv_options, NULL)) != -1) {
 		switch (opt) {
 		case 'u':
@@ -4167,6 +4243,12 @@
 		case 'p':
 			flags |= MGMT_ADV_FLAG_TX_POWER;
 			break;
+		case 'n':
+			flags |= MGMT_ADV_FLAG_LOCAL_NAME;
+			break;
+		case 'a':
+			flags |= MGMT_ADV_FLAG_APPEARANCE;
+			break;
 		case 'h':
 			success = true;
 		default:
@@ -4290,6 +4372,40 @@
 	cmd_rm_adv(mgmt, index, 2, rm_argv);
 }
 
+static void appearance_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Could not set Appearance with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("Appearance successfully set");
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_appearance(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	struct mgmt_cp_set_appearance cp;
+
+	if (argc < 2) {
+		print("Usage: appearance <appearance>");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cp.appearance = cpu_to_le16(strtol(argv[1], NULL, 0));
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_APPEARANCE, index, sizeof(cp), &cp,
+					appearance_rsp, NULL, NULL) == 0) {
+		error("Unable to send appearance cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
 struct cmd_info {
 	char *cmd;
 	void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
@@ -4358,6 +4474,7 @@
 	{ "add-adv",	cmd_add_adv,	"Add advertising instance"	},
 	{ "rm-adv",	cmd_rm_adv,	"Remove advertising instance"	},
 	{ "clr-adv",	cmd_clr_adv,	"Clear advertising instances"	},
+	{ "appearance",	cmd_appearance,	"Set appearance"		},
 };
 
 static void cmd_quit(struct mgmt *mgmt, uint16_t index,
diff --git a/tools/csr.c b/tools/csr.c
index 2c09189..15ae7c4 100644
--- a/tools/csr.c
+++ b/tools/csr.c
@@ -2756,7 +2756,7 @@
 
 	off++;
 
-	while (1) {
+	while (length <= sizeof(array) - 2) {
 		value = strtol(off, &end, 16);
 		if (value == 0 && off == end)
 			break;
diff --git a/tools/hciattach.h b/tools/hciattach.h
index 4279a33..249aab4 100644
--- a/tools/hciattach.h
+++ b/tools/hciattach.h
@@ -42,6 +42,9 @@
 #define HCI_UART_INTEL	6
 #define HCI_UART_BCM	7
 #define HCI_UART_QCA	8
+#define HCI_UART_AG6XX	9
+#define HCI_UART_NOKIA	10
+#define HCI_UART_MRVL	11
 
 #define HCI_UART_RAW_DEVICE	0
 #define HCI_UART_RESET_ON_INIT	1
diff --git a/tools/l2test.c b/tools/l2test.c
index d4e3ae6..1819423 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -830,7 +830,7 @@
 			return;
 		}
 
-		syslog(LOG_INFO, "Recevied %d bytes", len);
+		syslog(LOG_INFO, "Received %d bytes", len);
 		hexdump(buf, len);
 	}
 }
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index b6f4873..d87d800 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -63,6 +63,7 @@
 	struct hciemu *hciemu;
 	enum hciemu_type hciemu_type;
 	int unmet_conditions;
+	int unmet_setup_conditions;
 };
 
 static void mgmt_debug(const char *str, void *user_data)
@@ -287,6 +288,27 @@
 	tester_print("Test condition added, total %d", data->unmet_conditions);
 }
 
+static void test_add_setup_condition(struct test_data *data)
+{
+	data->unmet_setup_conditions++;
+
+	tester_print("Test setup condition added, total %d",
+		     data->unmet_setup_conditions);
+}
+
+static void test_setup_condition_complete(struct test_data *data)
+{
+	data->unmet_setup_conditions--;
+
+	tester_print("Test setup condition complete, %d left",
+		     data->unmet_setup_conditions);
+
+	if (data->unmet_setup_conditions > 0)
+		return;
+
+	tester_setup_complete();
+}
+
 static void test_condition_complete(struct test_data *data)
 {
 	data->unmet_conditions--;
@@ -384,6 +406,12 @@
 	tester_test_passed();
 }
 
+struct setup_mgmt_cmd {
+	uint8_t send_opcode;
+	const void *send_param;
+	uint16_t send_len;
+};
+
 struct generic_data {
 	const uint16_t *setup_settings;
 	bool setup_nobredr;
@@ -394,6 +422,7 @@
 	uint16_t setup_send_opcode;
 	const void *setup_send_param;
 	uint16_t setup_send_len;
+	const struct setup_mgmt_cmd *setup_mgmt_cmd_arr;
 	bool send_index_none;
 	uint16_t send_opcode;
 	const void *send_param;
@@ -1679,6 +1708,112 @@
 	.expect_status = MGMT_STATUS_REJECTED,
 };
 
+static const uint8_t set_adv_set_appearance_param[2] = { 0x54, 0x65 };
+
+static const uint8_t set_adv_scan_rsp_data_appear_1[] = {
+	0x04, /* Scan rsp data len */
+	0x03, /* Local name data len */
+	0x19, /* Complete name */
+	0x54, 0x65,
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data set_adv_on_appearance_test_1 = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_APPEARANCE,
+	.setup_send_param = set_adv_set_appearance_param,
+	.setup_send_len = sizeof(set_adv_set_appearance_param),
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_on_param,
+	.expect_param = set_adv_settings_param_2,
+	.expect_len = sizeof(set_adv_settings_param_2),
+	.send_len = sizeof(set_adv_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_adv_scan_rsp_data_appear_1,
+	.expect_hci_len = sizeof(set_adv_scan_rsp_data_appear_1),
+};
+
+static const char set_adv_set_local_name_param[260] = { 'T', 'e', 's', 't', ' ',
+							'n', 'a', 'm', 'e' };
+
+static const uint8_t set_adv_scan_rsp_data_name_1[] = {
+	0x0c, /* Scan rsp data len */
+	0x0b, /* Local name data len */
+	0x09, /* Complete name */
+	0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, /* "Test name" */
+	0x00, /* null */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data set_adv_on_local_name_test_1 = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = set_adv_set_local_name_param,
+	.setup_send_len = sizeof(set_adv_set_local_name_param),
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_on_param,
+	.expect_param = set_adv_settings_param_2,
+	.expect_len = sizeof(set_adv_settings_param_2),
+	.send_len = sizeof(set_adv_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_adv_scan_rsp_data_name_1,
+	.expect_hci_len = sizeof(set_adv_scan_rsp_data_name_1),
+};
+
+static const struct setup_mgmt_cmd set_advertising_mgmt_cmd_arr[] = {
+	{
+		.send_opcode = MGMT_OP_SET_APPEARANCE,
+		.send_param = set_adv_set_appearance_param,
+		.send_len = sizeof(set_adv_set_appearance_param),
+	},
+	{
+		.send_opcode = MGMT_OP_SET_LOCAL_NAME,
+		.send_param = set_adv_set_local_name_param,
+		.send_len = sizeof(set_adv_set_local_name_param),
+	},
+	{ /* last element should always have opcode 0x00 */
+		.send_opcode = 0x00,
+		.send_param = NULL,
+		.send_len = 0,
+	}
+};
+
+static const uint8_t set_adv_scan_rsp_data_name_and_appearance[] = {
+	0x10, /* scan rsp data len */
+	0x03, /* appearance data len */
+	0x19, /* eir_appearance */
+	0x54, 0x65, /* appearance value */
+	0x0b, /* local name data len */
+	0x09, /* complete name */
+	0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, /* "test name" */
+	0x00, /* null */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+
+static const struct generic_data set_adv_on_local_name_appear_test_1 = {
+	.setup_settings = settings_powered_le,
+	.setup_mgmt_cmd_arr = set_advertising_mgmt_cmd_arr,
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_on_param,
+	.expect_param = set_adv_settings_param_2,
+	.expect_len = sizeof(set_adv_settings_param_2),
+	.send_len = sizeof(set_adv_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_adv_scan_rsp_data_name_and_appearance,
+	.expect_hci_len = sizeof(set_adv_scan_rsp_data_name_and_appearance),
+};
+
 static const char set_bredr_off_param[] = { 0x00 };
 static const char set_bredr_on_param[] = { 0x01 };
 static const char set_bredr_invalid_param[] = { 0x02 };
@@ -1760,6 +1895,20 @@
 		0x0a, 0x09, 'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e',
 		0x02, 0x0a, 0x00, };
 
+static const struct mgmt_cp_set_local_name set_local_name_cp = {
+	.name = {'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e'},
+	.short_name = {'T', 'e', 's', 't'},
+};
+
+static const struct mgmt_cp_set_local_name set_local_name_longer_cp = {
+	.name = {'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e', '1', '2', '3'},
+};
+
+static const struct mgmt_cp_set_local_name set_local_name_long_short_cp = {
+	.name = {'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e', '1', '2', '3'},
+	.short_name = {'T', 'e', 's', 't'},
+};
+
 static const struct generic_data set_local_name_test_1 = {
 	.send_opcode = MGMT_OP_SET_LOCAL_NAME,
 	.send_param = set_local_name_param,
@@ -3998,7 +4147,7 @@
 };
 
 static const uint8_t read_adv_features_rsp_1[] =  {
-	0x1f, 0x00, 0x00, 0x00,	/* supported flags */
+	0x7f, 0x00, 0x00, 0x00,	/* supported flags */
 	0x1f,			/* max_adv_data_len */
 	0x1f,			/* max_scan_rsp_len */
 	0x05,			/* max_instances */
@@ -4013,7 +4162,7 @@
 };
 
 static const uint8_t read_adv_features_rsp_2[] =  {
-	0x1f, 0x00, 0x00, 0x00,	/* supported flags */
+	0x7f, 0x00, 0x00, 0x00,	/* supported flags */
 	0x1f,			/* max_adv_data_len */
 	0x1f,			/* max_scan_rsp_len */
 	0x05,			/* max_instances */
@@ -4862,6 +5011,210 @@
 	.expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA,
 };
 
+static const char ext_ctrl_info1[] = {
+	0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+	0x08, /* version */
+	0x3f, 0x00, /* manufacturer */
+	0xff, 0xbf, 0x00, 0x00, /* supported settings */
+	0x80, 0x00, 0x00, 0x00, /* current settings */
+	0x09, 0x00, /* eir length */
+	0x04, /* dev class length */
+	0x0d, /* dev class info */
+	0x00, /* minor */
+	0x00, /* major */
+	0x00, /* service classes */
+	0x01, /* complete name data length */
+	0x09, /* complete name flag */
+	0x01, /* short name data length */
+	0x08, /* short name flag */
+};
+
+static const struct generic_data read_ext_ctrl_info1 = {
+	.send_opcode = MGMT_OP_READ_EXT_INFO,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = ext_ctrl_info1,
+	.expect_len = sizeof(ext_ctrl_info1),
+};
+
+static const char set_dev_class1[] = { 0x03, 0xe0 };
+
+static const struct setup_mgmt_cmd set_dev_class_cmd_arr1[] = {
+	{
+		.send_opcode = MGMT_OP_SET_DEV_CLASS,
+		.send_param = set_dev_class1,
+		.send_len = sizeof(set_dev_class1),
+	},
+	{
+		.send_opcode = MGMT_OP_ADD_UUID,
+		.send_param = add_spp_uuid_param,
+		.send_len = sizeof(add_spp_uuid_param),
+	},
+	{ /* last element should always have opcode 0x00 */
+		.send_opcode = 0x00,
+		.send_param = NULL,
+		.send_len = 0,
+	}
+};
+
+static const char ext_ctrl_info2[] = {
+	0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+	0x08, /* version */
+	0x3f, 0x00, /* manufacturer */
+	0xff, 0xbf, 0x00, 0x00, /* supported settings */
+	0x81, 0x02, 0x00, 0x00, /* current settings */
+	0x0D, 0x00, /* eir length */
+	0x04, /* dev class length */
+	0x0d, /* dev class info */
+	0xe0, /* minor */
+	0x03, /* major */
+	0x00, /* service classes */
+	0x03, /* appearance length */
+	0x19, /* EIR_APPEARANCE */
+	0x00, /* Appearance value */
+	0x00,
+	0x01, /* complete name data length */
+	0x09, /* complete name flag */
+	0x01, /* short name data length */
+	0x08, /* short name flag */
+};
+
+static const struct generic_data read_ext_ctrl_info2 = {
+	.setup_settings = settings_powered_le,
+	.setup_mgmt_cmd_arr = set_dev_class_cmd_arr1,
+	.send_opcode = MGMT_OP_READ_EXT_INFO,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = ext_ctrl_info2,
+	.expect_len = sizeof(ext_ctrl_info2),
+};
+
+static const char ext_ctrl_info3[] = {
+	0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+	0x08, /* version */
+	0x3f, 0x00, /* manufacturer */
+	0xff, 0xbf, 0x00, 0x00, /* supported settings */
+	0x80, 0x02, 0x00, 0x00, /* current settings */
+	0x16, 0x00, /* eir length */
+	0x04, /* dev class length */
+	0x0d, /* dev class info */
+	0x00, /* minor */
+	0x00, /* major */
+	0x00, /* service classes */
+	0x03, /* appearance length */
+	0x19, /* EIR_APPEARANCE */
+	0x00, /* Appearance value */
+	0x00,
+	0x0A, /* Local name length */
+	0x09, /* Complete name */
+	0x54, 0x65, 0x73, 0x74,
+	0x20, 0x6E, 0x61, 0x6D, 0x65, /* "Test name" */
+	0x01, /* short name data length */
+	0x08, /* short name flag */
+};
+
+static const struct generic_data read_ext_ctrl_info3 = {
+	.setup_settings = settings_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = set_local_name_param,
+	.setup_send_len = sizeof(set_local_name_param),
+	.send_opcode = MGMT_OP_READ_EXT_INFO,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = ext_ctrl_info3,
+	.expect_len = sizeof(ext_ctrl_info3),
+};
+
+static const char ext_ctrl_info4[] = {
+	0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+	0x08, /* version */
+	0x3f, 0x00, /* manufacturer */
+	0xff, 0xbf, 0x00, 0x00, /* supported settings */
+	0x80, 0x02, 0x00, 0x00, /* current settings */
+	0x1a, 0x00, /* eir length */
+	0x04, /* dev class length */
+	0x0d, /* dev class info */
+	0x00, /* minor */
+	0x00, /* major */
+	0x00, /* service classes */
+	0x03, /* appearance length */
+	0x19, /* EIR_APPEARANCE */
+	0x00, /* Appearance value */
+	0x00,
+	0x0A, /* Complete Local name len */
+	0x09, /* Complete name */
+	0x54, 0x65, 0x73, 0x74,
+	0x20, 0x6E, 0x61, 0x6D, 0x65, /* "Test name" */
+	0x05, /* Short Local name len */
+	0x08, /* Short name */
+	0x54, 0x65, 0x73, 0x74, /* "Test" */
+};
+
+static const struct generic_data read_ext_ctrl_info4 = {
+	.setup_settings = settings_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = &set_local_name_cp,
+	.setup_send_len = sizeof(set_local_name_cp),
+	.send_opcode = MGMT_OP_READ_EXT_INFO,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = ext_ctrl_info4,
+	.expect_len = sizeof(ext_ctrl_info4),
+};
+
+static const struct setup_mgmt_cmd set_dev_class_cmd_arr2[] = {
+	{
+		.send_opcode = MGMT_OP_SET_DEV_CLASS,
+		.send_param = set_dev_class1,
+		.send_len = sizeof(set_dev_class1),
+	},
+	{
+		.send_opcode = MGMT_OP_ADD_UUID,
+		.send_param = add_spp_uuid_param,
+		.send_len = sizeof(add_spp_uuid_param),
+	},
+	{
+		.send_opcode = MGMT_OP_SET_LOCAL_NAME,
+		.send_param = &set_local_name_cp,
+		.send_len = sizeof(set_local_name_cp),
+	},
+	{ /* last element should always have opcode 0x00 */
+		.send_opcode = 0x00,
+		.send_param = NULL,
+		.send_len = 0,
+	}
+};
+
+static const char ext_ctrl_info5[] = {
+	0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+	0x08, /* version */
+	0x3f, 0x00, /* manufacturer */
+	0xff, 0xbf, 0x00, 0x00, /* supported settings */
+	0x81, 0x02, 0x00, 0x00, /* current settings */
+	0x1a, 0x00, /* eir len */
+	0x04, /* dev class len */
+	0x0d, /* dev class info */
+	0xe0, /* minor */
+	0x03, /* major */
+	0x00, /* service classes */
+	0x03, /* appearance length */
+	0x19, /* EIR_APPEARANCE */
+	0x00, /* Appearance value */
+	0x00,
+	0x0A, /* Complete Local name len */
+	0x09, /* Complete name */
+	0x54, 0x65, 0x73, 0x74,
+	0x20, 0x6E, 0x61, 0x6D, 0x65, /* "Test name" */
+	0x05, /* Short Local name len */
+	0x08, /* Short name */
+	0x54, 0x65, 0x73, 0x74, /* "Test" */
+};
+
+static const struct generic_data read_ext_ctrl_info5 = {
+	.setup_settings = settings_powered_le,
+	.setup_mgmt_cmd_arr = set_dev_class_cmd_arr2,
+	.send_opcode = MGMT_OP_READ_EXT_INFO,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = ext_ctrl_info5,
+	.expect_len = sizeof(ext_ctrl_info5),
+};
+
 static void client_cmd_complete(uint16_t opcode, uint8_t status,
 					const void *param, uint8_t len,
 					void *user_data)
@@ -5844,6 +6197,35 @@
 	test_condition_complete(data);
 }
 
+static void command_setup_hci_callback(uint16_t opcode, const void *param,
+					uint8_t length, void *user_data)
+{
+	struct test_data *data = user_data;
+	const struct generic_data *test = data->test_data;
+	const void *setup_expect_hci_param = test->setup_expect_hci_param;
+	uint8_t setup_expect_hci_len = test->setup_expect_hci_len;
+
+	tester_print("HCI Command 0x%04x length %u", opcode, length);
+
+	if (opcode != test->setup_expect_hci_command)
+		return;
+
+	if (length != setup_expect_hci_len) {
+		tester_warn("Invalid parameter size for HCI command");
+		tester_test_failed();
+		return;
+	}
+
+	if (memcmp(param, setup_expect_hci_param, length) != 0) {
+		tester_warn("Unexpected HCI command parameter value");
+		tester_test_failed();
+		return;
+	}
+
+	hciemu_clear_master_post_command_hooks(data->hciemu);
+	test_setup_condition_complete(data);
+}
+
 static void command_hci_callback(uint16_t opcode, const void *param,
 					uint8_t length, void *user_data)
 {
@@ -5875,6 +6257,484 @@
 	test_condition_complete(data);
 }
 
+static void setup_mgmt_cmd_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+	test_setup_condition_complete(user_data);
+}
+
+static void setup_command_generic(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const void *send_param = test->setup_send_param;
+	uint16_t send_len = test->setup_send_len;
+	size_t i = 0;
+
+	if (test->setup_expect_hci_command) {
+		tester_print("Registering setup expected HCI command callback");
+		tester_print("Setup expected HCI command 0x%04x",
+					 test->setup_expect_hci_command);
+		hciemu_add_master_post_command_hook(data->hciemu,
+					command_setup_hci_callback, data);
+		test_add_setup_condition(data);
+	}
+
+	if (test->setup_send_opcode) {
+		tester_print("Setup sending %s (0x%04x)",
+				mgmt_opstr(test->setup_send_opcode),
+				test->setup_send_opcode);
+		mgmt_send(data->mgmt, test->setup_send_opcode, data->mgmt_index,
+					send_len, send_param,
+					setup_mgmt_cmd_callback,
+					data, NULL);
+		test_add_setup_condition(data);
+		return;
+	}
+
+	tester_print("Sending setup opcode array");
+	for (; test->setup_mgmt_cmd_arr + i; ++i) {
+		const struct setup_mgmt_cmd *cmd = test->setup_mgmt_cmd_arr + i;
+
+		if (cmd->send_opcode == 0x00)
+			break;
+
+		tester_print("Setup sending %s (0x%04x)",
+				mgmt_opstr(cmd->send_opcode),
+				cmd->send_opcode);
+
+		mgmt_send(data->mgmt, cmd->send_opcode, data->mgmt_index,
+				cmd->send_len, cmd->send_param,
+				setup_mgmt_cmd_callback,
+				data, NULL);
+		test_add_setup_condition(data);
+	}
+}
+
+static const uint8_t add_advertising_param_empty[] = {
+	0x01,			/* adv instance */
+	0x00, 0x00, 0x00, 0x00,	/* flags: none */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x00,			/* scan rsp len */
+};
+
+static const struct generic_data add_advertising_empty_scrsp = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = set_local_name_param,
+	.setup_send_len = sizeof(set_local_name_param),
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_empty,
+	.send_len = sizeof(add_advertising_param_empty),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+};
+
+static const uint8_t add_advertising_param_scrsp_data_only_ok[] = {
+	0x01,			/* adv instance */
+	0x00, 0x00, 0x00, 0x00,	/* flags: none */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x1f,			/* scan rsp len */
+	/* adv data: */
+	/* scan rsp data: */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_data_only_ok = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_scrsp_data_only_ok,
+	.send_len = sizeof(add_advertising_param_scrsp_data_only_ok),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+};
+
+static const uint8_t add_advertising_param_scrsp_data_only_too_long[] = {
+	0x01,			/* adv instance */
+	0x00, 0x00, 0x00, 0x00,	/* flags: none */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x20,			/* scan rsp len */
+	/* adv data: */
+	/* scan rsp data: */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_data_only_too_long = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_scrsp_data_only_too_long,
+	.send_len = sizeof(add_advertising_param_scrsp_data_only_too_long),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = NULL,
+	.expect_len = 0,
+};
+
+static const uint8_t set_appearance_param[2] = { 0x54, 0x65 };
+
+static const uint8_t add_advertising_param_scrsp_appear_data_ok[] = {
+	0x01,			/* adv instance */
+	0x20, 0x00, 0x00, 0x00,	/* flags: appearance */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x1b,			/* scan rsp len */
+	/* adv data: */
+	/* scan rsp data: */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_appear_data_ok = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_APPEARANCE,
+	.setup_send_param = set_appearance_param,
+	.setup_send_len = sizeof(set_appearance_param),
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_scrsp_appear_data_ok,
+	.send_len = sizeof(add_advertising_param_scrsp_appear_data_ok),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+};
+
+static const uint8_t add_advertising_param_scrsp_appear_data_too_long[] = {
+	0x01,			/* adv instance */
+	0x20, 0x00, 0x00, 0x00,	/* flags: appearance */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x1c,			/* scan rsp len */
+	/* adv data: */
+	/* scan rsp data: */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_appear_data_too_long = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_APPEARANCE,
+	.setup_send_param = set_appearance_param,
+	.setup_send_len = sizeof(set_appearance_param),
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_scrsp_appear_data_too_long,
+	.send_len = sizeof(add_advertising_param_scrsp_appear_data_too_long),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = NULL,
+	.expect_len = 0,
+};
+
+static const uint8_t add_advertising_param_scrsp_appear_null[] = {
+	0x01,			/* adv instance */
+	0x20, 0x00, 0x00, 0x00,	/* flags: appearance */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x01,			/* scan rsp len */
+	/* adv data: */
+	/* scan rsp data: */
+	0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_appear_null = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_scrsp_appear_null,
+	.send_len = sizeof(add_advertising_param_scrsp_appear_null),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+};
+
+static const uint8_t add_advertising_empty_param[] = {
+	0x01,			/* adv instance */
+	0x40, 0x00, 0x00, 0x00,	/* flags: local name*/
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x01,			/* scan rsp len */
+	/* scan rsp data: */
+	0x00,
+};
+
+static const uint8_t scan_rsp_data_empty[] = {
+	0x01, /* scan rsp data len */
+	0x00, /* scan rsp data */
+	/* placeholder data */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_no_name_set = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_empty_param,
+	.send_len = sizeof(add_advertising_empty_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = scan_rsp_data_empty,
+	.expect_hci_len = sizeof(scan_rsp_data_empty),
+};
+
+static const uint8_t add_advertising_param_name[] = {
+	0x01,			/* adv instance */
+	0x40, 0x00, 0x00, 0x00,	/* flags: Add local name to scan_rsp */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x00,			/* scan rsp len */
+};
+
+static const uint8_t set_scan_rsp_data_name_fits_in_scrsp[] = {
+	0x0c, /* Scan rsp data len */
+	0x0b, /* Local name data len */
+	0x09, /* Complete name */
+	0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, /* "Test name" */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_name_fits_in_scrsp = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = &set_local_name_cp,
+	.setup_send_len = sizeof(set_local_name_cp),
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_name,
+	.send_len = sizeof(add_advertising_param_name),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_scan_rsp_data_name_fits_in_scrsp,
+	.expect_hci_len = sizeof(set_scan_rsp_data_name_fits_in_scrsp),
+};
+
+static const uint8_t set_scan_rsp_data_shortened_name_fits[] = {
+	0x0d, /* Scan rsp data len */
+	0x0c, /* Local name data len */
+	0x08, /* Short name */
+	0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x31,
+	/* "Test name1" */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_shortened_name_in_scrsp = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = &set_local_name_longer_cp,
+	.setup_send_len = sizeof(set_local_name_longer_cp),
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_name,
+	.send_len = sizeof(add_advertising_param_name),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_scan_rsp_data_shortened_name_fits,
+	.expect_hci_len = sizeof(set_scan_rsp_data_shortened_name_fits),
+};
+
+static const uint8_t set_scan_rsp_data_short_name_fits[] = {
+	0x07, /* Scan rsp data len */
+	0x06, /* Local name data len */
+	0x08, /* Short name */
+	0x54, 0x65, 0x73, 0x74,
+	/* "Test*/
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_short_name_in_scrsp = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = &set_local_name_long_short_cp,
+	.setup_send_len = sizeof(set_local_name_long_short_cp),
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_name,
+	.send_len = sizeof(add_advertising_param_name),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_scan_rsp_data_short_name_fits,
+	.expect_hci_len = sizeof(set_scan_rsp_data_short_name_fits),
+};
+
+static const uint8_t add_advertising_param_name_data_ok[] = {
+	0x01,			/* adv instance */
+	0x40, 0x00, 0x00, 0x00,	/* flags: local name */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x12,			/* scan rsp len */
+	/* adv data: */
+	/* scan rsp data: */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t set_scan_rsp_data_param_name_data_ok[] = {
+	0x1e, /* Scan rsp data len */
+	/* scan rsp data */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0b, /* Local name data len */
+	0x09, /* Complete name */
+	0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x00,
+	/* "Test name" */
+	/* padding */
+	0x00,
+};
+
+static const struct generic_data add_advertising_name_data_ok = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = &set_local_name_cp,
+	.setup_send_len = sizeof(set_local_name_cp),
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_name_data_ok,
+	.send_len = sizeof(add_advertising_param_name_data_ok),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_scan_rsp_data_param_name_data_ok,
+	.expect_hci_len = sizeof(set_scan_rsp_data_param_name_data_ok),
+};
+
+static const uint8_t add_advertising_param_name_data_inv[] = {
+	0x01,			/* adv instance */
+	0x40, 0x00, 0x00, 0x00,	/* flags: local name */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x14,			/* scan rsp len */
+	/* adv data: */
+	/* scan rsp data: */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const struct generic_data add_advertising_name_data_inv = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.setup_send_param = &set_local_name_cp,
+	.setup_send_len = sizeof(set_local_name_cp),
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_name_data_inv,
+	.send_len = sizeof(add_advertising_param_name_data_inv),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = NULL,
+	.expect_len = 0,
+};
+
+static const uint8_t add_advertising_param_name_data_appear[] = {
+	0x01,			/* adv instance */
+	0x60, 0x00, 0x00, 0x00,	/* flags: local name + appearance */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x00,			/* adv data len */
+	0x0e,			/* scan rsp len */
+	/* adv data: */
+	/* scan rsp data: */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct setup_mgmt_cmd add_advertising_mgmt_cmd_arr[] = {
+	{
+		.send_opcode = MGMT_OP_SET_APPEARANCE,
+		.send_param = set_appearance_param,
+		.send_len = sizeof(set_appearance_param),
+	},
+	{
+		.send_opcode = MGMT_OP_SET_LOCAL_NAME,
+		.send_param = &set_local_name_cp,
+		.send_len = sizeof(set_local_name_cp),
+	},
+	{ /* last element should always have opcode 0x00 */
+		.send_opcode = 0x00,
+		.send_param = NULL,
+		.send_len = 0,
+	}
+};
+
+static const uint8_t set_scan_rsp_data_name_data_appear[] = {
+	0x1e, /* Scan rsp data len */
+	0x03, /* appearance len */
+	0x19, /* EIR_APPEARANCE */
+	0x54, 0x65, /* appearance value */
+	/* scan rsp data */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x0b, /* Local name data len */
+	0x09, /* Complete name */
+	0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x00,
+	/* "Test name" */
+	/* padding */
+	0x00,
+};
+
+static const struct generic_data add_advertising_name_data_appear = {
+	.setup_settings = settings_powered_le,
+	.setup_mgmt_cmd_arr = add_advertising_mgmt_cmd_arr,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_name_data_appear,
+	.send_len = sizeof(add_advertising_param_name_data_appear),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_scan_rsp_data_name_data_appear,
+	.expect_hci_len = sizeof(set_scan_rsp_data_name_data_appear),
+};
+
+static const struct generic_data set_appearance_not_supported = {
+	.send_opcode = MGMT_OP_SET_APPEARANCE,
+	.send_param = set_appearance_param,
+	.send_len = sizeof(set_appearance_param),
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+	.expect_param = NULL,
+	.expect_len = 0,
+};
+
+static const struct generic_data set_appearance_success = {
+	.send_opcode = MGMT_OP_SET_APPEARANCE,
+	.send_param = set_appearance_param,
+	.send_len = sizeof(set_appearance_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = NULL,
+	.expect_len = 0,
+};
+
 static bool power_off(uint16_t index)
 {
 	int sk, err;
@@ -6489,6 +7349,18 @@
 				&set_adv_on_rejected_test_1,
 				NULL, test_command_generic);
 
+	test_bredrle("Set Advertising on - Appearance 1",
+				&set_adv_on_appearance_test_1,
+				setup_command_generic, test_command_generic);
+
+	test_bredrle("Set Advertising on - Local name 1",
+				&set_adv_on_local_name_test_1,
+				setup_command_generic, test_command_generic);
+
+	test_bredrle("Set Advertising on - Name + Appear 1",
+				&set_adv_on_local_name_appear_test_1,
+				setup_command_generic, test_command_generic);
+
 	test_bredrle("Set BR/EDR off - Success 1",
 				&set_bredr_off_success_test_1,
 				NULL, test_command_generic);
@@ -7071,10 +7943,69 @@
 					setup_add_advertising,
 					test_command_generic);
 
+	test_bredrle("Add Advertising - Success (Empty ScRsp)",
+					 &add_advertising_empty_scrsp,
+					 setup_command_generic,
+					 test_command_generic);
+
+	test_bredrle("Add Advertising - Success (ScRsp only)",
+					&add_advertising_scrsp_data_only_ok,
+						NULL, test_command_generic);
+
+	test_bredrle("Add Advertising - Invalid Params (ScRsp too long)",
+				&add_advertising_scrsp_data_only_too_long,
+						NULL, test_command_generic);
+
+	test_bredrle("Add Advertising - Success (ScRsp appear)",
+					&add_advertising_scrsp_appear_data_ok,
+				setup_command_generic, test_command_generic);
+
+	test_bredrle("Add Advertising - Invalid Params (ScRsp appear long)",
+				&add_advertising_scrsp_appear_data_too_long,
+				setup_command_generic, test_command_generic);
+
+	test_bredrle("Add Advertising - Success (Appear is null)",
+					&add_advertising_scrsp_appear_null,
+						NULL, test_command_generic);
+
+	test_bredrle("Add Advertising - Success (Name is null)",
+					 &add_advertising_no_name_set,
+					 NULL, test_command_generic);
+
+	test_bredrle("Add Advertising - Success (Complete name)",
+					&add_advertising_name_fits_in_scrsp,
+					setup_command_generic,
+					test_command_generic);
+
+	test_bredrle("Add Advertising - Success (Shortened name)",
+				&add_advertising_shortened_name_in_scrsp,
+					setup_command_generic,
+					test_command_generic);
+
+	test_bredrle("Add Advertising - Success (Short name)",
+					&add_advertising_short_name_in_scrsp,
+					setup_command_generic,
+					test_command_generic);
+
+	test_bredrle("Add Advertising - Success (Name + data)",
+					 &add_advertising_name_data_ok,
+					 setup_command_generic,
+					 test_command_generic);
+
+	test_bredrle("Add Advertising - Invalid Params (Name + data)",
+					 &add_advertising_name_data_inv,
+					 setup_command_generic,
+					 test_command_generic);
+
+	test_bredrle("Add Advertising - Success (Name+data+appear)",
+					 &add_advertising_name_data_appear,
+					 setup_command_generic,
+					 test_command_generic);
 
 	test_bredrle("Remove Advertising - Invalid Params 1",
 					&remove_advertising_fail_1,
 					NULL, test_command_generic);
+
 	test_bredrle("Remove Advertising - Success 1",
 						&remove_advertising_success_1,
 						setup_add_advertising,
@@ -7100,6 +8031,41 @@
 					setup_add_advertising_duration,
 					test_command_generic, 3);
 
+	test_bredr("Set appearance - BR/EDR only",
+					&set_appearance_not_supported,
+					NULL,
+					test_command_generic);
+
+	test_bredrle("Set appearance - BR/EDR LE",
+					&set_appearance_success,
+					NULL,
+					test_command_generic);
+
+	test_le("Set appearance - LE only",
+					&set_appearance_success,
+					NULL,
+					test_command_generic);
+
+	test_bredrle("Read Ext Controller Info 1",
+				&read_ext_ctrl_info1,
+				NULL, test_command_generic);
+
+	test_bredrle("Read Ext Controller Info 2",
+				&read_ext_ctrl_info2,
+				setup_command_generic, test_command_generic);
+
+	test_bredrle("Read Ext Controller Info 3",
+				&read_ext_ctrl_info3,
+				setup_command_generic, test_command_generic);
+
+	test_bredrle("Read Ext Controller Info 4",
+				&read_ext_ctrl_info4,
+				setup_command_generic, test_command_generic);
+
+	test_bredrle("Read Ext Controller Info 5",
+				&read_ext_ctrl_info5,
+				setup_command_generic, test_command_generic);
+
 	test_bredrle("Read Local OOB Data - Not powered",
 				&read_local_oob_not_powered_test,
 				NULL, test_command_generic);
diff --git a/tools/parse_companies.pl b/tools/parse_companies.pl
index f755390..d5b2815 100755
--- a/tools/parse_companies.pl
+++ b/tools/parse_companies.pl
@@ -8,7 +8,11 @@
 
 my %known_entities = (
     'nbsp' => ' ',
+    'aacute' => 'á',
     'eacute' => 'é',
+    'iacute' => 'í',
+    'oacute' => 'ó',
+    'uacute' => 'ú',
     'auml' => 'ä',
     'uuml' => 'ü',
     'Uuml' => 'Ü',
@@ -23,8 +27,7 @@
     }
     foreach my $entity (map { lc $_ } $name =~ /&([^;]+);/g) {
         if ($entity ne 'amp') {
-            print "Unable to convert &$entity;, giving up\n";
-            exit 1;
+            die "\nparse_companies.pl: Unable to convert &$entity; giving up\n";
         }
     }
     $name =~ s/&amp;/&/ig;
@@ -51,6 +54,7 @@
         my $name = uri_decode($1);
         $name =~ s/^\s+//g; # kill leading
         $name =~ s/\s+$//g; # and trailing space
+        $name =~ s/"/\\"/g; # escape double quotes
         my $id = hex($identifier);
         if ($id != 65535) {
             print "\tcase $id:\n";
diff --git a/tools/scotest.c b/tools/scotest.c
index 596e403..f894c24 100644
--- a/tools/scotest.c
+++ b/tools/scotest.c
@@ -265,7 +265,7 @@
 
 	syslog(LOG_INFO,"Receiving ...");
 	while ((len = read(sk, buf, data_size)) > 0)
-		syslog(LOG_INFO, "Recevied %d bytes", len);
+		syslog(LOG_INFO, "Received %d bytes", len);
 }
 
 static void recv_mode(int sk)
diff --git a/tools/smp-tester.c b/tools/smp-tester.c
index d24c9b2..90b091e 100644
--- a/tools/smp-tester.c
+++ b/tools/smp-tester.c
@@ -593,7 +593,7 @@
 					data->ra_type, data->ra, confirm))
 		return false;
 
-	if (memcmp(data->pcnf, confirm, sizeof(data->pcnf) != 0)) {
+	if (memcmp(data->pcnf, confirm, sizeof(data->pcnf)) != 0) {
 		tester_warn("Confirmation values don't match");
 		return false;
 	}
diff --git a/tools/update_compids.sh b/tools/update_compids.sh
index 7c4cc12..e82d3e4 100755
--- a/tools/update_compids.sh
+++ b/tools/update_compids.sh
@@ -25,7 +25,7 @@
 
 path=specifications/assigned-numbers/company-identifiers
 # Use "iconv -c" to strip unwanted unicode characters
-curl https://www.bluetooth.com/$path | \
+curl --insecure https://www.bluetooth.com/$path | \
     $scriptdir/tools/parse_companies.pl >> new.c
 
 if ! grep -q "return \"" new.c; then
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 8129719..c7a8fa5 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -2414,6 +2414,14 @@
 					0x18),
 			raw_pdu(0x01, 0x06, 0x08, 0x00, 0x0a));
 
+	define_test_att("/TP/GAD/CL/BV-02-C-1-alternative",
+			test_search_primary, &uuid_16,
+			NULL,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x00,
+					0x18),
+			raw_pdu(0x07, 0x01, 0x00, 0xFF, 0xFF));
+
 	define_test_att("/TP/GAD/CL/BV-02-C-2", test_search_primary, &uuid_128,
 			NULL,
 			MTU_EXCHANGE_CLIENT_PDUS,