[PATCH BlueZ v2] client: Support single profile connection/disconnection

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 client/bluetoothctl.rst | 20 +++++++--
 client/main.c           | 93 +++++++++++++++++++++++++++++++++++------
 src/uuid-helper.c       | 32 +++++++-------
 3 files changed, 114 insertions(+), 31 deletions(-)

diff --git a/client/bluetoothctl.rst b/client/bluetoothctl.rst
index b6c2efa35..c60bf719f 100644
--- a/client/bluetoothctl.rst
+++ b/client/bluetoothctl.rst
@@ -9,7 +9,7 @@ Bluetooth Control Command Line Tool
 :Version: BlueZ
 :Copyright: Free use of this software is granted under the terms of the GNU
             Lesser General Public Licenses (LGPL).
-:Date: November 2022
+:Date: March 2024
 :Manual section: 1
 :Manual group: Linux System Administration
 
@@ -262,6 +262,13 @@ Connect device.
 
 This will initiate a connection to a device.
 
+By default this commands tries to connect all the profiles the remote device
+supports and have been flagged as auto-connectable. In case when the UUID of
+the remote service is given only that service will be connected. The UUID can
+be either a short form (16-bit UUID) or a long form (128-bit UUID). There are
+also some special values for well-known profiles like "a2dp-sink",
+"a2dp-source", "hfp-hf", "hfp-ag", "ftp" or "spp".
+
 To connect with an LE device the controller must have an active scan report of
 the device it wants to connect to.
 
@@ -269,17 +276,24 @@ If no advertising report is received before the timeout a
 le-connection-abort-by-local error will be issued. In that case either try
 again to connect assuming the device is advertising.
 
-:Usage: **> connect <dev>**
+:Usage: **> connect <dev> [uuid]**
+:Example: **> connect 1C:48:F9:9D:81:5C hsp-hs**
+:Example: **> connect 1C:48:F9:9D:81:5C 00001108-0000-1000-8000-00805f9b34fb**
+:Example: **> connect 1C:48:F9:9D:81:5C 0x1108**
 
 disconnect
 ----------
 
 Disconnect device.
 
+By default this commands disconnects all profiles and then terminates the
+connection. In case when the UUID of the remote service is given only that
+service will be disconnected.
+
 For LE when disconnecting from an active connection the device address is not
 needed.
 
-:Usage: **> disconnect <dev>**
+:Usage: **> disconnect <dev> [uuid]**
 
 info
 ----
diff --git a/client/main.c b/client/main.c
index 6b938da3f..3f2bfcf6b 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1981,13 +1981,44 @@ static void cmd_remove(int argc, char *argv[])
 	remove_device(proxy);
 }
 
