diff mbox series

[BlueZ,2/2] advertising: Add scan response support in bluetoothctl

Message ID 20241124165539.2913146-3-yuxinwang9999@gmail.com (mailing list archive)
State New
Headers show
Series advertising: Add scan response data handling | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success

Commit Message

Yuxin Wang Nov. 24, 2024, 4:55 p.m. UTC
Add commands in the bluetoothctl advertising submenu to manage
scan response data.
---
 client/advertising.c              | 486 +++++++++++++++++++++++-------
 client/advertising.h              |  25 +-
 client/bluetoothctl-advertise.rst |  39 +++
 client/main.c                     |  95 +++++-
 4 files changed, 515 insertions(+), 130 deletions(-)
diff mbox series

Patch

diff --git a/client/advertising.c b/client/advertising.c
index 4a98121..46af2e0 100644
--- a/client/advertising.c
+++ b/client/advertising.c
@@ -59,11 +59,13 @@  static struct ad {
 	uint16_t duration;
 	uint16_t timeout;
 	uint16_t discoverable_to;
-	char **uuids;
-	size_t uuids_len;
-	struct service_data service;
-	struct manufacturer_data manufacturer;
-	struct data data;
+	char **uuids[AD_TYPE_COUNT];
+	size_t uuids_len[AD_TYPE_COUNT];
+	char **solicit[AD_TYPE_COUNT];
+	size_t solicit_len[AD_TYPE_COUNT];
+	struct service_data service[AD_TYPE_COUNT];
+	struct manufacturer_data manufacturer[AD_TYPE_COUNT];
+	struct data data[AD_TYPE_COUNT];
 	bool discoverable;
 	bool tx_power;
 	bool name;
@@ -111,7 +113,7 @@  static void register_setup(DBusMessageIter *iter, void *user_data)
 	dbus_message_iter_close_container(iter, &dict);
 }
 
-static void print_uuid(const char *uuid)
+static void print_uuid(const char *prefix, const char *uuid)
 {
 	const char *text;
 
@@ -130,37 +132,70 @@  static void print_uuid(const char *uuid)
 				str[sizeof(str) - 4] = '.';
 		}
 
-		bt_shell_printf("UUID: %s(%s)\n", str, uuid);
+		bt_shell_printf("%s: %s(%s)\n", prefix, str, uuid);
 	} else
-		bt_shell_printf("UUID: (%s)\n", uuid ? uuid : "");
+		bt_shell_printf("%s: (%s)\n", prefix, uuid ? uuid : "");
+}
+
+static const struct {
+    const char* uuid[AD_TYPE_COUNT];
+    const char* solicit[AD_TYPE_COUNT];
+    const char* service[AD_TYPE_COUNT];
+    const char* manufacturer[AD_TYPE_COUNT];
+    const char* data[AD_TYPE_COUNT];
+} ad_names = {
+    .uuid = { "UUID", "Scan Response UUID" },
+    .solicit = { "Solicit UUID", "Scan Response Solicit UUID" },
+    .service = { "UUID", "Scan Response UUID" },
+    .manufacturer = { "Manufacturer", "Scan Response Manufacturer" },
+    .data = { "Data", "Scan Response Data" }
+};
+
+static void print_ad_uuids(int type)
+{
+	char **uuid;
+
+	for (uuid = ad.uuids[type]; uuid && *uuid; uuid++)
+		print_uuid(ad_names.uuid[type], *uuid);
 }
 
-static void print_ad_uuids(void)
+static void print_ad_solicit(int type)
 {
 	char **uuid;
 
-	for (uuid = ad.uuids; uuid && *uuid; uuid++)
-		print_uuid(*uuid);
+	for (uuid = ad.solicit[type]; uuid && *uuid; uuid++)
+		print_uuid(ad_names.solicit[type], *uuid);
 }
 
 static void print_ad(void)
 {
-	print_ad_uuids();
+	int type;
 
-	if (ad.service.uuid) {
-		print_uuid(ad.service.uuid);
-		bt_shell_hexdump(ad.service.data.data, ad.service.data.len);
-	}
+	for (type = AD_TYPE_AD; type <= AD_TYPE_SRD; type++) {
+		print_ad_uuids(type);
+		print_ad_solicit(type);
 
-	if (ad.manufacturer.data.len) {
-		bt_shell_printf("Manufacturer: %u\n", ad.manufacturer.id);
-		bt_shell_hexdump(ad.manufacturer.data.data,
-						ad.manufacturer.data.len);
-	}
+		if (ad.service[type].uuid) {
+			print_uuid(ad_names.service[type], 
+						ad.service[type].uuid);
+			bt_shell_hexdump(ad.service[type].data.data, 
+						ad.service[type].data.len);
+		}
+
+		if (ad.manufacturer[type].data.len) {
+			bt_shell_printf("%s: %u\n", ad_names.manufacturer[type],
+						ad.manufacturer[type].id);
+			bt_shell_hexdump(ad.manufacturer[type].data.data,
+						ad.manufacturer[type].data.len);
+		}
 
-	if (ad.data.valid) {
-		bt_shell_printf("Data Type: 0x%02x\n", ad.data.type);
-		bt_shell_hexdump(ad.data.data.data, ad.data.data.len);
+		if (ad.data[type].valid) {
+			bt_shell_printf("%s Type: 0x%02x\n", 
+						ad_names.data[type], 
+						ad.data[type].type);
+			bt_shell_hexdump(ad.data[type].data.data, 
+						ad.data[type].data.len);
+		}
 	}
 
 	bt_shell_printf("Tx Power: %s\n", ad.tx_power ? "on" : "off");
@@ -228,12 +263,13 @@  static gboolean get_type(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
-static gboolean uuids_exists(const GDBusPropertyTable *property, void *data)
+static gboolean uuids_exists(int type, const GDBusPropertyTable *property,
+								void *data)
 {
-	return ad.uuids_len != 0;
+	return ad.uuids_len[type] != 0;
 }
 
-static gboolean get_uuids(const GDBusPropertyTable *property,
+static gboolean get_uuids(int type, const GDBusPropertyTable *property,
 				DBusMessageIter *iter, void *user_data)
 {
 	DBusMessageIter array;
@@ -241,55 +277,149 @@  static gboolean get_uuids(const GDBusPropertyTable *property,
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
 
-	for (i = 0; i < ad.uuids_len; i++)
+	for (i = 0; i < ad.uuids_len[type]; i++)
 		dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
-							&ad.uuids[i]);
+						&ad.uuids[type][i]);
 
 	dbus_message_iter_close_container(iter, &array);
 
 	return TRUE;
 }
 
-static gboolean service_data_exists(const GDBusPropertyTable *property,
+static gboolean ad_uuids_exists(const GDBusPropertyTable *property, void *data)
+{
+	return uuids_exists(AD_TYPE_AD, property, data);
+}
+
+static gboolean get_ad_uuids(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	return get_uuids(AD_TYPE_AD, property, iter, user_data);
+}
+
+static gboolean sr_uuids_exists(const GDBusPropertyTable *property, void *data)
+{
+	return uuids_exists(AD_TYPE_SRD, property, data);
+}
+
+static gboolean get_sr_uuids(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	return get_uuids(AD_TYPE_SRD, property, iter, user_data);
+}
+
+static gboolean solicit_uuids_exists(int type,
+				const GDBusPropertyTable *property, void *data)
+{
+	return ad.solicit_len[type] != 0;
+}
+
+static gboolean get_solicit_uuids(int type, 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.solicit_len[type]; i++)
+		dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+						&ad.solicit[type][i]);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static gboolean ad_solicit_uuids_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	return solicit_uuids_exists(AD_TYPE_AD, property, data);
+}
+
+static gboolean get_ad_solicit_uuids(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	return get_solicit_uuids(AD_TYPE_AD, property, iter, user_data);
+}
+
+static gboolean sr_solicit_uuids_exists(const GDBusPropertyTable *property,
 								void *data)
 {
-	return ad.service.uuid != NULL;
+	return solicit_uuids_exists(AD_TYPE_SRD, property, data);
 }
 
-static gboolean get_service_data(const GDBusPropertyTable *property,
+static gboolean get_sr_solicit_uuids(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	return get_solicit_uuids(AD_TYPE_SRD, property, iter, user_data);
+}
+
+static gboolean service_data_exists(int type,
+				const GDBusPropertyTable *property, void *data)
+{
+	return ad.service[type].uuid != NULL;
+}
+
+static gboolean get_service_data(int type, const GDBusPropertyTable *property,
 				DBusMessageIter *iter, void *user_data)
 {
 	DBusMessageIter dict;
-	struct ad_data *data = &ad.service.data;
+	struct ad_data *data = &ad.service[type].data;
 	uint8_t *val = data->data;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
 
-	g_dbus_dict_append_array(&dict, ad.service.uuid, DBUS_TYPE_BYTE, &val,
-								data->len);
+	g_dbus_dict_append_array(&dict, ad.service[type].uuid, DBUS_TYPE_BYTE,
+							&val, data->len);
 
 	dbus_message_iter_close_container(iter, &dict);
 
 	return TRUE;
 }
 
-static gboolean manufacturer_data_exists(const GDBusPropertyTable *property,
+static gboolean ad_service_data_exists(const GDBusPropertyTable *property,
 								void *data)
 {
-	return ad.manufacturer.id != 0;
+	return service_data_exists(AD_TYPE_AD, property, data);
 }
 
-static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *user_data)
+static gboolean get_ad_service_data(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	return get_service_data(AD_TYPE_AD, property, iter, user_data);
+}
+
+static gboolean sr_service_data_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	return service_data_exists(AD_TYPE_SRD, property, data);
+}
+
+static gboolean get_sr_service_data(const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
+{
+	return get_service_data(AD_TYPE_SRD, property, iter, user_data);
+}
+
+static gboolean manufacturer_data_exists(int type,
+				const GDBusPropertyTable *property, void *data)
+{
+	return ad.manufacturer[type].id != 0;
+}
+
+static gboolean get_manufacturer_data(int type,
+				const GDBusPropertyTable *property,
+				DBusMessageIter *iter, void *user_data)
 {
 	DBusMessageIter dict;
-	struct ad_data *data = &ad.manufacturer.data;
+	struct ad_data *data = &ad.manufacturer[type].data;
 	uint8_t *val = data->data;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{qv}", &dict);
 
 	g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_UINT16,
-					&ad.manufacturer.id,
+					&ad.manufacturer[type].id,
 					DBUS_TYPE_BYTE, &val, data->len);
 
 	dbus_message_iter_close_container(iter, &dict);
@@ -297,6 +427,30 @@  static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static gboolean ad_manufacturer_data_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	return manufacturer_data_exists(AD_TYPE_AD, property, data);
+}
+
+static gboolean get_ad_manufacturer_data(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	return get_manufacturer_data(AD_TYPE_AD, property, iter, user_data);
+}
+
+static gboolean sr_manufacturer_data_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	return manufacturer_data_exists(AD_TYPE_SRD, property, data);
+}
+
+static gboolean get_sr_manufacturer_data(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	return get_manufacturer_data(AD_TYPE_SRD, property, iter, user_data);
+}
+
 static gboolean includes_exists(const GDBusPropertyTable *property, void *data)
 {
 	return ad.tx_power || ad.name || ad.appearance || ad.rsi;
@@ -394,28 +548,51 @@  static gboolean get_timeout(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
-static gboolean data_exists(const GDBusPropertyTable *property, void *data)
+static gboolean data_exists(int type, const GDBusPropertyTable *property,
+								void *data)
 {
-	return ad.data.valid;
+	return ad.data[type].valid;
 }
 
-static gboolean get_data(const GDBusPropertyTable *property,
+static gboolean get_data(int type, const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *user_data)
 {
 	DBusMessageIter dict;
-	struct ad_data *data = &ad.data.data;
+	struct ad_data *data = &ad.data[type].data;
 	uint8_t *val = data->data;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{yv}", &dict);
 
-	g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_BYTE, &ad.data.type,
-					DBUS_TYPE_BYTE, &val, data->len);
+	g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_BYTE,
+			&ad.data[type].type, DBUS_TYPE_BYTE, &val, data->len);
 
 	dbus_message_iter_close_container(iter, &dict);
 
 	return TRUE;
 }
 
+static gboolean ad_data_exists(const GDBusPropertyTable *property, void *data)
+{
+	return data_exists(AD_TYPE_AD, property, data);
+}
+
+static gboolean get_ad_data(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	return get_data(AD_TYPE_AD, property, iter, user_data);
+}
+
+static gboolean sr_data_exists(const GDBusPropertyTable *property, void *data)
+{
+	return data_exists(AD_TYPE_SRD, property, data);
+}
+
+static gboolean get_sr_data(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	return get_data(AD_TYPE_SRD, property, iter, user_data);
+}
+
 static gboolean get_discoverable(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *user_data)
 {
@@ -487,11 +664,23 @@  static gboolean get_max_interval(const GDBusPropertyTable *property,
 
 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 },
-	{ "Data", "a{yv}", get_data, NULL, data_exists },
+	{ "ServiceUUIDs", "as", get_ad_uuids, NULL, ad_uuids_exists },
+	{ "SolicitUUIDs", "as", get_ad_solicit_uuids, NULL,
+						ad_solicit_uuids_exists },
+	{ "ServiceData", "a{sv}", get_ad_service_data, NULL,
+						ad_service_data_exists },
+	{ "ManufacturerData", "a{qv}", get_ad_manufacturer_data, NULL,
+						ad_manufacturer_data_exists },
+	{ "Data", "a{yv}", get_ad_data, NULL, ad_data_exists },
+	{ "ScanResponseServiceUUIDs", "as", get_sr_uuids, NULL,
+						sr_uuids_exists },
+	{ "ScanResponseSolicitUUIDs", "as", get_sr_solicit_uuids, NULL,
+						sr_solicit_uuids_exists },
+	{ "ScanResponseServiceData", "a{sv}", get_sr_service_data, NULL,
+						sr_service_data_exists },
+	{ "ScanResponseManufacturerData", "a{qv}", get_sr_manufacturer_data,
+					NULL, sr_manufacturer_data_exists },
+	{ "ScanResponseData", "a{yv}", get_sr_data, NULL, sr_data_exists },
 	{ "Discoverable", "b", get_discoverable, NULL, NULL },
 	{ "DiscoverableTimeout", "q", get_discoverable_timeout, NULL,
 						discoverable_timeout_exists },
@@ -582,50 +771,109 @@  void ad_unregister(DBusConnection *conn, GDBusProxy *manager)
 	}
 }
 
-static void ad_clear_uuids(void)
+static const struct {
+    const char* uuid[AD_TYPE_COUNT];
+    const char* solicit[AD_TYPE_COUNT];
+    const char* service[AD_TYPE_COUNT];
+    const char* manufacturer[AD_TYPE_COUNT];
+    const char* data[AD_TYPE_COUNT];
+} prop_names = {
+    .uuid = { "ServiceUUIDs", 	"ScanResponseServiceUUIDs" },
+    .solicit = { "SolicitUUIDs", "ScanResponseSolicitUUIDs" },
+    .service = { "ServiceData", "ScanResponseServiceData" },
+    .manufacturer = { "ManufacturerData", "ScanResponseManufacturerData" },
+    .data = { "Data", "ScanResponseData" }
+};
+
+static void ad_clear_uuids(int type)
 {
-	g_strfreev(ad.uuids);
-	ad.uuids = NULL;
-	ad.uuids_len = 0;
+	g_strfreev(ad.uuids[type]);
+	ad.uuids[type] = NULL;
+	ad.uuids_len[type] = 0;
 }
 
-void ad_advertise_uuids(DBusConnection *conn, int argc, char *argv[])
+void ad_advertise_uuids(DBusConnection *conn, int type, int argc, char *argv[])
 {
 	if (argc < 2 || !strlen(argv[1])) {
-		print_ad_uuids();
+		print_ad_uuids(type);
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 	}
 
-	ad_clear_uuids();
+	ad_clear_uuids(type);
 
-	ad.uuids = g_strdupv(&argv[1]);
-	if (!ad.uuids) {
+	ad.uuids[type] = g_strdupv(&argv[1]);
+	if (!ad.uuids[type]) {
 		bt_shell_printf("Failed to parse input\n");
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
-	ad.uuids_len = g_strv_length(ad.uuids);
+	ad.uuids_len[type] = g_strv_length(ad.uuids[type]);
 
-	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceUUIDs");
+	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+							prop_names.uuid[type]);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-void ad_disable_uuids(DBusConnection *conn)
+void ad_disable_uuids(DBusConnection *conn, int type)
 {
-	if (!ad.uuids)
+	if (!ad.uuids[type])
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 
-	ad_clear_uuids();
-	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceUUIDs");
+	ad_clear_uuids(type);
+	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+							prop_names.uuid[type]);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void ad_clear_service(void)
+static void ad_clear_solicit(int type)
 {
-	g_free(ad.service.uuid);
-	memset(&ad.service, 0, sizeof(ad.service));
+	g_strfreev(ad.solicit[type]);
+	ad.solicit[type] = NULL;
+	ad.solicit_len[type] = 0;
+}
+
+void ad_advertise_solicit(DBusConnection *conn, int type,
+							int argc, char *argv[])
+{
+	if (argc < 2 || !strlen(argv[1])) {
+		print_ad_solicit(type);
+		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+	}
+
+	ad_clear_solicit(type);
+
+	ad.solicit[type] = g_strdupv(&argv[1]);
+	if (!ad.solicit[type]) {
+		bt_shell_printf("Failed to parse input\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	ad.solicit_len[type] = g_strv_length(ad.solicit[type]);
+
+	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+						prop_names.solicit[type]);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+void ad_disable_solicit(DBusConnection *conn, int type)
+{
+	if (!ad.solicit[type])
+		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+
+	ad_clear_solicit(type);
+	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+						prop_names.solicit[type]);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void ad_clear_service(int type)
+{
+	g_free(ad.service[type].uuid);
+	memset(&ad.service[type], 0, sizeof(ad.service[type]));
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -658,15 +906,17 @@  static bool ad_add_data(struct ad_data *data, int argc, char *argv[])
 	return true;
 }
 
-void ad_advertise_service(DBusConnection *conn, int argc, char *argv[])
+void ad_advertise_service(DBusConnection *conn, int type,
+							int argc, char *argv[])
 {
 	struct ad_data data;
 
 	if (argc < 2 || !strlen(argv[1])) {
-		if (ad.service.uuid) {
-			print_uuid(ad.service.uuid);
-			bt_shell_hexdump(ad.service.data.data,
-						ad.service.data.len);
+		if (ad.service[type].uuid) {
+			print_uuid(ad_names.service[type],
+						ad.service[type].uuid);
+			bt_shell_hexdump(ad.service[type].data.data,
+						ad.service[type].data.len);
 		}
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 	}
@@ -674,46 +924,49 @@  void ad_advertise_service(DBusConnection *conn, int argc, char *argv[])
 	if (!ad_add_data(&data, argc - 2, argv + 2))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 
-	ad_clear_service();
+	ad_clear_service(type);
 
-	ad.service.uuid = g_strdup(argv[1]);
-	ad.service.data = data;
+	ad.service[type].uuid = g_strdup(argv[1]);
+	ad.service[type].data = data;
 
-	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceData");
+	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+						prop_names.service[type]);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-void ad_disable_service(DBusConnection *conn)
+void ad_disable_service(DBusConnection *conn, int type)
 {
-	if (!ad.service.uuid)
+	if (!ad.service[type].uuid)
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 
-	ad_clear_service();
-	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceData");
+	ad_clear_service(type);
+	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+						prop_names.service[type]);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void ad_clear_manufacturer(void)
+static void ad_clear_manufacturer(int type)
 {
-	memset(&ad.manufacturer, 0, sizeof(ad.manufacturer));
+	memset(&ad.manufacturer[type], 0, sizeof(ad.manufacturer[type]));
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-void ad_advertise_manufacturer(DBusConnection *conn, int argc, char *argv[])
+void ad_advertise_manufacturer(DBusConnection *conn, int type,
+							int argc, char *argv[])
 {
 	char *endptr = NULL;
 	long int val;
 	struct ad_data data;
 
 	if (argc < 2 || !strlen(argv[1])) {
-		if (ad.manufacturer.data.len) {
-			bt_shell_printf("Manufacturer: %u\n",
-						ad.manufacturer.id);
-			bt_shell_hexdump(ad.manufacturer.data.data,
-						ad.manufacturer.data.len);
+		if (ad.manufacturer[type].data.len) {
+			bt_shell_printf("%s: %u\n", ad_names.manufacturer[type],
+						ad.manufacturer[type].id);
+			bt_shell_hexdump(ad.manufacturer[type].data.data,
+						ad.manufacturer[type].data.len);
 		}
 
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
@@ -728,45 +981,48 @@  void ad_advertise_manufacturer(DBusConnection *conn, int argc, char *argv[])
 	if (!ad_add_data(&data, argc - 2, argv + 2))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 
-	ad_clear_manufacturer();
-	ad.manufacturer.id = val;
-	ad.manufacturer.data = data;
+	ad_clear_manufacturer(type);
+	ad.manufacturer[type].id = val;
+	ad.manufacturer[type].data = data;
 
 	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
-							"ManufacturerData");
+						prop_names.manufacturer[type]);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-void ad_disable_manufacturer(DBusConnection *conn)
+void ad_disable_manufacturer(DBusConnection *conn, int type)
 {
-	if (!ad.manufacturer.id && !ad.manufacturer.data.len)
+	if (!ad.manufacturer[type].id && !ad.manufacturer[type].data.len)
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 
-	ad_clear_manufacturer();
+	ad_clear_manufacturer(type);
 	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
-							"ManufacturerData");
+						prop_names.manufacturer[type]);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void ad_clear_data(void)
+static void ad_clear_data(int type)
 {
-	memset(&ad.data, 0, sizeof(ad.data));
+	memset(&ad.data[type], 0, sizeof(ad.data[type]));
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-void ad_advertise_data(DBusConnection *conn, int argc, char *argv[])
+void ad_advertise_data(DBusConnection *conn, int type, int argc, char *argv[])
 {
 	char *endptr = NULL;
 	long int val;
 	struct ad_data data;
 
 	if (argc < 2 || !strlen(argv[1])) {
-		if (ad.data.data.len) {
-			bt_shell_printf("Type: 0x%02x\n", ad.data.type);
-			bt_shell_hexdump(ad.data.data.data, ad.data.data.len);
+		if (ad.data[type].data.len) {
+			bt_shell_printf("%s Type: 0x%02x\n",
+							ad_names.data[type],
+							ad.data[type].type);
+			bt_shell_hexdump(ad.data[type].data.data,
+							ad.data[type].data.len);
 		}
 
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
@@ -781,23 +1037,25 @@  void ad_advertise_data(DBusConnection *conn, int argc, char *argv[])
 	if (!ad_add_data(&data, argc - 2, argv + 2))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 
-	ad_clear_data();
-	ad.data.valid = true;
-	ad.data.type = val;
-	ad.data.data = data;
+	ad_clear_data(type);
+	ad.data[type].valid = true;
+	ad.data[type].type = val;
+	ad.data[type].data = data;
 
-	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Data");
+	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+							prop_names.data[type]);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-void ad_disable_data(DBusConnection *conn)
+void ad_disable_data(DBusConnection *conn, int type)
 {
-	if (!ad.data.type && !ad.data.data.len)
+	if (!ad.data[type].type && !ad.data[type].data.len)
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 
-	ad_clear_data();
-	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Data");
+	ad_clear_data(type);
+	g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+							prop_names.data[type]);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
diff --git a/client/advertising.h b/client/advertising.h
index 145ac80..9d124c7 100644
--- a/client/advertising.h
+++ b/client/advertising.h
@@ -8,15 +8,24 @@ 
  *
  */
 
+#define AD_TYPE_AD	0
+#define AD_TYPE_SRD	1
+#define AD_TYPE_COUNT	2
+
 void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type);
 void ad_unregister(DBusConnection *conn, GDBusProxy *manager);
 
-void ad_advertise_uuids(DBusConnection *conn, int argc, char *argv[]);
-void ad_disable_uuids(DBusConnection *conn);
-void ad_advertise_service(DBusConnection *conn, int argc, char *argv[]);
-void ad_disable_service(DBusConnection *conn);
-void ad_advertise_manufacturer(DBusConnection *conn, int argc, char *argv[]);
-void ad_disable_manufacturer(DBusConnection *conn);
+void ad_advertise_uuids(DBusConnection *conn, int type, int argc, char *argv[]);
+void ad_disable_uuids(DBusConnection *conn, int type);
+void ad_advertise_solicit(DBusConnection *conn, int type,
+							int argc, char *argv[]);
+void ad_disable_solicit(DBusConnection *conn, int type);
+void ad_advertise_service(DBusConnection *conn, int type,
+							int argc, char *argv[]);
+void ad_disable_service(DBusConnection *conn, int type);
+void ad_advertise_manufacturer(DBusConnection *conn, int type,
+							int argc, char *argv[]);
+void ad_disable_manufacturer(DBusConnection *conn, int type);
 void ad_advertise_tx_power(DBusConnection *conn, dbus_bool_t *value);
 void ad_advertise_name(DBusConnection *conn, bool value);
 void ad_advertise_appearance(DBusConnection *conn, bool value);
@@ -24,8 +33,8 @@  void ad_advertise_local_name(DBusConnection *conn, const char *name);
 void ad_advertise_local_appearance(DBusConnection *conn, long int *value);
 void ad_advertise_duration(DBusConnection *conn, long int *value);
 void ad_advertise_timeout(DBusConnection *conn, long int *value);
-void ad_advertise_data(DBusConnection *conn, int argc, char *argv[]);
-void ad_disable_data(DBusConnection *conn);
+void ad_advertise_data(DBusConnection *conn, int type, int argc, char *argv[]);
+void ad_disable_data(DBusConnection *conn, int type);
 void ad_advertise_discoverable(DBusConnection *conn, dbus_bool_t *value);
 void ad_advertise_discoverable_timeout(DBusConnection *conn, long int *value);
 void ad_advertise_secondary(DBusConnection *conn, const char *value);
diff --git a/client/bluetoothctl-advertise.rst b/client/bluetoothctl-advertise.rst
index 4b56324..008f800 100644
--- a/client/bluetoothctl-advertise.rst
+++ b/client/bluetoothctl-advertise.rst
@@ -31,6 +31,12 @@  Set/Get advertise uuids.
 :Example: **# uuids 0x12345678**
 :Example: **# uuids 90f95193-35de-4306-a6e9-699328f15059**
 
+solicit
+-------
+
+Set/Get advertise solicit uuids.
+:Usage: **# solicit [all/uuid1 uuid2 ...]**
+
 service
 -------
 
@@ -71,6 +77,39 @@  To get the currently set data use the command data without any arguments.
 :Usage: **# data [type] [data=xx xx ...]**
 :Example: **# data 0x0C 01 0x0F 13**
 
+sr-uuids
+--------
+
+Set/Get scan response uuids.
+
+:Usage: **# sr-uuids [all/uuid1 uuid2 ...]**
+
+sr-solicit
+----------
+
+Set/Get scan response solicit uuids.
+:Usage: **# sr-solicit [all/uuid1 uuid2 ...]**
+
+sr-service
+----------
+
+Set/Get scan response service data.
+
+:Usage: **# sr-service [uuid] [data=xx xx ...]**
+
+sr-manufacturer
+---------------
+
+Set/Get scan response manufacturer data.
+
+:Usage: **# sr-manufacturer [id] [data=xx xx ...]**
+
+sr-data
+-------
+
+Set/Get scan response data.
+:Usage: **# sr-data [type] [data=xx xx ...]**
+
 discoverable
 ------------
 
diff --git a/client/main.c b/client/main.c
index 3f8143d..3485e1a 100644
--- a/client/main.c
+++ b/client/main.c
@@ -2538,22 +2538,52 @@  static char *ad_generator(const char *text, int state)
 
 static void cmd_advertise_uuids(int argc, char *argv[])
 {
-	ad_advertise_uuids(dbus_conn, argc, argv);
+	ad_advertise_uuids(dbus_conn, AD_TYPE_AD, argc, argv);
+}
+
+static void cmd_advertise_solicit(int argc, char *argv[])
+{
+	ad_advertise_solicit(dbus_conn, AD_TYPE_AD, argc, argv);
 }
 
 static void cmd_advertise_service(int argc, char *argv[])
 {
-	ad_advertise_service(dbus_conn, argc, argv);
+	ad_advertise_service(dbus_conn, AD_TYPE_AD, argc, argv);
 }
 
 static void cmd_advertise_manufacturer(int argc, char *argv[])
 {
-	ad_advertise_manufacturer(dbus_conn, argc, argv);
+	ad_advertise_manufacturer(dbus_conn, AD_TYPE_AD, argc, argv);
 }
 
 static void cmd_advertise_data(int argc, char *argv[])
 {
-	ad_advertise_data(dbus_conn, argc, argv);
+	ad_advertise_data(dbus_conn, AD_TYPE_AD, argc, argv);
+}
+
+static void cmd_advertise_sr_uuids(int argc, char *argv[])
+{
+	ad_advertise_uuids(dbus_conn, AD_TYPE_SRD, argc, argv);
+}
+
+static void cmd_advertise_sr_solicit(int argc, char *argv[])
+{
+	ad_advertise_solicit(dbus_conn, AD_TYPE_SRD, argc, argv);
+}
+
+static void cmd_advertise_sr_service(int argc, char *argv[])
+{
+	ad_advertise_service(dbus_conn, AD_TYPE_SRD, argc, argv);
+}
+
+static void cmd_advertise_sr_manufacturer(int argc, char *argv[])
+{
+	ad_advertise_manufacturer(dbus_conn, AD_TYPE_SRD, argc, argv);
+}
+
+static void cmd_advertise_sr_data(int argc, char *argv[])
+{
+	ad_advertise_data(dbus_conn, AD_TYPE_SRD, argc, argv);
 }
 
 static void cmd_advertise_discoverable(int argc, char *argv[])
@@ -2753,22 +2783,52 @@  static void cmd_advertise_rsi(int argc, char *argv[])
 
 static void ad_clear_uuids(void)
 {
-	ad_disable_uuids(dbus_conn);
+	ad_disable_uuids(dbus_conn, AD_TYPE_AD);
+}
+
+static void ad_clear_solicit(void)
+{
+	ad_disable_solicit(dbus_conn, AD_TYPE_AD);
 }
 
 static void ad_clear_service(void)
 {
-	ad_disable_service(dbus_conn);
+	ad_disable_service(dbus_conn, AD_TYPE_AD);
 }
 
 static void ad_clear_manufacturer(void)
 {
-	ad_disable_manufacturer(dbus_conn);
+	ad_disable_manufacturer(dbus_conn, AD_TYPE_AD);
 }
 
 static void ad_clear_data(void)
 {
-	ad_disable_data(dbus_conn);
+	ad_disable_data(dbus_conn, AD_TYPE_AD);
+}
+
+static void ad_clear_sr_uuids(void)
+{
+	ad_disable_uuids(dbus_conn, AD_TYPE_SRD);
+}
+
+static void ad_clear_sr_solicit(void)
+{
+	ad_disable_solicit(dbus_conn, AD_TYPE_SRD);
+}
+
+static void ad_clear_sr_service(void)
+{
+	ad_disable_service(dbus_conn, AD_TYPE_SRD);
+}
+
+static void ad_clear_sr_manufacturer(void)
+{
+	ad_disable_manufacturer(dbus_conn, AD_TYPE_SRD);
+}
+
+static void ad_clear_sr_data(void)
+{
+	ad_disable_data(dbus_conn, AD_TYPE_SRD);
 }
 
 static void ad_clear_tx_power(void)
@@ -2819,9 +2879,15 @@  static void ad_clear_interval(void)
 
 static const struct clear_entry ad_clear[] = {
 	{ "uuids",		ad_clear_uuids },
+	{ "solicit",		ad_clear_solicit },
 	{ "service",		ad_clear_service },
 	{ "manufacturer",	ad_clear_manufacturer },
 	{ "data",		ad_clear_data },
+	{ "sr-uuids",		ad_clear_sr_uuids },
+	{ "sr-solicit",		ad_clear_sr_solicit },
+	{ "sr-service",		ad_clear_sr_service },
+	{ "sr-manufacturer",	ad_clear_sr_manufacturer },
+	{ "sr-data",		ad_clear_sr_data },
 	{ "tx-power",		ad_clear_tx_power },
 	{ "name",		ad_clear_name },
 	{ "appearance",		ad_clear_appearance },
@@ -2922,6 +2988,8 @@  static const struct bt_shell_menu advertise_menu = {
 	.entries = {
 	{ "uuids", "[uuid1 uuid2 ...]", cmd_advertise_uuids,
 			"Set/Get advertise uuids" },
+	{ "solicit", "[uuid1 uuid2 ...]", cmd_advertise_solicit,
+			"Set/Get advertise solicit uuids" },
 	{ "service", "[uuid] [data=xx xx ...]", cmd_advertise_service,
 			"Set/Get advertise service data" },
 	{ "manufacturer", "[id] [data=xx xx ...]",
@@ -2929,6 +2997,17 @@  static const struct bt_shell_menu advertise_menu = {
 			"Set/Get advertise manufacturer data" },
 	{ "data", "[type] [data=xx xx ...]", cmd_advertise_data,
 			"Set/Get advertise data" },
+	{ "sr-uuids", "[uuid1 uuid2 ...]", cmd_advertise_sr_uuids,
+			"Set/Get scan response uuids" },
+	{ "sr-solicit", "[uuid1 uuid2 ...]", cmd_advertise_sr_solicit,
+			"Set/Get scan response solicit uuids" },
+	{ "sr-service", "[uuid] [data=xx xx ...]", cmd_advertise_sr_service,
+			"Set/Get scan response service data" },
+	{ "sr-manufacturer", "[id] [data=xx xx ...]",
+			cmd_advertise_sr_manufacturer,
+			"Set/Get scan response manufacturer data" },
+	{ "sr-data", "[type] [data=xx xx ...]", cmd_advertise_sr_data,
+			"Set/Get scan response data" },
 	{ "discoverable", "[on/off]", cmd_advertise_discoverable,
 			"Set/Get advertise discoverable" },
 	{ "discoverable-timeout", "[seconds]",