+struct connection_data {
+	GDBusProxy *proxy;
+	char *uuid;
+};
+
+static void connection_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct connection_data *data = user_data;
+
+	if (!data->uuid)
+		return;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->uuid);
+}
+
+static void format_connection_profile(char *output, size_t size,
+							const char *uuid)
+{
+	const char *text;
+
+	text = bt_uuidstr_to_str(uuid);
+	if (!text)
+		text = uuid;
+
+	snprintf(output, size, " profile \"%s\"", text);
+}
+
 static void connect_reply(DBusMessage *message, void *user_data)
 {
-	GDBusProxy *proxy = user_data;
+	struct connection_data *data = user_data;
+	GDBusProxy *proxy = data->proxy;
 	DBusError error;
 
 	dbus_error_init(&error);
 
+	g_free(data->uuid);
+	g_free(data);
+
 	if (dbus_set_error_from_message(&error, message) == TRUE) {
 		bt_shell_printf("Failed to connect: %s %s\n", error.name,
 				error.message);
@@ -2003,6 +2034,9 @@ static void connect_reply(DBusMessage *message, void *user_data)
 
 static void cmd_connect(int argc, char *argv[])
 {
+	struct connection_data *data;
+	const char *method = "Connect";
+	char profile[128] = "";
 	GDBusProxy *proxy;
 
 	if (check_default_ctrl() == FALSE)
@@ -2014,31 +2048,49 @@ static void cmd_connect(int argc, char *argv[])
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
-	if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
-							proxy, NULL) == FALSE) {
+	data = new0(struct connection_data, 1);
+	data->proxy = proxy;
+
+	if (argc == 3) {
+		method = "ConnectProfile";
+		data->uuid = g_strdup(argv[2]);
+		format_connection_profile(profile, sizeof(profile), argv[2]);
+	}
+
+	if (g_dbus_proxy_method_call(proxy, method, connection_setup,
+					connect_reply, data, NULL) == FALSE) {
 		bt_shell_printf("Failed to connect\n");
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
-	bt_shell_printf("Attempting to connect to %s\n", argv[1]);
+	bt_shell_printf("Attempting to connect%s to %s\n", profile, argv[1]);
 }
 
 static void disconn_reply(DBusMessage *message, void *user_data)
 {
-	GDBusProxy *proxy = user_data;
+	struct connection_data *data = user_data;
+	const bool profile_disconnected = data->uuid != NULL;
+	GDBusProxy *proxy = data->proxy;
 	DBusError error;
 
 	dbus_error_init(&error);
 
+	g_free(data->uuid);
+	g_free(data);
+
 	if (dbus_set_error_from_message(&error, message) == TRUE) {
 		bt_shell_printf("Failed to disconnect: %s\n", error.name);
 		dbus_error_free(&error);
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
-	bt_shell_printf("Successful disconnected\n");
+	bt_shell_printf("Disconnection successful\n");
 
-	if (proxy == default_dev)
+	/* If only a single profile was disconnected, the device itself might
+	 * still be connected. In that case, let the property change handler
+	 * take care of setting the default device to NULL.
+	 */
+	if (proxy == default_dev && !profile_disconnected)
 		set_default_device(NULL, NULL);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
@@ -2046,19 +2098,31 @@ static void disconn_reply(DBusMessage *message, void *user_data)
 
 static void cmd_disconn(int argc, char *argv[])
 {
+	struct connection_data *data;
+	const char *method = "Disconnect";
+	char profile[128] = "";
 	GDBusProxy *proxy;
 
 	proxy = find_device(argc, argv);
 	if (!proxy)
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 
-	if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
-							proxy, NULL) == FALSE) {
+	data = new0(struct connection_data, 1);
+	data->proxy = proxy;
+
+	if (argc == 3) {
+		method = "DisconnectProfile";
+		data->uuid = g_strdup(argv[2]);
+		format_connection_profile(profile, sizeof(profile), argv[2]);
+	}
+
+	if (g_dbus_proxy_method_call(proxy, method, connection_setup,
+					disconn_reply, data, NULL) == FALSE) {
 		bt_shell_printf("Failed to disconnect\n");
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
-	bt_shell_printf("Attempting to disconnect from %s\n",
+	bt_shell_printf("Attempting to disconnect%s from %s\n", profile,
 						proxy_address(proxy));
 }
 
@@ -3253,10 +3317,13 @@ static const struct bt_shell_menu main_menu = {
 								dev_generator },
 	{ "remove",       "<dev>",    cmd_remove, "Remove device",
 							dev_generator },
-	{ "connect",      "<dev>",    cmd_connect, "Connect device",
-							dev_generator },
-	{ "disconnect",   "[dev]",    cmd_disconn, "Disconnect device",
+	{ "connect",      "<dev> [uuid]", cmd_connect,
+				"Connect a device and all its profiles or "
+				"optionally connect a single profile only",
 							dev_generator },
+	{ "disconnect",   "[dev] [uuid]", cmd_disconn,
+				"Disconnect a device or optionally disconnect "
+				"a single profile only", dev_generator },
 	{ "wake",         "[dev] [on/off]",    cmd_wake, "Get/Set wake support",
 							dev_generator },
 	{ } },
diff --git a/src/uuid-helper.c b/src/uuid-helper.c
index f32ee0a85..640592fd2 100644
--- a/src/uuid-helper.c
+++ b/src/uuid-helper.c
@@ -101,29 +101,31 @@ static struct {
 	const char	*name;
 	uint16_t	class;
 } bt_services[] = {
-	{ "pbap",	PBAP_SVCLASS_ID			},
-	{ "sap",	SAP_SVCLASS_ID			},
-	{ "ftp",	OBEX_FILETRANS_SVCLASS_ID	},
-	{ "bpp",	BASIC_PRINTING_SVCLASS_ID	},
+	{ "a2dp-sink",	AUDIO_SINK_SVCLASS_ID		},
+	{ "a2dp-source",AUDIO_SOURCE_SVCLASS_ID		},
 	{ "bip",	IMAGING_SVCLASS_ID		},
-	{ "synch",	IRMC_SYNC_SVCLASS_ID		},
+	{ "bpp",	BASIC_PRINTING_SVCLASS_ID	},
 	{ "dun",	DIALUP_NET_SVCLASS_ID		},
-	{ "opp",	OBEX_OBJPUSH_SVCLASS_ID		},
 	{ "fax",	FAX_SVCLASS_ID			},
-	{ "spp",	SERIAL_PORT_SVCLASS_ID		},
-	{ "hsp",	HEADSET_SVCLASS_ID		},
-	{ "hsp-hs",	HEADSET_SVCLASS_ID		},
-	{ "hsp-ag",	HEADSET_AGW_SVCLASS_ID		},
+	{ "ftp",	OBEX_FILETRANS_SVCLASS_ID	},
+	{ "gnss",	GNSS_SERVER_SVCLASS_ID		},
 	{ "hfp",	HANDSFREE_SVCLASS_ID		},
-	{ "hfp-hf",	HANDSFREE_SVCLASS_ID		},
 	{ "hfp-ag",	HANDSFREE_AGW_SVCLASS_ID	},
-	{ "pbap-pce",	PBAP_PCE_SVCLASS_ID		},
-	{ "pbap-pse",	PBAP_PSE_SVCLASS_ID		},
-	{ "map-mse",	MAP_MSE_SVCLASS_ID		},
+	{ "hfp-hf",	HANDSFREE_SVCLASS_ID		},
+	{ "hsp",	HEADSET_SVCLASS_ID		},
+	{ "hsp-ag",	HEADSET_AGW_SVCLASS_ID		},
+	{ "hsp-hs",	HEADSET_SVCLASS_ID		},
 	{ "map-mas",	MAP_MSE_SVCLASS_ID		},
 	{ "map-mce",	MAP_MCE_SVCLASS_ID		},
 	{ "map-mns",	MAP_MCE_SVCLASS_ID		},
-	{ "gnss",	GNSS_SERVER_SVCLASS_ID		},
+	{ "map-mse",	MAP_MSE_SVCLASS_ID		},
+	{ "opp",	OBEX_OBJPUSH_SVCLASS_ID		},
+	{ "pbap",	PBAP_SVCLASS_ID			},
+	{ "pbap-pce",	PBAP_PCE_SVCLASS_ID		},
+	{ "pbap-pse",	PBAP_PSE_SVCLASS_ID		},
+	{ "sap",	SAP_SVCLASS_ID			},
+	{ "spp",	SERIAL_PORT_SVCLASS_ID		},
+	{ "synch",	IRMC_SYNC_SVCLASS_ID		},
 	{ }
 };
 
-- 
2.39.5





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux