linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bluez v3] bearer: Implement Connect/Disconnect methods
@ 2025-11-18  9:29 Ye He via B4 Relay
  2025-11-18 10:23 ` [bluez,v3] " bluez.test.bot
  2025-11-18 14:14 ` [PATCH bluez v3] " Luiz Augusto von Dentz
  0 siblings, 2 replies; 7+ messages in thread
From: Ye He via B4 Relay @ 2025-11-18  9:29 UTC (permalink / raw)
  To: Linux Bluetooth; +Cc: Ye He

From: Ye He <ye.he@amlogic.com>

This patch provides implementations for the Connect and Disconnect
methods of the org.bluez.Bearer.LE1 and org.bluez.Bearer.BREDR1
interfaces.

Signed-off-by: Ye He <ye.he@amlogic.com>
---
Changes in v3:
- Move the modification logic from the device to bearer, and add
- some helper functions to the device.
- Link to v2: https://patch.msgid.link/20251113-bearer-impl-v2-1-c3e825cc6758@amlogic.com

Changes in v2:
- Fix build error & warning.
- Link to v1: https://patch.msgid.link/20251111-bearer-impl-v1-1-f41585144218@amlogic.com
---
 src/bearer.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/device.c | 130 ++++++++++++++++++++++++++++++++++++++++--------
 src/device.h |  17 +++++++
 3 files changed, 285 insertions(+), 22 deletions(-)

diff --git a/src/bearer.c b/src/bearer.c
index 9723b59e2e7b39b2974e258363aa0ace95862651..46e272b16096740ce28bcc80d0e2426df8538a2e 100644
--- a/src/bearer.c
+++ b/src/bearer.c
@@ -25,23 +25,38 @@
 
 #include "bluetooth/bluetooth.h"
 #include "bluetooth/mgmt.h"
+#include "bluetooth/uuid.h"
 
 #include "gdbus/gdbus.h"
 #include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/timeout.h"
 
 #include "log.h"
 #include "error.h"
 #include "adapter.h"
 #include "device.h"
+#include "profile.h"
+#include "service.h"
 #include "dbus-common.h"
 #include "bearer.h"
 
+#define DISCONNECT_TIMER	2
+
 struct btd_bearer {
 	struct btd_device *device;
 	uint8_t type;
 	const char *path;
+	unsigned int disconn_timer;
+	struct queue *disconnects; /* disconnects message */
+	DBusMessage *connect; /* connect message */
 };
 
+static void bearer_free_dbus_message(void *data)
+{
+    dbus_message_unref((DBusMessage *)data);
+}
+
 static void bearer_free(void *data)
 {
 	struct btd_bearer *bearer = data;
@@ -49,17 +64,126 @@ static void bearer_free(void *data)
 	free(bearer);
 }
 
+static void bearer_disconnect_service(struct btd_service *service, void *user_data)
+{
+	uint8_t bdaddr_type = *(uint8_t *)user_data;
+	struct btd_profile *profile;
+	bt_uuid_t uuid;
+
+	profile = btd_service_get_profile(service);
+	if (!profile || !profile->remote_uuid)
+		return;
+
+	if (bt_string_to_uuid(&uuid, profile->remote_uuid) != 0)
+		return;
+
+	if (bdaddr_type == BDADDR_BREDR) {
+		if (uuid.value.u16 < 0x1100 || uuid.value.u16 > 0x11FF)
+			return;
+	} else {
+		if (uuid.value.u16 < 0x1800 || uuid.value.u16 > 0x18FF)
+			return;
+	}
+
+	DBG("Disconnecting profile %s (UUID 0x%04x) for bearer addr type %u",
+	profile->name ?: "(unknown)", uuid.value.u16, bdaddr_type);
+
+	btd_service_disconnect(service);
+}
+
+static bool bearer_disconnect_link(gpointer user_data)
+{
+	struct btd_bearer *bearer = user_data;
+	struct btd_device *device = bearer->device;
+
+	bearer->disconn_timer = 0;
+
+	if (btd_device_bdaddr_type_connected(device, bearer->type))
+		btd_adapter_disconnect_device(device_get_adapter(device),
+						device_get_address(device),
+						bearer->type);
+	return FALSE;
+}
+
 static DBusMessage *bearer_connect(DBusConnection *conn, DBusMessage *msg,
 							void *user_data)
 {
-	/* TODO */
+	struct btd_bearer *bearer = user_data;
+	struct btd_device *device = bearer->device;
+	int err;
+
+	if (btd_device_bdaddr_type_connected(device, bearer->type)) {
+		if (msg)
+			return btd_error_already_connected(msg);
+		return NULL;
+	}
+
+	if (device_is_bonding(device, NULL)) {
+		if (msg)
+			return btd_error_in_progress(msg);
+		return NULL;
+	}
+
+	if (device_is_connecting(device) ||
+		bearer->connect) {
+		if (msg)
+			return btd_error_in_progress(msg);
+		return NULL;
+	}
+
+	if (msg)
+		bearer->connect = dbus_message_ref(msg);
+
+	if (bearer->type == BDADDR_BREDR)
+		return device_connect_profiles(device, BDADDR_BREDR,
+								msg, NULL);
+	else {
+		btd_device_set_temporary(device, false);
+		err = device_connect_le(device);
+		if (err < 0)
+			return btd_error_failed(msg, strerror(-err));
+	}
+
 	return NULL;
 }
 
 static DBusMessage *bearer_disconnect(DBusConnection *conn, DBusMessage *msg,
 							void *user_data)
 {
-	/* TODO */
+	struct btd_bearer *bearer = user_data;
+	struct btd_device *device = bearer->device;
+
+	if (!btd_device_bdaddr_type_connected(device, bearer->type)) {
+		if (msg)
+			return btd_error_not_connected(msg);
+		return NULL;
+	}
+
+	if (device_is_disconnecting(device)) {
+		if (msg)
+			return btd_error_in_progress(msg);
+		return NULL;
+	}
+
+	if (msg)
+		queue_push_tail(bearer->disconnects, dbus_message_ref(msg));
+
+	device_cancel_bonding(device, MGMT_STATUS_DISCONNECTED);
+
+	device_cancel_browse(device, bearer->type);
+
+	btd_device_foreach_service(device, bearer_disconnect_service,
+							&bearer->type);
+
+	device_remove_pending_services(device, bearer->type);
+
+	if (bearer->disconn_timer)
+		return NULL;
+
+	bearer->disconn_timer = timeout_add_seconds(DISCONNECT_TIMER,
+							bearer_disconnect_link,
+							bearer, NULL);
+
 	return NULL;
 }
 
@@ -151,6 +275,7 @@ struct btd_bearer *btd_bearer_new(struct btd_device *device, uint8_t type)
 	bearer->device = device;
 	bearer->type = type;
 	bearer->path = device_get_path(device);
+	bearer->disconnects = queue_new();
 
 	if (!g_dbus_register_interface(btd_get_dbus_connection(),
 					bearer->path, bearer_interface(type),
@@ -174,6 +299,16 @@ void btd_bearer_destroy(struct btd_bearer *bearer)
 		return;
 	}
 
+	if (bearer->disconnects) {
+		queue_destroy(bearer->disconnects, bearer_free_dbus_message);
+		bearer->disconnects = NULL;
+	}
+
+	if (bearer->connect) {
+		dbus_message_unref(bearer->connect);
+		bearer->connect = NULL;
+	}
+
 	g_dbus_unregister_interface(btd_get_dbus_connection(), bearer->path,
 					bearer_interface(bearer->type));
 }
@@ -203,6 +338,13 @@ void btd_bearer_connected(struct btd_bearer *bearer)
 	if (!bearer || !bearer->path)
 		return;
 
+	if (bearer->connect) {
+		g_dbus_send_reply(btd_get_dbus_connection(), bearer->connect,
+						DBUS_TYPE_INVALID);
+		dbus_message_unref(bearer->connect);
+		bearer->connect = NULL;
+	}
+
 	g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
 					bearer_interface(bearer->type),
 					"Connected");
@@ -212,10 +354,24 @@ void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason)
 {
 	const char *name;
 	const char *message;
+	DBusMessage *msg;
+	const struct queue_entry *entry;
 
 	if (!bearer || !bearer->path)
 		return;
 
+	if (!btd_device_is_connected(bearer->device))
+		device_disconnect_watches_callback(bearer->device);
+
+	while (!queue_isempty(bearer->disconnects)) {
+		entry = queue_get_entries(bearer->disconnects);
+		msg = entry->data;
+		g_dbus_send_reply(btd_get_dbus_connection(), msg,
+						DBUS_TYPE_INVALID);
+		queue_remove(bearer->disconnects, msg);
+		dbus_message_unref(msg);
+	}
+
 	g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
 					bearer_interface(bearer->type),
 					"Connected");
diff --git a/src/device.c b/src/device.c
index 91b6cc0c65eaae8058cd445c0942ffee57289f0d..918f1e51acd3a9cd6b776cc080c198885511fb20 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2022,6 +2022,28 @@ static void dev_disconn_service(gpointer a, gpointer b)
 	btd_service_disconnect(a);
 }
 
+void device_disconnect_watches_callback(struct btd_device *device)
+{
+	if (!device || !device->watches)
+		return;
+
+	while (device->watches) {
+		struct btd_disconnect_data *data = device->watches->data;
+
+		if (data->watch)
+			/* temporary is set if device is going to be removed */
+			data->watch(device, device->temporary,
+							data->user_data);
+
+		/* Check if the watch has been removed by callback function */
+		if (!g_slist_find(device->watches, data))
+			continue;
+
+		device->watches = g_slist_remove(device->watches, data);
+		g_free(data);
+	}
+}
+
 void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
 {
 	if (device->bonding)
@@ -2063,21 +2085,7 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
 	g_slist_free(device->pending);
 	device->pending = NULL;
 
-	while (device->watches) {
-		struct btd_disconnect_data *data = device->watches->data;
-
-		if (data->watch)
-			/* temporary is set if device is going to be removed */
-			data->watch(device, device->temporary,
-							data->user_data);
-
-		/* Check if the watch has been removed by callback function */
-		if (!g_slist_find(device->watches, data))
-			continue;
-
-		device->watches = g_slist_remove(device->watches, data);
-		g_free(data);
-	}
+	device_disconnect_watches_callback(device);
 
 	if (!btd_device_is_connected(device)) {
 		if (msg)
@@ -2095,6 +2103,11 @@ bool device_is_disconnecting(struct btd_device *device)
 	return device->disconn_timer > 0;
 }
 
+bool device_is_connecting(struct btd_device *device)
+{
+	return device->connect != NULL;
+}
+
 static void add_set(void *data, void *user_data)
 {
 	struct sirk_info *sirk = data;
@@ -2712,8 +2725,8 @@ int btd_device_connect_services(struct btd_device *dev, GSList *services)
 	return connect_next(dev);
 }
 
-static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type,
-					DBusMessage *msg, const char *uuid)
+DBusMessage *device_connect_profiles(struct btd_device *dev,
+		uint8_t bdaddr_type, DBusMessage *msg, const char *uuid)
 {
 	struct bearer_state *state = get_state(dev, bdaddr_type);
 	int err;
@@ -2826,7 +2839,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
 		return NULL;
 	}
 
-	return connect_profiles(dev, bdaddr_type, msg, NULL);
+	return device_connect_profiles(dev, bdaddr_type, msg, NULL);
 }
 
 static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
@@ -2848,7 +2861,7 @@ static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
 		return btd_error_invalid_args_str(msg,
 					ERR_BREDR_CONN_INVALID_ARGUMENTS);
 
-	reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid);
+	reply = device_connect_profiles(dev, BDADDR_BREDR, msg, uuid);
 	free(uuid);
 
 	return reply;
@@ -3421,7 +3434,7 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
 	}
 }
 
-static void device_cancel_bonding(struct btd_device *device, uint8_t status)
+void device_cancel_bonding(struct btd_device *device, uint8_t status)
 {
 	struct bonding_req *bonding = device->bonding;
 	DBusMessage *reply;
@@ -6629,6 +6642,38 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
 	return err;
 }
 
+static gboolean device_is_browsing(struct btd_device *device, uint8_t bdaddr_type)
+{
+	if (!device->browse)
+		return FALSE;
+
+	if (bdaddr_type == BDADDR_BREDR && device->browse->type == BROWSE_SDP)
+		return TRUE;
+
+	if (bdaddr_type != BDADDR_BREDR && device->browse->type == BROWSE_GATT)
+		return TRUE;
+
+	return FALSE;
+}
+
+void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type)
+{
+	struct btd_adapter *adapter = device->adapter;
+
+	DBG("");
+
+	if (!device_is_browsing(device, bdaddr_type))
+		return;
+
+	if (bdaddr_type == BDADDR_BREDR)
+		bt_cancel_discovery(btd_adapter_get_address(adapter),
+							&device->bdaddr);
+	else
+		attio_cleanup(device);
+
+	browse_request_free(device->browse);
+}
+
 int device_discover_services(struct btd_device *device)
 {
 	int err;
@@ -8088,3 +8133,48 @@ void btd_device_foreach_service_data(struct btd_device *dev, bt_ad_func_t func,
 {
 	bt_ad_foreach_service_data(dev->ad, func, data);
 }
+
+
+void btd_device_foreach_service(struct btd_device *dev,
+				btd_device_service_func_t func,
+				void *user_data)
+{
+	GSList *l;
+
+	for (l = dev->services; l; l = l->next)
+		func(l->data, user_data);
+}
+
+void device_remove_pending_services(struct btd_device *dev,
+					uint8_t bdaddr_type)
+{
+	GSList *l = dev->pending;
+	GSList *next;
+	struct btd_service *service;
+	struct btd_profile *profile;
+	bt_uuid_t uuid;
+	bool match = false;
+
+	while (l) {
+		next = l->next;
+		service = l->data;
+
+		profile = btd_service_get_profile(service);
+		if (profile && profile->remote_uuid &&
+			bt_string_to_uuid(&uuid, profile->remote_uuid) == 0) {
+
+			if (bdaddr_type == BDADDR_BREDR)
+				match = uuid.value.u16 >= 0x1100 ||
+					uuid.value.u16 <= 0x11FF;
+			else
+				match = uuid.value.u16 >= 0x1800 ||
+					uuid.value.u16 <= 0x18FF;
+
+			if (match)
+				dev->pending = g_slist_remove(dev->pending,
+								service);
+		}
+
+		l = next;
+	}
+}
\ No newline at end of file
diff --git a/src/device.h b/src/device.h
index 6ed8affa0d4a9274d30cac9b48e8a6826edefd64..fee1ad50dc9c7ae248acd819dae07762c783ac35 100644
--- a/src/device.h
+++ b/src/device.h
@@ -120,6 +120,7 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
 gboolean device_is_bonding(struct btd_device *device, const char *sender);
 void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
 void device_bonding_failed(struct btd_device *device, uint8_t status);
+void device_cancel_bonding(struct btd_device *device, uint8_t status);
 struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device);
 int device_bonding_attempt_retry(struct btd_device *device);
 long device_bonding_last_duration(struct btd_device *device);
@@ -134,6 +135,9 @@ int device_notify_pincode(struct btd_device *device, gboolean secure,
 							const char *pincode);
 void device_cancel_authentication(struct btd_device *device, gboolean aborted);
 gboolean device_is_authenticating(struct btd_device *device);
+
+void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type);
+
 void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type,
 							uint32_t flags);
 void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
@@ -141,6 +145,7 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
 							uint8_t reason);
 void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
 bool device_is_disconnecting(struct btd_device *device);
+bool device_is_connecting(struct btd_device *device);
 void device_set_ltk(struct btd_device *device, const uint8_t val[16],
 				bool central, uint8_t enc_size);
 bool btd_device_get_ltk(struct btd_device *device, uint8_t val[16],
@@ -170,6 +175,7 @@ guint device_add_disconnect_watch(struct btd_device *device,
 				disconnect_watch watch, void *user_data,
 				GDestroyNotify destroy);
 void device_remove_disconnect_watch(struct btd_device *device, guint id);
+void device_disconnect_watches_callback(struct btd_device *device);
 int device_get_appearance(struct btd_device *device, uint16_t *value);
 void device_set_appearance(struct btd_device *device, uint16_t value);
 
@@ -184,6 +190,9 @@ void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
 
 int device_connect_le(struct btd_device *dev);
 
+DBusMessage *device_connect_profiles(struct btd_device *dev,
+		uint8_t bdaddr_type, DBusMessage *msg, const char *uuid);
+
 typedef void (*device_svc_cb_t) (struct btd_device *dev, int err,
 							void *user_data);
 
@@ -225,3 +234,11 @@ void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval,
 void btd_device_foreach_service_data(struct btd_device *dev,
 					bt_device_ad_func_t func,
 					void *data);
+
+typedef void (*btd_device_service_func_t)(struct btd_service *service,
+					void *user_data);
+void btd_device_foreach_service(struct btd_device *dev,
+				btd_device_service_func_t func,
+				void *user_data);
+void device_remove_pending_services(struct btd_device *dev,
+					uint8_t bdaddr_type);
\ No newline at end of file

---
base-commit: 5e41d1e1d361e7288964e4c2c5ed90736025662f
change-id: 20251111-bearer-impl-5f7e396174aa

Best regards,
-- 
Ye He <ye.he@amlogic.com>



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

* RE: [bluez,v3] bearer: Implement Connect/Disconnect methods
  2025-11-18  9:29 [PATCH bluez v3] bearer: Implement Connect/Disconnect methods Ye He via B4 Relay
@ 2025-11-18 10:23 ` bluez.test.bot
  2025-11-18 14:14 ` [PATCH bluez v3] " Luiz Augusto von Dentz
  1 sibling, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2025-11-18 10:23 UTC (permalink / raw)
  To: linux-bluetooth, ye.he

[-- Attachment #1: Type: text/plain, Size: 1602 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1024759

---Test result---

Test Summary:
CheckPatch                    PENDING   0.33 seconds
GitLint                       PENDING   0.34 seconds
BuildEll                      PASS      20.13 seconds
BluezMake                     PASS      628.69 seconds
MakeCheck                     PASS      21.57 seconds
MakeDistcheck                 PASS      244.27 seconds
CheckValgrind                 PASS      307.33 seconds
CheckSmatch                   WARNING   362.04 seconds
bluezmakeextell               PASS      184.85 seconds
IncrementalBuild              PENDING   0.29 seconds
ScanBuild                     PASS      986.43 seconds

Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:

##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:

##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/bearer.c: note: in included file:src/device.h:244:61: warning: no newline at end of filesrc/device.c:8180:1: warning: no newline at end of filesrc/device.c: note: in included file:src/device.h:244:61: warning: no newline at end of file
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:



---
Regards,
Linux Bluetooth


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

* Re: [PATCH bluez v3] bearer: Implement Connect/Disconnect methods
  2025-11-18  9:29 [PATCH bluez v3] bearer: Implement Connect/Disconnect methods Ye He via B4 Relay
  2025-11-18 10:23 ` [bluez,v3] " bluez.test.bot
@ 2025-11-18 14:14 ` Luiz Augusto von Dentz
  2025-11-19  3:26   ` Ye He
       [not found]   ` <09087c54-4d27-460e-a6cb-ac74833068b6@amlogic.com>
  1 sibling, 2 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2025-11-18 14:14 UTC (permalink / raw)
  To: ye.he; +Cc: Linux Bluetooth

Hi,

On Tue, Nov 18, 2025 at 4:32 AM Ye He via B4 Relay
<devnull+ye.he.amlogic.com@kernel.org> wrote:
>
> From: Ye He <ye.he@amlogic.com>
>
> This patch provides implementations for the Connect and Disconnect
> methods of the org.bluez.Bearer.LE1 and org.bluez.Bearer.BREDR1
> interfaces.

Ok, we will need some coverage in bluetoothctl in order to exercise this API.

> Signed-off-by: Ye He <ye.he@amlogic.com>
> ---
> Changes in v3:
> - Move the modification logic from the device to bearer, and add
> - some helper functions to the device.
> - Link to v2: https://patch.msgid.link/20251113-bearer-impl-v2-1-c3e825cc6758@amlogic.com
>
> Changes in v2:
> - Fix build error & warning.
> - Link to v1: https://patch.msgid.link/20251111-bearer-impl-v1-1-f41585144218@amlogic.com
> ---
>  src/bearer.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  src/device.c | 130 ++++++++++++++++++++++++++++++++++++++++--------
>  src/device.h |  17 +++++++
>  3 files changed, 285 insertions(+), 22 deletions(-)
>
> diff --git a/src/bearer.c b/src/bearer.c
> index 9723b59e2e7b39b2974e258363aa0ace95862651..46e272b16096740ce28bcc80d0e2426df8538a2e 100644
> --- a/src/bearer.c
> +++ b/src/bearer.c
> @@ -25,23 +25,38 @@
>
>  #include "bluetooth/bluetooth.h"
>  #include "bluetooth/mgmt.h"
> +#include "bluetooth/uuid.h"
>
>  #include "gdbus/gdbus.h"
>  #include "src/shared/util.h"
> +#include "src/shared/queue.h"
> +#include "src/shared/timeout.h"
>
>  #include "log.h"
>  #include "error.h"
>  #include "adapter.h"
>  #include "device.h"
> +#include "profile.h"
> +#include "service.h"
>  #include "dbus-common.h"
>  #include "bearer.h"
>
> +#define DISCONNECT_TIMER       2
> +
>  struct btd_bearer {
>         struct btd_device *device;
>         uint8_t type;
>         const char *path;
> +       unsigned int disconn_timer;
> +       struct queue *disconnects; /* disconnects message */
> +       DBusMessage *connect; /* connect message */

This doesn't sound quite right, why don't we queue connect messages?

>  };
>
> +static void bearer_free_dbus_message(void *data)
> +{
> +    dbus_message_unref((DBusMessage *)data);
> +}
> +
>  static void bearer_free(void *data)
>  {
>         struct btd_bearer *bearer = data;
> @@ -49,17 +64,126 @@ static void bearer_free(void *data)
>         free(bearer);
>  }
>
> +static void bearer_disconnect_service(struct btd_service *service, void *user_data)
> +{
> +       uint8_t bdaddr_type = *(uint8_t *)user_data;
> +       struct btd_profile *profile;
> +       bt_uuid_t uuid;
> +
> +       profile = btd_service_get_profile(service);
> +       if (!profile || !profile->remote_uuid)
> +               return;
> +
> +       if (bt_string_to_uuid(&uuid, profile->remote_uuid) != 0)
> +               return;
> +
> +       if (bdaddr_type == BDADDR_BREDR) {
> +               if (uuid.value.u16 < 0x1100 || uuid.value.u16 > 0x11FF)
> +                       return;
> +       } else {
> +               if (uuid.value.u16 < 0x1800 || uuid.value.u16 > 0x18FF)
> +                       return;
> +       }

What are these ranges above?

> +
> +       DBG("Disconnecting profile %s (UUID 0x%04x) for bearer addr type %u",
> +       profile->name ?: "(unknown)", uuid.value.u16, bdaddr_type);
> +
> +       btd_service_disconnect(service);
> +}
> +
> +static bool bearer_disconnect_link(gpointer user_data)
> +{
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +
> +       bearer->disconn_timer = 0;
> +
> +       if (btd_device_bdaddr_type_connected(device, bearer->type))
> +               btd_adapter_disconnect_device(device_get_adapter(device),
> +                                               device_get_address(device),
> +                                               bearer->type);
> +       return FALSE;
> +}
> +
>  static DBusMessage *bearer_connect(DBusConnection *conn, DBusMessage *msg,
>                                                         void *user_data)
>  {
> -       /* TODO */
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +       int err;
> +
> +       if (btd_device_bdaddr_type_connected(device, bearer->type)) {
> +               if (msg)
> +                       return btd_error_already_connected(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_bonding(device, NULL)) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_connecting(device) ||
> +               bearer->connect) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (msg)
> +               bearer->connect = dbus_message_ref(msg);
> +
> +       if (bearer->type == BDADDR_BREDR)
> +               return device_connect_profiles(device, BDADDR_BREDR,
> +                                                               msg, NULL);
> +       else {
> +               btd_device_set_temporary(device, false);
> +               err = device_connect_le(device);
> +               if (err < 0)
> +                       return btd_error_failed(msg, strerror(-err));
> +       }
> +
>         return NULL;
>  }
>
>  static DBusMessage *bearer_disconnect(DBusConnection *conn, DBusMessage *msg,
>                                                         void *user_data)
>  {
> -       /* TODO */
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +
> +       if (!btd_device_bdaddr_type_connected(device, bearer->type)) {
> +               if (msg)
> +                       return btd_error_not_connected(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_disconnecting(device)) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (msg)
> +               queue_push_tail(bearer->disconnects, dbus_message_ref(msg));

This also makes no sense, if you are going to queue the disconnect
message why are you sending in progress error if it is already
disconnecting?

> +       device_cancel_bonding(device, MGMT_STATUS_DISCONNECTED);
> +
> +       device_cancel_browse(device, bearer->type);
> +
> +       btd_device_foreach_service(device, bearer_disconnect_service,
> +                                                       &bearer->type);
> +
> +       device_remove_pending_services(device, bearer->type);
> +
> +       if (bearer->disconn_timer)
> +               return NULL;
> +
> +       bearer->disconn_timer = timeout_add_seconds(DISCONNECT_TIMER,
> +                                                       bearer_disconnect_link,
> +                                                       bearer, NULL);
> +
>         return NULL;
>  }
>
> @@ -151,6 +275,7 @@ struct btd_bearer *btd_bearer_new(struct btd_device *device, uint8_t type)
>         bearer->device = device;
>         bearer->type = type;
>         bearer->path = device_get_path(device);
> +       bearer->disconnects = queue_new();
>
>         if (!g_dbus_register_interface(btd_get_dbus_connection(),
>                                         bearer->path, bearer_interface(type),
> @@ -174,6 +299,16 @@ void btd_bearer_destroy(struct btd_bearer *bearer)
>                 return;
>         }
>
> +       if (bearer->disconnects) {
> +               queue_destroy(bearer->disconnects, bearer_free_dbus_message);
> +               bearer->disconnects = NULL;
> +       }
> +
> +       if (bearer->connect) {
> +               dbus_message_unref(bearer->connect);
> +               bearer->connect = NULL;
> +       }
> +
>         g_dbus_unregister_interface(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type));
>  }
> @@ -203,6 +338,13 @@ void btd_bearer_connected(struct btd_bearer *bearer)
>         if (!bearer || !bearer->path)
>                 return;
>
> +       if (bearer->connect) {
> +               g_dbus_send_reply(btd_get_dbus_connection(), bearer->connect,
> +                                               DBUS_TYPE_INVALID);
> +               dbus_message_unref(bearer->connect);
> +               bearer->connect = NULL;
> +       }
> +
>         g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type),
>                                         "Connected");
> @@ -212,10 +354,24 @@ void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason)
>  {
>         const char *name;
>         const char *message;
> +       DBusMessage *msg;
> +       const struct queue_entry *entry;
>
>         if (!bearer || !bearer->path)
>                 return;
>
> +       if (!btd_device_is_connected(bearer->device))
> +               device_disconnect_watches_callback(bearer->device);
> +
> +       while (!queue_isempty(bearer->disconnects)) {
> +               entry = queue_get_entries(bearer->disconnects);
> +               msg = entry->data;
> +               g_dbus_send_reply(btd_get_dbus_connection(), msg,
> +                                               DBUS_TYPE_INVALID);
> +               queue_remove(bearer->disconnects, msg);
> +               dbus_message_unref(msg);
> +       }
> +
>         g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type),
>                                         "Connected");
> diff --git a/src/device.c b/src/device.c
> index 91b6cc0c65eaae8058cd445c0942ffee57289f0d..918f1e51acd3a9cd6b776cc080c198885511fb20 100644
> --- a/src/device.c
> +++ b/src/device.c
> @@ -2022,6 +2022,28 @@ static void dev_disconn_service(gpointer a, gpointer b)
>         btd_service_disconnect(a);
>  }
>
> +void device_disconnect_watches_callback(struct btd_device *device)
> +{
> +       if (!device || !device->watches)
> +               return;
> +
> +       while (device->watches) {
> +               struct btd_disconnect_data *data = device->watches->data;
> +
> +               if (data->watch)
> +                       /* temporary is set if device is going to be removed */
> +                       data->watch(device, device->temporary,
> +                                                       data->user_data);
> +
> +               /* Check if the watch has been removed by callback function */
> +               if (!g_slist_find(device->watches, data))
> +                       continue;
> +
> +               device->watches = g_slist_remove(device->watches, data);
> +               g_free(data);
> +       }
> +}
> +
>  void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>  {
>         if (device->bonding)
> @@ -2063,21 +2085,7 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>         g_slist_free(device->pending);
>         device->pending = NULL;
>
> -       while (device->watches) {
> -               struct btd_disconnect_data *data = device->watches->data;
> -
> -               if (data->watch)
> -                       /* temporary is set if device is going to be removed */
> -                       data->watch(device, device->temporary,
> -                                                       data->user_data);
> -
> -               /* Check if the watch has been removed by callback function */
> -               if (!g_slist_find(device->watches, data))
> -                       continue;
> -
> -               device->watches = g_slist_remove(device->watches, data);
> -               g_free(data);
> -       }
> +       device_disconnect_watches_callback(device);
>
>         if (!btd_device_is_connected(device)) {
>                 if (msg)
> @@ -2095,6 +2103,11 @@ bool device_is_disconnecting(struct btd_device *device)
>         return device->disconn_timer > 0;
>  }
>
> +bool device_is_connecting(struct btd_device *device)
> +{
> +       return device->connect != NULL;
> +}
> +
>  static void add_set(void *data, void *user_data)
>  {
>         struct sirk_info *sirk = data;
> @@ -2712,8 +2725,8 @@ int btd_device_connect_services(struct btd_device *dev, GSList *services)
>         return connect_next(dev);
>  }
>
> -static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type,
> -                                       DBusMessage *msg, const char *uuid)
> +DBusMessage *device_connect_profiles(struct btd_device *dev,
> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid)
>  {
>         struct bearer_state *state = get_state(dev, bdaddr_type);
>         int err;
> @@ -2826,7 +2839,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
>                 return NULL;
>         }
>
> -       return connect_profiles(dev, bdaddr_type, msg, NULL);
> +       return device_connect_profiles(dev, bdaddr_type, msg, NULL);
>  }
>
>  static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
> @@ -2848,7 +2861,7 @@ static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
>                 return btd_error_invalid_args_str(msg,
>                                         ERR_BREDR_CONN_INVALID_ARGUMENTS);
>
> -       reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid);
> +       reply = device_connect_profiles(dev, BDADDR_BREDR, msg, uuid);
>         free(uuid);
>
>         return reply;
> @@ -3421,7 +3434,7 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
>         }
>  }
>
> -static void device_cancel_bonding(struct btd_device *device, uint8_t status)
> +void device_cancel_bonding(struct btd_device *device, uint8_t status)
>  {
>         struct bonding_req *bonding = device->bonding;
>         DBusMessage *reply;
> @@ -6629,6 +6642,38 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
>         return err;
>  }
>
> +static gboolean device_is_browsing(struct btd_device *device, uint8_t bdaddr_type)
> +{
> +       if (!device->browse)
> +               return FALSE;
> +
> +       if (bdaddr_type == BDADDR_BREDR && device->browse->type == BROWSE_SDP)
> +               return TRUE;
> +
> +       if (bdaddr_type != BDADDR_BREDR && device->browse->type == BROWSE_GATT)
> +               return TRUE;
> +
> +       return FALSE;
> +}
> +
> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type)
> +{
> +       struct btd_adapter *adapter = device->adapter;
> +
> +       DBG("");
> +
> +       if (!device_is_browsing(device, bdaddr_type))
> +               return;
> +
> +       if (bdaddr_type == BDADDR_BREDR)
> +               bt_cancel_discovery(btd_adapter_get_address(adapter),
> +                                                       &device->bdaddr);
> +       else
> +               attio_cleanup(device);
> +
> +       browse_request_free(device->browse);
> +}
> +
>  int device_discover_services(struct btd_device *device)
>  {
>         int err;
> @@ -8088,3 +8133,48 @@ void btd_device_foreach_service_data(struct btd_device *dev, bt_ad_func_t func,
>  {
>         bt_ad_foreach_service_data(dev->ad, func, data);
>  }
> +
> +
> +void btd_device_foreach_service(struct btd_device *dev,
> +                               btd_device_service_func_t func,
> +                               void *user_data)
> +{
> +       GSList *l;
> +
> +       for (l = dev->services; l; l = l->next)
> +               func(l->data, user_data);
> +}
> +
> +void device_remove_pending_services(struct btd_device *dev,
> +                                       uint8_t bdaddr_type)
> +{
> +       GSList *l = dev->pending;
> +       GSList *next;
> +       struct btd_service *service;
> +       struct btd_profile *profile;
> +       bt_uuid_t uuid;
> +       bool match = false;
> +
> +       while (l) {
> +               next = l->next;
> +               service = l->data;
> +
> +               profile = btd_service_get_profile(service);
> +               if (profile && profile->remote_uuid &&
> +                       bt_string_to_uuid(&uuid, profile->remote_uuid) == 0) {
> +
> +                       if (bdaddr_type == BDADDR_BREDR)
> +                               match = uuid.value.u16 >= 0x1100 ||
> +                                       uuid.value.u16 <= 0x11FF;
> +                       else
> +                               match = uuid.value.u16 >= 0x1800 ||
> +                                       uuid.value.u16 <= 0x18FF;
> +
> +                       if (match)
> +                               dev->pending = g_slist_remove(dev->pending,
> +                                                               service);
> +               }
> +
> +               l = next;
> +       }
> +}
> \ No newline at end of file
> diff --git a/src/device.h b/src/device.h
> index 6ed8affa0d4a9274d30cac9b48e8a6826edefd64..fee1ad50dc9c7ae248acd819dae07762c783ac35 100644
> --- a/src/device.h
> +++ b/src/device.h
> @@ -120,6 +120,7 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
>  gboolean device_is_bonding(struct btd_device *device, const char *sender);
>  void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
>  void device_bonding_failed(struct btd_device *device, uint8_t status);
> +void device_cancel_bonding(struct btd_device *device, uint8_t status);
>  struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device);
>  int device_bonding_attempt_retry(struct btd_device *device);
>  long device_bonding_last_duration(struct btd_device *device);
> @@ -134,6 +135,9 @@ int device_notify_pincode(struct btd_device *device, gboolean secure,
>                                                         const char *pincode);
>  void device_cancel_authentication(struct btd_device *device, gboolean aborted);
>  gboolean device_is_authenticating(struct btd_device *device);
> +
> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type);
> +
>  void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type,
>                                                         uint32_t flags);
>  void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
> @@ -141,6 +145,7 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
>                                                         uint8_t reason);
>  void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
>  bool device_is_disconnecting(struct btd_device *device);
> +bool device_is_connecting(struct btd_device *device);
>  void device_set_ltk(struct btd_device *device, const uint8_t val[16],
>                                 bool central, uint8_t enc_size);
>  bool btd_device_get_ltk(struct btd_device *device, uint8_t val[16],
> @@ -170,6 +175,7 @@ guint device_add_disconnect_watch(struct btd_device *device,
>                                 disconnect_watch watch, void *user_data,
>                                 GDestroyNotify destroy);
>  void device_remove_disconnect_watch(struct btd_device *device, guint id);
> +void device_disconnect_watches_callback(struct btd_device *device);
>  int device_get_appearance(struct btd_device *device, uint16_t *value);
>  void device_set_appearance(struct btd_device *device, uint16_t value);
>
> @@ -184,6 +190,9 @@ void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
>
>  int device_connect_le(struct btd_device *dev);
>
> +DBusMessage *device_connect_profiles(struct btd_device *dev,
> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid);
> +
>  typedef void (*device_svc_cb_t) (struct btd_device *dev, int err,
>                                                         void *user_data);
>
> @@ -225,3 +234,11 @@ void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval,
>  void btd_device_foreach_service_data(struct btd_device *dev,
>                                         bt_device_ad_func_t func,
>                                         void *data);
> +
> +typedef void (*btd_device_service_func_t)(struct btd_service *service,
> +                                       void *user_data);
> +void btd_device_foreach_service(struct btd_device *dev,
> +                               btd_device_service_func_t func,
> +                               void *user_data);
> +void device_remove_pending_services(struct btd_device *dev,
> +                                       uint8_t bdaddr_type);
> \ No newline at end of file
>
> ---
> base-commit: 5e41d1e1d361e7288964e4c2c5ed90736025662f
> change-id: 20251111-bearer-impl-5f7e396174aa
>
> Best regards,
> --
> Ye He <ye.he@amlogic.com>
>
>
>


-- 
Luiz Augusto von Dentz

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

* Re: [PATCH bluez v3] bearer: Implement Connect/Disconnect methods
  2025-11-18 14:14 ` [PATCH bluez v3] " Luiz Augusto von Dentz
@ 2025-11-19  3:26   ` Ye He
       [not found]   ` <09087c54-4d27-460e-a6cb-ac74833068b6@amlogic.com>
  1 sibling, 0 replies; 7+ messages in thread
From: Ye He @ 2025-11-19  3:26 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: Linux Bluetooth

Hi Luiz,

> Hi,
>
> On Tue, Nov 18, 2025 at 4:32 AM Ye He via B4 Relay
> <devnull+ye.he.amlogic.com@kernel.org> wrote:
>> From: Ye He <ye.he@amlogic.com>
>>
>> This patch provides implementations for the Connect and Disconnect
>> methods of the org.bluez.Bearer.LE1 and org.bluez.Bearer.BREDR1
>> interfaces.
> Ok, we will need some coverage in bluetoothctl in order to exercise this API.
OK, I will add the shell commands of bearer into bluetoothctl.
>> Signed-off-by: Ye He <ye.he@amlogic.com>
>> ---
>> Changes in v3:
>> - Move the modification logic from the device to bearer, and add
>> - some helper functions to the device.
>> - Link to v2: https://patch.msgid.link/20251113-bearer-impl-v2-1-c3e825cc6758@amlogic.com
>>
>> Changes in v2:
>> - Fix build error & warning.
>> - Link to v1: https://patch.msgid.link/20251111-bearer-impl-v1-1-f41585144218@amlogic.com
>> ---
>>   src/bearer.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>>   src/device.c | 130 ++++++++++++++++++++++++++++++++++++++++--------
>>   src/device.h |  17 +++++++
>>   3 files changed, 285 insertions(+), 22 deletions(-)
>>
>> diff --git a/src/bearer.c b/src/bearer.c
>> index 9723b59e2e7b39b2974e258363aa0ace95862651..46e272b16096740ce28bcc80d0e2426df8538a2e 100644
>> --- a/src/bearer.c
>> +++ b/src/bearer.c
>> @@ -25,23 +25,38 @@
>>
>>   #include "bluetooth/bluetooth.h"
>>   #include "bluetooth/mgmt.h"
>> +#include "bluetooth/uuid.h"
>>
>>   #include "gdbus/gdbus.h"
>>   #include "src/shared/util.h"
>> +#include "src/shared/queue.h"
>> +#include "src/shared/timeout.h"
>>
>>   #include "log.h"
>>   #include "error.h"
>>   #include "adapter.h"
>>   #include "device.h"
>> +#include "profile.h"
>> +#include "service.h"
>>   #include "dbus-common.h"
>>   #include "bearer.h"
>>
>> +#define DISCONNECT_TIMER       2
>> +
>>   struct btd_bearer {
>>          struct btd_device *device;
>>          uint8_t type;
>>          const char *path;
>> +       unsigned int disconn_timer;
>> +       struct queue *disconnects; /* disconnects message */
>> +       DBusMessage *connect; /* connect message */
> This doesn't sound quite right, why don't we queue connect messages?
Just like Connect() in org.bluez.Device1, the connect operation is defined
to allow only a single in-flight request. Queuing additional Connect
calls would change the existing API semantics and could lead to
unexpected state transitions—for example, duplicated browse attempts
or redundant link-level connection requests.

Disconnect() is different: queuing only affects how replies are
delivered, not the device’s actual behavior.
>>   };
>>
>> +static void bearer_free_dbus_message(void *data)
>> +{
>> +    dbus_message_unref((DBusMessage *)data);
>> +}
>> +
>>   static void bearer_free(void *data)
>>   {
>>          struct btd_bearer *bearer = data;
>> @@ -49,17 +64,126 @@ static void bearer_free(void *data)
>>          free(bearer);
>>   }
>>
>> +static void bearer_disconnect_service(struct btd_service *service, void *user_data)
>> +{
>> +       uint8_t bdaddr_type = *(uint8_t *)user_data;
>> +       struct btd_profile *profile;
>> +       bt_uuid_t uuid;
>> +
>> +       profile = btd_service_get_profile(service);
>> +       if (!profile || !profile->remote_uuid)
>> +               return;
>> +
>> +       if (bt_string_to_uuid(&uuid, profile->remote_uuid) != 0)
>> +               return;
>> +
>> +       if (bdaddr_type == BDADDR_BREDR) {
>> +               if (uuid.value.u16 < 0x1100 || uuid.value.u16 > 0x11FF)
>> +                       return;
>> +       } else {
>> +               if (uuid.value.u16 < 0x1800 || uuid.value.u16 > 0x18FF)
>> +                       return;
>> +       }
> What are these ranges above?
The UUID ranges are the SIG-assigned blocks for BREDR profiles
(0x1100–0x11FF) and LE GATT services (0x1800–0x18FF).
I originally used them to guess which bearer a service belongs to, but
this seems unreliable — some profiles exist on both BREDR and LE.
I’m open to better guidance on how to classify services/profiles
per bearer.
>> +
>> +       DBG("Disconnecting profile %s (UUID 0x%04x) for bearer addr type %u",
>> +       profile->name ?: "(unknown)", uuid.value.u16, bdaddr_type);
>> +
>> +       btd_service_disconnect(service);
>> +}
>> +
>> +static bool bearer_disconnect_link(gpointer user_data)
>> +{
>> +       struct btd_bearer *bearer = user_data;
>> +       struct btd_device *device = bearer->device;
>> +
>> +       bearer->disconn_timer = 0;
>> +
>> +       if (btd_device_bdaddr_type_connected(device, bearer->type))
>> +               btd_adapter_disconnect_device(device_get_adapter(device),
>> +                                               device_get_address(device),
>> +                                               bearer->type);
>> +       return FALSE;
>> +}
>> +
>>   static DBusMessage *bearer_connect(DBusConnection *conn, DBusMessage *msg,
>>                                                          void *user_data)
>>   {
>> -       /* TODO */
>> +       struct btd_bearer *bearer = user_data;
>> +       struct btd_device *device = bearer->device;
>> +       int err;
>> +
>> +       if (btd_device_bdaddr_type_connected(device, bearer->type)) {
>> +               if (msg)
>> +                       return btd_error_already_connected(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (device_is_bonding(device, NULL)) {
>> +               if (msg)
>> +                       return btd_error_in_progress(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (device_is_connecting(device) ||
>> +               bearer->connect) {
>> +               if (msg)
>> +                       return btd_error_in_progress(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (msg)
>> +               bearer->connect = dbus_message_ref(msg);
>> +
>> +       if (bearer->type == BDADDR_BREDR)
>> +               return device_connect_profiles(device, BDADDR_BREDR,
>> +                                                               msg, NULL);
>> +       else {
>> +               btd_device_set_temporary(device, false);
>> +               err = device_connect_le(device);
>> +               if (err < 0)
>> +                       return btd_error_failed(msg, strerror(-err));
>> +       }
>> +
>>          return NULL;
>>   }
>>
>>   static DBusMessage *bearer_disconnect(DBusConnection *conn, DBusMessage *msg,
>>                                                          void *user_data)
>>   {
>> -       /* TODO */
>> +       struct btd_bearer *bearer = user_data;
>> +       struct btd_device *device = bearer->device;
>> +
>> +       if (!btd_device_bdaddr_type_connected(device, bearer->type)) {
>> +               if (msg)
>> +                       return btd_error_not_connected(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (device_is_disconnecting(device)) {
>> +               if (msg)
>> +                       return btd_error_in_progress(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (msg)
>> +               queue_push_tail(bearer->disconnects, dbus_message_ref(msg));
> This also makes no sense, if you are going to queue the disconnect
> message why are you sending in progress error if it is already
> disconnecting?
device_is_disconnecting(device) indicates that the 
org.bluez.Device1.Disconnect()
operation is in progress. This call has the higher priority because
it tears down both LE and BR/EDR bearers. Any bearer-level disconnect
requests should therefore be dropped once this state is active.
>> +       device_cancel_bonding(device, MGMT_STATUS_DISCONNECTED);
>> +
>> +       device_cancel_browse(device, bearer->type);
>> +
>> +       btd_device_foreach_service(device, bearer_disconnect_service,
>> +                                                       &bearer->type);
>> +
>> +       device_remove_pending_services(device, bearer->type);
>> +
>> +       if (bearer->disconn_timer)
>> +               return NULL;
>> +
>> +       bearer->disconn_timer = timeout_add_seconds(DISCONNECT_TIMER,
>> +                                                       bearer_disconnect_link,
>> +                                                       bearer, NULL);
>> +
>>          return NULL;
>>   }
>>
>> @@ -151,6 +275,7 @@ struct btd_bearer *btd_bearer_new(struct btd_device *device, uint8_t type)
>>          bearer->device = device;
>>          bearer->type = type;
>>          bearer->path = device_get_path(device);
>> +       bearer->disconnects = queue_new();
>>
>>          if (!g_dbus_register_interface(btd_get_dbus_connection(),
>>                                          bearer->path, bearer_interface(type),
>> @@ -174,6 +299,16 @@ void btd_bearer_destroy(struct btd_bearer *bearer)
>>                  return;
>>          }
>>
>> +       if (bearer->disconnects) {
>> +               queue_destroy(bearer->disconnects, bearer_free_dbus_message);
>> +               bearer->disconnects = NULL;
>> +       }
>> +
>> +       if (bearer->connect) {
>> +               dbus_message_unref(bearer->connect);
>> +               bearer->connect = NULL;
>> +       }
>> +
>>          g_dbus_unregister_interface(btd_get_dbus_connection(), bearer->path,
>>                                          bearer_interface(bearer->type));
>>   }
>> @@ -203,6 +338,13 @@ void btd_bearer_connected(struct btd_bearer *bearer)
>>          if (!bearer || !bearer->path)
>>                  return;
>>
>> +       if (bearer->connect) {
>> +               g_dbus_send_reply(btd_get_dbus_connection(), bearer->connect,
>> +                                               DBUS_TYPE_INVALID);
>> +               dbus_message_unref(bearer->connect);
>> +               bearer->connect = NULL;
>> +       }
>> +
>>          g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>>                                          bearer_interface(bearer->type),
>>                                          "Connected");
>> @@ -212,10 +354,24 @@ void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason)
>>   {
>>          const char *name;
>>          const char *message;
>> +       DBusMessage *msg;
>> +       const struct queue_entry *entry;
>>
>>          if (!bearer || !bearer->path)
>>                  return;
>>
>> +       if (!btd_device_is_connected(bearer->device))
>> +               device_disconnect_watches_callback(bearer->device);
>> +
>> +       while (!queue_isempty(bearer->disconnects)) {
>> +               entry = queue_get_entries(bearer->disconnects);
>> +               msg = entry->data;
>> +               g_dbus_send_reply(btd_get_dbus_connection(), msg,
>> +                                               DBUS_TYPE_INVALID);
>> +               queue_remove(bearer->disconnects, msg);
>> +               dbus_message_unref(msg);
>> +       }
>> +
>>          g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>>                                          bearer_interface(bearer->type),
>>                                          "Connected");
>> diff --git a/src/device.c b/src/device.c
>> index 91b6cc0c65eaae8058cd445c0942ffee57289f0d..918f1e51acd3a9cd6b776cc080c198885511fb20 100644
>> --- a/src/device.c
>> +++ b/src/device.c
>> @@ -2022,6 +2022,28 @@ static void dev_disconn_service(gpointer a, gpointer b)
>>          btd_service_disconnect(a);
>>   }
>>
>> +void device_disconnect_watches_callback(struct btd_device *device)
>> +{
>> +       if (!device || !device->watches)
>> +               return;
>> +
>> +       while (device->watches) {
>> +               struct btd_disconnect_data *data = device->watches->data;
>> +
>> +               if (data->watch)
>> +                       /* temporary is set if device is going to be removed */
>> +                       data->watch(device, device->temporary,
>> +                                                       data->user_data);
>> +
>> +               /* Check if the watch has been removed by callback function */
>> +               if (!g_slist_find(device->watches, data))
>> +                       continue;
>> +
>> +               device->watches = g_slist_remove(device->watches, data);
>> +               g_free(data);
>> +       }
>> +}
>> +
>>   void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>>   {
>>          if (device->bonding)
>> @@ -2063,21 +2085,7 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>>          g_slist_free(device->pending);
>>          device->pending = NULL;
>>
>> -       while (device->watches) {
>> -               struct btd_disconnect_data *data = device->watches->data;
>> -
>> -               if (data->watch)
>> -                       /* temporary is set if device is going to be removed */
>> -                       data->watch(device, device->temporary,
>> -                                                       data->user_data);
>> -
>> -               /* Check if the watch has been removed by callback function */
>> -               if (!g_slist_find(device->watches, data))
>> -                       continue;
>> -
>> -               device->watches = g_slist_remove(device->watches, data);
>> -               g_free(data);
>> -       }
>> +       device_disconnect_watches_callback(device);
>>
>>          if (!btd_device_is_connected(device)) {
>>                  if (msg)
>> @@ -2095,6 +2103,11 @@ bool device_is_disconnecting(struct btd_device *device)
>>          return device->disconn_timer > 0;
>>   }
>>
>> +bool device_is_connecting(struct btd_device *device)
>> +{
>> +       return device->connect != NULL;
>> +}
>> +
>>   static void add_set(void *data, void *user_data)
>>   {
>>          struct sirk_info *sirk = data;
>> @@ -2712,8 +2725,8 @@ int btd_device_connect_services(struct btd_device *dev, GSList *services)
>>          return connect_next(dev);
>>   }
>>
>> -static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type,
>> -                                       DBusMessage *msg, const char *uuid)
>> +DBusMessage *device_connect_profiles(struct btd_device *dev,
>> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid)
>>   {
>>          struct bearer_state *state = get_state(dev, bdaddr_type);
>>          int err;
>> @@ -2826,7 +2839,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
>>                  return NULL;
>>          }
>>
>> -       return connect_profiles(dev, bdaddr_type, msg, NULL);
>> +       return device_connect_profiles(dev, bdaddr_type, msg, NULL);
>>   }
>>
>>   static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
>> @@ -2848,7 +2861,7 @@ static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
>>                  return btd_error_invalid_args_str(msg,
>>                                          ERR_BREDR_CONN_INVALID_ARGUMENTS);
>>
>> -       reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid);
>> +       reply = device_connect_profiles(dev, BDADDR_BREDR, msg, uuid);
>>          free(uuid);
>>
>>          return reply;
>> @@ -3421,7 +3434,7 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
>>          }
>>   }
>>
>> -static void device_cancel_bonding(struct btd_device *device, uint8_t status)
>> +void device_cancel_bonding(struct btd_device *device, uint8_t status)
>>   {
>>          struct bonding_req *bonding = device->bonding;
>>          DBusMessage *reply;
>> @@ -6629,6 +6642,38 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
>>          return err;
>>   }
>>
>> +static gboolean device_is_browsing(struct btd_device *device, uint8_t bdaddr_type)
>> +{
>> +       if (!device->browse)
>> +               return FALSE;
>> +
>> +       if (bdaddr_type == BDADDR_BREDR && device->browse->type == BROWSE_SDP)
>> +               return TRUE;
>> +
>> +       if (bdaddr_type != BDADDR_BREDR && device->browse->type == BROWSE_GATT)
>> +               return TRUE;
>> +
>> +       return FALSE;
>> +}
>> +
>> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type)
>> +{
>> +       struct btd_adapter *adapter = device->adapter;
>> +
>> +       DBG("");
>> +
>> +       if (!device_is_browsing(device, bdaddr_type))
>> +               return;
>> +
>> +       if (bdaddr_type == BDADDR_BREDR)
>> +               bt_cancel_discovery(btd_adapter_get_address(adapter),
>> +                                                       &device->bdaddr);
>> +       else
>> +               attio_cleanup(device);
>> +
>> +       browse_request_free(device->browse);
>> +}
>> +
>>   int device_discover_services(struct btd_device *device)
>>   {
>>          int err;
>> @@ -8088,3 +8133,48 @@ void btd_device_foreach_service_data(struct btd_device *dev, bt_ad_func_t func,
>>   {
>>          bt_ad_foreach_service_data(dev->ad, func, data);
>>   }
>> +
>> +
>> +void btd_device_foreach_service(struct btd_device *dev,
>> +                               btd_device_service_func_t func,
>> +                               void *user_data)
>> +{
>> +       GSList *l;
>> +
>> +       for (l = dev->services; l; l = l->next)
>> +               func(l->data, user_data);
>> +}
>> +
>> +void device_remove_pending_services(struct btd_device *dev,
>> +                                       uint8_t bdaddr_type)
>> +{
>> +       GSList *l = dev->pending;
>> +       GSList *next;
>> +       struct btd_service *service;
>> +       struct btd_profile *profile;
>> +       bt_uuid_t uuid;
>> +       bool match = false;
>> +
>> +       while (l) {
>> +               next = l->next;
>> +               service = l->data;
>> +
>> +               profile = btd_service_get_profile(service);
>> +               if (profile && profile->remote_uuid &&
>> +                       bt_string_to_uuid(&uuid, profile->remote_uuid) == 0) {
>> +
>> +                       if (bdaddr_type == BDADDR_BREDR)
>> +                               match = uuid.value.u16 >= 0x1100 ||
>> +                                       uuid.value.u16 <= 0x11FF;
>> +                       else
>> +                               match = uuid.value.u16 >= 0x1800 ||
>> +                                       uuid.value.u16 <= 0x18FF;
>> +
>> +                       if (match)
>> +                               dev->pending = g_slist_remove(dev->pending,
>> +                                                               service);
>> +               }
>> +
>> +               l = next;
>> +       }
>> +}
>> \ No newline at end of file
>> diff --git a/src/device.h b/src/device.h
>> index 6ed8affa0d4a9274d30cac9b48e8a6826edefd64..fee1ad50dc9c7ae248acd819dae07762c783ac35 100644
>> --- a/src/device.h
>> +++ b/src/device.h
>> @@ -120,6 +120,7 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
>>   gboolean device_is_bonding(struct btd_device *device, const char *sender);
>>   void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
>>   void device_bonding_failed(struct btd_device *device, uint8_t status);
>> +void device_cancel_bonding(struct btd_device *device, uint8_t status);
>>   struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device);
>>   int device_bonding_attempt_retry(struct btd_device *device);
>>   long device_bonding_last_duration(struct btd_device *device);
>> @@ -134,6 +135,9 @@ int device_notify_pincode(struct btd_device *device, gboolean secure,
>>                                                          const char *pincode);
>>   void device_cancel_authentication(struct btd_device *device, gboolean aborted);
>>   gboolean device_is_authenticating(struct btd_device *device);
>> +
>> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type);
>> +
>>   void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type,
>>                                                          uint32_t flags);
>>   void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
>> @@ -141,6 +145,7 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
>>                                                          uint8_t reason);
>>   void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
>>   bool device_is_disconnecting(struct btd_device *device);
>> +bool device_is_connecting(struct btd_device *device);
>>   void device_set_ltk(struct btd_device *device, const uint8_t val[16],
>>                                  bool central, uint8_t enc_size);
>>   bool btd_device_get_ltk(struct btd_device *device, uint8_t val[16],
>> @@ -170,6 +175,7 @@ guint device_add_disconnect_watch(struct btd_device *device,
>>                                  disconnect_watch watch, void *user_data,
>>                                  GDestroyNotify destroy);
>>   void device_remove_disconnect_watch(struct btd_device *device, guint id);
>> +void device_disconnect_watches_callback(struct btd_device *device);
>>   int device_get_appearance(struct btd_device *device, uint16_t *value);
>>   void device_set_appearance(struct btd_device *device, uint16_t value);
>>
>> @@ -184,6 +190,9 @@ void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
>>
>>   int device_connect_le(struct btd_device *dev);
>>
>> +DBusMessage *device_connect_profiles(struct btd_device *dev,
>> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid);
>> +
>>   typedef void (*device_svc_cb_t) (struct btd_device *dev, int err,
>>                                                          void *user_data);
>>
>> @@ -225,3 +234,11 @@ void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval,
>>   void btd_device_foreach_service_data(struct btd_device *dev,
>>                                          bt_device_ad_func_t func,
>>                                          void *data);
>> +
>> +typedef void (*btd_device_service_func_t)(struct btd_service *service,
>> +                                       void *user_data);
>> +void btd_device_foreach_service(struct btd_device *dev,
>> +                               btd_device_service_func_t func,
>> +                               void *user_data);
>> +void device_remove_pending_services(struct btd_device *dev,
>> +                                       uint8_t bdaddr_type);
>> \ No newline at end of file
>>
>> ---
>> base-commit: 5e41d1e1d361e7288964e4c2c5ed90736025662f
>> change-id: 20251111-bearer-impl-5f7e396174aa
>>
>> Best regards,
>> --
>> Ye He <ye.he@amlogic.com>
>>
>>
>>
>
> --
> Luiz Augusto von Dentz

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

* Re: [PATCH bluez v3] bearer: Implement Connect/Disconnect methods
       [not found]   ` <09087c54-4d27-460e-a6cb-ac74833068b6@amlogic.com>
@ 2025-11-20 16:22     ` Luiz Augusto von Dentz
  2025-11-21  1:59       ` Ye He
  2025-11-20 16:24     ` Luiz Augusto von Dentz
  1 sibling, 1 reply; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2025-11-20 16:22 UTC (permalink / raw)
  To: Ye He; +Cc: Linux Bluetooth

Hi,

On Tue, Nov 18, 2025 at 9:40 PM Ye He <ye.he@amlogic.com> wrote:
>
> Hi Luiz,
>
> Hi,
>
> On Tue, Nov 18, 2025 at 4:32 AM Ye He via B4 Relay
> <devnull+ye.he.amlogic.com@kernel.org> wrote:
>
> From: Ye He <ye.he@amlogic.com>
>
> This patch provides implementations for the Connect and Disconnect
> methods of the org.bluez.Bearer.LE1 and org.bluez.Bearer.BREDR1
> interfaces.
>
> Ok, we will need some coverage in bluetoothctl in order to exercise this API.
>
> OK, I will add the shell commands of bearer into bluetoothctl.
>
> Signed-off-by: Ye He <ye.he@amlogic.com>
> ---
> Changes in v3:
> - Move the modification logic from the device to bearer, and add
> - some helper functions to the device.
> - Link to v2: https://patch.msgid.link/20251113-bearer-impl-v2-1-c3e825cc6758@amlogic.com
>
> Changes in v2:
> - Fix build error & warning.
> - Link to v1: https://patch.msgid.link/20251111-bearer-impl-v1-1-f41585144218@amlogic.com
> ---
>  src/bearer.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  src/device.c | 130 ++++++++++++++++++++++++++++++++++++++++--------
>  src/device.h |  17 +++++++
>  3 files changed, 285 insertions(+), 22 deletions(-)
>
> diff --git a/src/bearer.c b/src/bearer.c
> index 9723b59e2e7b39b2974e258363aa0ace95862651..46e272b16096740ce28bcc80d0e2426df8538a2e 100644
> --- a/src/bearer.c
> +++ b/src/bearer.c
> @@ -25,23 +25,38 @@
>
>  #include "bluetooth/bluetooth.h"
>  #include "bluetooth/mgmt.h"
> +#include "bluetooth/uuid.h"
>
>  #include "gdbus/gdbus.h"
>  #include "src/shared/util.h"
> +#include "src/shared/queue.h"
> +#include "src/shared/timeout.h"
>
>  #include "log.h"
>  #include "error.h"
>  #include "adapter.h"
>  #include "device.h"
> +#include "profile.h"
> +#include "service.h"
>  #include "dbus-common.h"
>  #include "bearer.h"
>
> +#define DISCONNECT_TIMER       2
> +
>  struct btd_bearer {
>         struct btd_device *device;
>         uint8_t type;
>         const char *path;
> +       unsigned int disconn_timer;
> +       struct queue *disconnects; /* disconnects message */
> +       DBusMessage *connect; /* connect message */
>
> This doesn't sound quite right, why don't we queue connect messages?
>
> Just like Connect() in org.bluez.Device1, the connect operation is defined
> to allow only a single in-flight request. Queuing additional Connect
> calls would change the existing API semantics and could lead to
> unexpected state transitions—for example, duplicated browse attempts
> or redundant link-level connection requests.

I think that was done because there is the likes of ConnectProfile,
which is not the case here, anyway I'd probably add a comment that
this is done in order to keep API semantics from Device.Connect.

> Disconnect() is different: queuing only affects how replies are
> delivered, not the device’s actual behavior.
>
>  };
>
> +static void bearer_free_dbus_message(void *data)
> +{
> +    dbus_message_unref((DBusMessage *)data);
> +}
> +
>  static void bearer_free(void *data)
>  {
>         struct btd_bearer *bearer = data;
> @@ -49,17 +64,126 @@ static void bearer_free(void *data)
>         free(bearer);
>  }
>
> +static void bearer_disconnect_service(struct btd_service *service, void *user_data)
> +{
> +       uint8_t bdaddr_type = *(uint8_t *)user_data;
> +       struct btd_profile *profile;
> +       bt_uuid_t uuid;
> +
> +       profile = btd_service_get_profile(service);
> +       if (!profile || !profile->remote_uuid)
> +               return;
> +
> +       if (bt_string_to_uuid(&uuid, profile->remote_uuid) != 0)
> +               return;
> +
> +       if (bdaddr_type == BDADDR_BREDR) {
> +               if (uuid.value.u16 < 0x1100 || uuid.value.u16 > 0x11FF)
> +                       return;
> +       } else {
> +               if (uuid.value.u16 < 0x1800 || uuid.value.u16 > 0x18FF)
> +                       return;
> +       }
>
> What are these ranges above?
>
> The UUID ranges are the SIG-assigned blocks for BR/EDR profiles
> (0x1100–0x11FF) and GATT services (0x1800–0x18FF).
> I originally used them to guess which bearer a service belongs to, but
> this seems unreliable — some profiles exist on both BR/EDR and LE.
> I’m open to better guidance on how to classify services/profiles
> per bearer.

If we really need to distinct the services per bearer Id add a bearer
field to btd_profile, because as you say above there are profiles that
can exist in both BR/EDR and LE, anyway if you look at util.c where
there is the table of UUIDs it seems that the service UUID can extend
from 0x1800 to 0x2800, but there could also be vendor ones which are
on 0xfxxx range, meaning there could be multiple ranges we would need
to check so I don't think this would be reliable in the end.

> +
> +       DBG("Disconnecting profile %s (UUID 0x%04x) for bearer addr type %u",
> +       profile->name ?: "(unknown)", uuid.value.u16, bdaddr_type);
> +
> +       btd_service_disconnect(service);
> +}
> +
> +static bool bearer_disconnect_link(gpointer user_data)
> +{
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +
> +       bearer->disconn_timer = 0;
> +
> +       if (btd_device_bdaddr_type_connected(device, bearer->type))
> +               btd_adapter_disconnect_device(device_get_adapter(device),
> +                                               device_get_address(device),
> +                                               bearer->type);
> +       return FALSE;
> +}
> +
>  static DBusMessage *bearer_connect(DBusConnection *conn, DBusMessage *msg,
>                                                         void *user_data)
>  {
> -       /* TODO */
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +       int err;
> +
> +       if (btd_device_bdaddr_type_connected(device, bearer->type)) {
> +               if (msg)
> +                       return btd_error_already_connected(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_bonding(device, NULL)) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_connecting(device) ||
> +               bearer->connect) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (msg)
> +               bearer->connect = dbus_message_ref(msg);
> +
> +       if (bearer->type == BDADDR_BREDR)
> +               return device_connect_profiles(device, BDADDR_BREDR,
> +                                                               msg, NULL);
> +       else {
> +               btd_device_set_temporary(device, false);
> +               err = device_connect_le(device);
> +               if (err < 0)
> +                       return btd_error_failed(msg, strerror(-err));
> +       }
> +
>         return NULL;
>  }
>
>  static DBusMessage *bearer_disconnect(DBusConnection *conn, DBusMessage *msg,
>                                                         void *user_data)
>  {
> -       /* TODO */
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +
> +       if (!btd_device_bdaddr_type_connected(device, bearer->type)) {
> +               if (msg)
> +                       return btd_error_not_connected(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_disconnecting(device)) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (msg)
> +               queue_push_tail(bearer->disconnects, dbus_message_ref(msg));
>
> This also makes no sense, if you are going to queue the disconnect
> message why are you sending in progress error if it is already
> disconnecting?
>
> device_is_disconnecting(device) indicates that the org.bluez.Device1.Disconnect()
> operation is in progress. This call has the higher priority because
> it tears down both LE and BR/EDR bearers. Any bearer-level disconnect
> requests should therefore be dropped once this state is active.
>
> +       device_cancel_bonding(device, MGMT_STATUS_DISCONNECTED);
> +
> +       device_cancel_browse(device, bearer->type);
> +
> +       btd_device_foreach_service(device, bearer_disconnect_service,
> +                                                       &bearer->type);
> +
> +       device_remove_pending_services(device, bearer->type);
> +
> +       if (bearer->disconn_timer)
> +               return NULL;
> +
> +       bearer->disconn_timer = timeout_add_seconds(DISCONNECT_TIMER,
> +                                                       bearer_disconnect_link,
> +                                                       bearer, NULL);
> +
>         return NULL;
>  }
>
> @@ -151,6 +275,7 @@ struct btd_bearer *btd_bearer_new(struct btd_device *device, uint8_t type)
>         bearer->device = device;
>         bearer->type = type;
>         bearer->path = device_get_path(device);
> +       bearer->disconnects = queue_new();
>
>         if (!g_dbus_register_interface(btd_get_dbus_connection(),
>                                         bearer->path, bearer_interface(type),
> @@ -174,6 +299,16 @@ void btd_bearer_destroy(struct btd_bearer *bearer)
>                 return;
>         }
>
> +       if (bearer->disconnects) {
> +               queue_destroy(bearer->disconnects, bearer_free_dbus_message);
> +               bearer->disconnects = NULL;
> +       }
> +
> +       if (bearer->connect) {
> +               dbus_message_unref(bearer->connect);
> +               bearer->connect = NULL;
> +       }
> +
>         g_dbus_unregister_interface(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type));
>  }
> @@ -203,6 +338,13 @@ void btd_bearer_connected(struct btd_bearer *bearer)
>         if (!bearer || !bearer->path)
>                 return;
>
> +       if (bearer->connect) {
> +               g_dbus_send_reply(btd_get_dbus_connection(), bearer->connect,
> +                                               DBUS_TYPE_INVALID);
> +               dbus_message_unref(bearer->connect);
> +               bearer->connect = NULL;
> +       }
> +
>         g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type),
>                                         "Connected");
> @@ -212,10 +354,24 @@ void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason)
>  {
>         const char *name;
>         const char *message;
> +       DBusMessage *msg;
> +       const struct queue_entry *entry;
>
>         if (!bearer || !bearer->path)
>                 return;
>
> +       if (!btd_device_is_connected(bearer->device))
> +               device_disconnect_watches_callback(bearer->device);
> +
> +       while (!queue_isempty(bearer->disconnects)) {
> +               entry = queue_get_entries(bearer->disconnects);
> +               msg = entry->data;
> +               g_dbus_send_reply(btd_get_dbus_connection(), msg,
> +                                               DBUS_TYPE_INVALID);
> +               queue_remove(bearer->disconnects, msg);
> +               dbus_message_unref(msg);
> +       }
> +
>         g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type),
>                                         "Connected");
> diff --git a/src/device.c b/src/device.c
> index 91b6cc0c65eaae8058cd445c0942ffee57289f0d..918f1e51acd3a9cd6b776cc080c198885511fb20 100644
> --- a/src/device.c
> +++ b/src/device.c
> @@ -2022,6 +2022,28 @@ static void dev_disconn_service(gpointer a, gpointer b)
>         btd_service_disconnect(a);
>  }
>
> +void device_disconnect_watches_callback(struct btd_device *device)
> +{
> +       if (!device || !device->watches)
> +               return;
> +
> +       while (device->watches) {
> +               struct btd_disconnect_data *data = device->watches->data;
> +
> +               if (data->watch)
> +                       /* temporary is set if device is going to be removed */
> +                       data->watch(device, device->temporary,
> +                                                       data->user_data);
> +
> +               /* Check if the watch has been removed by callback function */
> +               if (!g_slist_find(device->watches, data))
> +                       continue;
> +
> +               device->watches = g_slist_remove(device->watches, data);
> +               g_free(data);
> +       }
> +}
> +
>  void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>  {
>         if (device->bonding)
> @@ -2063,21 +2085,7 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>         g_slist_free(device->pending);
>         device->pending = NULL;
>
> -       while (device->watches) {
> -               struct btd_disconnect_data *data = device->watches->data;
> -
> -               if (data->watch)
> -                       /* temporary is set if device is going to be removed */
> -                       data->watch(device, device->temporary,
> -                                                       data->user_data);
> -
> -               /* Check if the watch has been removed by callback function */
> -               if (!g_slist_find(device->watches, data))
> -                       continue;
> -
> -               device->watches = g_slist_remove(device->watches, data);
> -               g_free(data);
> -       }
> +       device_disconnect_watches_callback(device);
>
>         if (!btd_device_is_connected(device)) {
>                 if (msg)
> @@ -2095,6 +2103,11 @@ bool device_is_disconnecting(struct btd_device *device)
>         return device->disconn_timer > 0;
>  }
>
> +bool device_is_connecting(struct btd_device *device)
> +{
> +       return device->connect != NULL;
> +}
> +
>  static void add_set(void *data, void *user_data)
>  {
>         struct sirk_info *sirk = data;
> @@ -2712,8 +2725,8 @@ int btd_device_connect_services(struct btd_device *dev, GSList *services)
>         return connect_next(dev);
>  }
>
> -static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type,
> -                                       DBusMessage *msg, const char *uuid)
> +DBusMessage *device_connect_profiles(struct btd_device *dev,
> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid)
>  {
>         struct bearer_state *state = get_state(dev, bdaddr_type);
>         int err;
> @@ -2826,7 +2839,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
>                 return NULL;
>         }
>
> -       return connect_profiles(dev, bdaddr_type, msg, NULL);
> +       return device_connect_profiles(dev, bdaddr_type, msg, NULL);
>  }
>
>  static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
> @@ -2848,7 +2861,7 @@ static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
>                 return btd_error_invalid_args_str(msg,
>                                         ERR_BREDR_CONN_INVALID_ARGUMENTS);
>
> -       reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid);
> +       reply = device_connect_profiles(dev, BDADDR_BREDR, msg, uuid);
>         free(uuid);
>
>         return reply;
> @@ -3421,7 +3434,7 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
>         }
>  }
>
> -static void device_cancel_bonding(struct btd_device *device, uint8_t status)
> +void device_cancel_bonding(struct btd_device *device, uint8_t status)
>  {
>         struct bonding_req *bonding = device->bonding;
>         DBusMessage *reply;
> @@ -6629,6 +6642,38 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
>         return err;
>  }
>
> +static gboolean device_is_browsing(struct btd_device *device, uint8_t bdaddr_type)
> +{
> +       if (!device->browse)
> +               return FALSE;
> +
> +       if (bdaddr_type == BDADDR_BREDR && device->browse->type == BROWSE_SDP)
> +               return TRUE;
> +
> +       if (bdaddr_type != BDADDR_BREDR && device->browse->type == BROWSE_GATT)
> +               return TRUE;
> +
> +       return FALSE;
> +}
> +
> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type)
> +{
> +       struct btd_adapter *adapter = device->adapter;
> +
> +       DBG("");
> +
> +       if (!device_is_browsing(device, bdaddr_type))
> +               return;
> +
> +       if (bdaddr_type == BDADDR_BREDR)
> +               bt_cancel_discovery(btd_adapter_get_address(adapter),
> +                                                       &device->bdaddr);
> +       else
> +               attio_cleanup(device);
> +
> +       browse_request_free(device->browse);
> +}
> +
>  int device_discover_services(struct btd_device *device)
>  {
>         int err;
> @@ -8088,3 +8133,48 @@ void btd_device_foreach_service_data(struct btd_device *dev, bt_ad_func_t func,
>  {
>         bt_ad_foreach_service_data(dev->ad, func, data);
>  }
> +
> +
> +void btd_device_foreach_service(struct btd_device *dev,
> +                               btd_device_service_func_t func,
> +                               void *user_data)
> +{
> +       GSList *l;
> +
> +       for (l = dev->services; l; l = l->next)
> +               func(l->data, user_data);
> +}
> +
> +void device_remove_pending_services(struct btd_device *dev,
> +                                       uint8_t bdaddr_type)
> +{
> +       GSList *l = dev->pending;
> +       GSList *next;
> +       struct btd_service *service;
> +       struct btd_profile *profile;
> +       bt_uuid_t uuid;
> +       bool match = false;
> +
> +       while (l) {
> +               next = l->next;
> +               service = l->data;
> +
> +               profile = btd_service_get_profile(service);
> +               if (profile && profile->remote_uuid &&
> +                       bt_string_to_uuid(&uuid, profile->remote_uuid) == 0) {
> +
> +                       if (bdaddr_type == BDADDR_BREDR)
> +                               match = uuid.value.u16 >= 0x1100 ||
> +                                       uuid.value.u16 <= 0x11FF;
> +                       else
> +                               match = uuid.value.u16 >= 0x1800 ||
> +                                       uuid.value.u16 <= 0x18FF;
> +
> +                       if (match)
> +                               dev->pending = g_slist_remove(dev->pending,
> +                                                               service);
> +               }
> +
> +               l = next;
> +       }
> +}
> \ No newline at end of file
> diff --git a/src/device.h b/src/device.h
> index 6ed8affa0d4a9274d30cac9b48e8a6826edefd64..fee1ad50dc9c7ae248acd819dae07762c783ac35 100644
> --- a/src/device.h
> +++ b/src/device.h
> @@ -120,6 +120,7 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
>  gboolean device_is_bonding(struct btd_device *device, const char *sender);
>  void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
>  void device_bonding_failed(struct btd_device *device, uint8_t status);
> +void device_cancel_bonding(struct btd_device *device, uint8_t status);
>  struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device);
>  int device_bonding_attempt_retry(struct btd_device *device);
>  long device_bonding_last_duration(struct btd_device *device);
> @@ -134,6 +135,9 @@ int device_notify_pincode(struct btd_device *device, gboolean secure,
>                                                         const char *pincode);
>  void device_cancel_authentication(struct btd_device *device, gboolean aborted);
>  gboolean device_is_authenticating(struct btd_device *device);
> +
> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type);
> +
>  void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type,
>                                                         uint32_t flags);
>  void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
> @@ -141,6 +145,7 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
>                                                         uint8_t reason);
>  void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
>  bool device_is_disconnecting(struct btd_device *device);
> +bool device_is_connecting(struct btd_device *device);
>  void device_set_ltk(struct btd_device *device, const uint8_t val[16],
>                                 bool central, uint8_t enc_size);
>  bool btd_device_get_ltk(struct btd_device *device, uint8_t val[16],
> @@ -170,6 +175,7 @@ guint device_add_disconnect_watch(struct btd_device *device,
>                                 disconnect_watch watch, void *user_data,
>                                 GDestroyNotify destroy);
>  void device_remove_disconnect_watch(struct btd_device *device, guint id);
> +void device_disconnect_watches_callback(struct btd_device *device);
>  int device_get_appearance(struct btd_device *device, uint16_t *value);
>  void device_set_appearance(struct btd_device *device, uint16_t value);
>
> @@ -184,6 +190,9 @@ void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
>
>  int device_connect_le(struct btd_device *dev);
>
> +DBusMessage *device_connect_profiles(struct btd_device *dev,
> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid);
> +
>  typedef void (*device_svc_cb_t) (struct btd_device *dev, int err,
>                                                         void *user_data);
>
> @@ -225,3 +234,11 @@ void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval,
>  void btd_device_foreach_service_data(struct btd_device *dev,
>                                         bt_device_ad_func_t func,
>                                         void *data);
> +
> +typedef void (*btd_device_service_func_t)(struct btd_service *service,
> +                                       void *user_data);
> +void btd_device_foreach_service(struct btd_device *dev,
> +                               btd_device_service_func_t func,
> +                               void *user_data);
> +void device_remove_pending_services(struct btd_device *dev,
> +                                       uint8_t bdaddr_type);
> \ No newline at end of file
>
> ---
> base-commit: 5e41d1e1d361e7288964e4c2c5ed90736025662f
> change-id: 20251111-bearer-impl-5f7e396174aa
>
> Best regards,
> --
> Ye He <ye.he@amlogic.com>
>
>
>
>
> --
> Luiz Augusto von Dentz



-- 
Luiz Augusto von Dentz

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

* Re: [PATCH bluez v3] bearer: Implement Connect/Disconnect methods
       [not found]   ` <09087c54-4d27-460e-a6cb-ac74833068b6@amlogic.com>
  2025-11-20 16:22     ` Luiz Augusto von Dentz
@ 2025-11-20 16:24     ` Luiz Augusto von Dentz
  1 sibling, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2025-11-20 16:24 UTC (permalink / raw)
  To: Ye He; +Cc: Linux Bluetooth

Hi,

On Tue, Nov 18, 2025 at 9:40 PM Ye He <ye.he@amlogic.com> wrote:
>
> Hi Luiz,
>
> Hi,
>
> On Tue, Nov 18, 2025 at 4:32 AM Ye He via B4 Relay
> <devnull+ye.he.amlogic.com@kernel.org> wrote:
>
> From: Ye He <ye.he@amlogic.com>
>
> This patch provides implementations for the Connect and Disconnect
> methods of the org.bluez.Bearer.LE1 and org.bluez.Bearer.BREDR1
> interfaces.
>
> Ok, we will need some coverage in bluetoothctl in order to exercise this API.
>
> OK, I will add the shell commands of bearer into bluetoothctl.
>
> Signed-off-by: Ye He <ye.he@amlogic.com>
> ---
> Changes in v3:
> - Move the modification logic from the device to bearer, and add
> - some helper functions to the device.
> - Link to v2: https://patch.msgid.link/20251113-bearer-impl-v2-1-c3e825cc6758@amlogic.com
>
> Changes in v2:
> - Fix build error & warning.
> - Link to v1: https://patch.msgid.link/20251111-bearer-impl-v1-1-f41585144218@amlogic.com
> ---
>  src/bearer.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  src/device.c | 130 ++++++++++++++++++++++++++++++++++++++++--------
>  src/device.h |  17 +++++++
>  3 files changed, 285 insertions(+), 22 deletions(-)
>
> diff --git a/src/bearer.c b/src/bearer.c
> index 9723b59e2e7b39b2974e258363aa0ace95862651..46e272b16096740ce28bcc80d0e2426df8538a2e 100644
> --- a/src/bearer.c
> +++ b/src/bearer.c
> @@ -25,23 +25,38 @@
>
>  #include "bluetooth/bluetooth.h"
>  #include "bluetooth/mgmt.h"
> +#include "bluetooth/uuid.h"
>
>  #include "gdbus/gdbus.h"
>  #include "src/shared/util.h"
> +#include "src/shared/queue.h"
> +#include "src/shared/timeout.h"
>
>  #include "log.h"
>  #include "error.h"
>  #include "adapter.h"
>  #include "device.h"
> +#include "profile.h"
> +#include "service.h"
>  #include "dbus-common.h"
>  #include "bearer.h"
>
> +#define DISCONNECT_TIMER       2
> +
>  struct btd_bearer {
>         struct btd_device *device;
>         uint8_t type;
>         const char *path;
> +       unsigned int disconn_timer;
> +       struct queue *disconnects; /* disconnects message */
> +       DBusMessage *connect; /* connect message */
>
> This doesn't sound quite right, why don't we queue connect messages?
>
> Just like Connect() in org.bluez.Device1, the connect operation is defined
> to allow only a single in-flight request. Queuing additional Connect
> calls would change the existing API semantics and could lead to
> unexpected state transitions—for example, duplicated browse attempts
> or redundant link-level connection requests.
>
> Disconnect() is different: queuing only affects how replies are
> delivered, not the device’s actual behavior.
>
>  };
>
> +static void bearer_free_dbus_message(void *data)
> +{
> +    dbus_message_unref((DBusMessage *)data);
> +}
> +
>  static void bearer_free(void *data)
>  {
>         struct btd_bearer *bearer = data;
> @@ -49,17 +64,126 @@ static void bearer_free(void *data)
>         free(bearer);
>  }
>
> +static void bearer_disconnect_service(struct btd_service *service, void *user_data)
> +{
> +       uint8_t bdaddr_type = *(uint8_t *)user_data;
> +       struct btd_profile *profile;
> +       bt_uuid_t uuid;
> +
> +       profile = btd_service_get_profile(service);
> +       if (!profile || !profile->remote_uuid)
> +               return;
> +
> +       if (bt_string_to_uuid(&uuid, profile->remote_uuid) != 0)
> +               return;
> +
> +       if (bdaddr_type == BDADDR_BREDR) {
> +               if (uuid.value.u16 < 0x1100 || uuid.value.u16 > 0x11FF)
> +                       return;
> +       } else {
> +               if (uuid.value.u16 < 0x1800 || uuid.value.u16 > 0x18FF)
> +                       return;
> +       }
>
> What are these ranges above?
>
> The UUID ranges are the SIG-assigned blocks for BR/EDR profiles
> (0x1100–0x11FF) and GATT services (0x1800–0x18FF).
> I originally used them to guess which bearer a service belongs to, but
> this seems unreliable — some profiles exist on both BR/EDR and LE.
> I’m open to better guidance on how to classify services/profiles
> per bearer.
>
> +
> +       DBG("Disconnecting profile %s (UUID 0x%04x) for bearer addr type %u",
> +       profile->name ?: "(unknown)", uuid.value.u16, bdaddr_type);
> +
> +       btd_service_disconnect(service);
> +}
> +
> +static bool bearer_disconnect_link(gpointer user_data)
> +{
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +
> +       bearer->disconn_timer = 0;
> +
> +       if (btd_device_bdaddr_type_connected(device, bearer->type))
> +               btd_adapter_disconnect_device(device_get_adapter(device),
> +                                               device_get_address(device),
> +                                               bearer->type);
> +       return FALSE;
> +}
> +
>  static DBusMessage *bearer_connect(DBusConnection *conn, DBusMessage *msg,
>                                                         void *user_data)
>  {
> -       /* TODO */
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +       int err;
> +
> +       if (btd_device_bdaddr_type_connected(device, bearer->type)) {
> +               if (msg)
> +                       return btd_error_already_connected(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_bonding(device, NULL)) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_connecting(device) ||
> +               bearer->connect) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (msg)
> +               bearer->connect = dbus_message_ref(msg);
> +
> +       if (bearer->type == BDADDR_BREDR)
> +               return device_connect_profiles(device, BDADDR_BREDR,
> +                                                               msg, NULL);
> +       else {
> +               btd_device_set_temporary(device, false);
> +               err = device_connect_le(device);
> +               if (err < 0)
> +                       return btd_error_failed(msg, strerror(-err));
> +       }
> +
>         return NULL;
>  }
>
>  static DBusMessage *bearer_disconnect(DBusConnection *conn, DBusMessage *msg,
>                                                         void *user_data)
>  {
> -       /* TODO */
> +       struct btd_bearer *bearer = user_data;
> +       struct btd_device *device = bearer->device;
> +
> +       if (!btd_device_bdaddr_type_connected(device, bearer->type)) {
> +               if (msg)
> +                       return btd_error_not_connected(msg);
> +               return NULL;
> +       }
> +
> +       if (device_is_disconnecting(device)) {
> +               if (msg)
> +                       return btd_error_in_progress(msg);
> +               return NULL;
> +       }
> +
> +       if (msg)
> +               queue_push_tail(bearer->disconnects, dbus_message_ref(msg));
>
> This also makes no sense, if you are going to queue the disconnect
> message why are you sending in progress error if it is already
> disconnecting?
>
> device_is_disconnecting(device) indicates that the org.bluez.Device1.Disconnect()
> operation is in progress. This call has the higher priority because
> it tears down both LE and BR/EDR bearers. Any bearer-level disconnect
> requests should therefore be dropped once this state is active.

Forgot about this one, this is fine but you might want to add the
above as a comment to why checking device_is_diconnecting is
necessary.

> +       device_cancel_bonding(device, MGMT_STATUS_DISCONNECTED);
> +
> +       device_cancel_browse(device, bearer->type);
> +
> +       btd_device_foreach_service(device, bearer_disconnect_service,
> +                                                       &bearer->type);
> +
> +       device_remove_pending_services(device, bearer->type);
> +
> +       if (bearer->disconn_timer)
> +               return NULL;
> +
> +       bearer->disconn_timer = timeout_add_seconds(DISCONNECT_TIMER,
> +                                                       bearer_disconnect_link,
> +                                                       bearer, NULL);
> +
>         return NULL;
>  }
>
> @@ -151,6 +275,7 @@ struct btd_bearer *btd_bearer_new(struct btd_device *device, uint8_t type)
>         bearer->device = device;
>         bearer->type = type;
>         bearer->path = device_get_path(device);
> +       bearer->disconnects = queue_new();
>
>         if (!g_dbus_register_interface(btd_get_dbus_connection(),
>                                         bearer->path, bearer_interface(type),
> @@ -174,6 +299,16 @@ void btd_bearer_destroy(struct btd_bearer *bearer)
>                 return;
>         }
>
> +       if (bearer->disconnects) {
> +               queue_destroy(bearer->disconnects, bearer_free_dbus_message);
> +               bearer->disconnects = NULL;
> +       }
> +
> +       if (bearer->connect) {
> +               dbus_message_unref(bearer->connect);
> +               bearer->connect = NULL;
> +       }
> +
>         g_dbus_unregister_interface(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type));
>  }
> @@ -203,6 +338,13 @@ void btd_bearer_connected(struct btd_bearer *bearer)
>         if (!bearer || !bearer->path)
>                 return;
>
> +       if (bearer->connect) {
> +               g_dbus_send_reply(btd_get_dbus_connection(), bearer->connect,
> +                                               DBUS_TYPE_INVALID);
> +               dbus_message_unref(bearer->connect);
> +               bearer->connect = NULL;
> +       }
> +
>         g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type),
>                                         "Connected");
> @@ -212,10 +354,24 @@ void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason)
>  {
>         const char *name;
>         const char *message;
> +       DBusMessage *msg;
> +       const struct queue_entry *entry;
>
>         if (!bearer || !bearer->path)
>                 return;
>
> +       if (!btd_device_is_connected(bearer->device))
> +               device_disconnect_watches_callback(bearer->device);
> +
> +       while (!queue_isempty(bearer->disconnects)) {
> +               entry = queue_get_entries(bearer->disconnects);
> +               msg = entry->data;
> +               g_dbus_send_reply(btd_get_dbus_connection(), msg,
> +                                               DBUS_TYPE_INVALID);
> +               queue_remove(bearer->disconnects, msg);
> +               dbus_message_unref(msg);
> +       }
> +
>         g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>                                         bearer_interface(bearer->type),
>                                         "Connected");
> diff --git a/src/device.c b/src/device.c
> index 91b6cc0c65eaae8058cd445c0942ffee57289f0d..918f1e51acd3a9cd6b776cc080c198885511fb20 100644
> --- a/src/device.c
> +++ b/src/device.c
> @@ -2022,6 +2022,28 @@ static void dev_disconn_service(gpointer a, gpointer b)
>         btd_service_disconnect(a);
>  }
>
> +void device_disconnect_watches_callback(struct btd_device *device)
> +{
> +       if (!device || !device->watches)
> +               return;
> +
> +       while (device->watches) {
> +               struct btd_disconnect_data *data = device->watches->data;
> +
> +               if (data->watch)
> +                       /* temporary is set if device is going to be removed */
> +                       data->watch(device, device->temporary,
> +                                                       data->user_data);
> +
> +               /* Check if the watch has been removed by callback function */
> +               if (!g_slist_find(device->watches, data))
> +                       continue;
> +
> +               device->watches = g_slist_remove(device->watches, data);
> +               g_free(data);
> +       }
> +}
> +
>  void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>  {
>         if (device->bonding)
> @@ -2063,21 +2085,7 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>         g_slist_free(device->pending);
>         device->pending = NULL;
>
> -       while (device->watches) {
> -               struct btd_disconnect_data *data = device->watches->data;
> -
> -               if (data->watch)
> -                       /* temporary is set if device is going to be removed */
> -                       data->watch(device, device->temporary,
> -                                                       data->user_data);
> -
> -               /* Check if the watch has been removed by callback function */
> -               if (!g_slist_find(device->watches, data))
> -                       continue;
> -
> -               device->watches = g_slist_remove(device->watches, data);
> -               g_free(data);
> -       }
> +       device_disconnect_watches_callback(device);
>
>         if (!btd_device_is_connected(device)) {
>                 if (msg)
> @@ -2095,6 +2103,11 @@ bool device_is_disconnecting(struct btd_device *device)
>         return device->disconn_timer > 0;
>  }
>
> +bool device_is_connecting(struct btd_device *device)
> +{
> +       return device->connect != NULL;
> +}
> +
>  static void add_set(void *data, void *user_data)
>  {
>         struct sirk_info *sirk = data;
> @@ -2712,8 +2725,8 @@ int btd_device_connect_services(struct btd_device *dev, GSList *services)
>         return connect_next(dev);
>  }
>
> -static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type,
> -                                       DBusMessage *msg, const char *uuid)
> +DBusMessage *device_connect_profiles(struct btd_device *dev,
> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid)
>  {
>         struct bearer_state *state = get_state(dev, bdaddr_type);
>         int err;
> @@ -2826,7 +2839,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
>                 return NULL;
>         }
>
> -       return connect_profiles(dev, bdaddr_type, msg, NULL);
> +       return device_connect_profiles(dev, bdaddr_type, msg, NULL);
>  }
>
>  static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
> @@ -2848,7 +2861,7 @@ static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
>                 return btd_error_invalid_args_str(msg,
>                                         ERR_BREDR_CONN_INVALID_ARGUMENTS);
>
> -       reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid);
> +       reply = device_connect_profiles(dev, BDADDR_BREDR, msg, uuid);
>         free(uuid);
>
>         return reply;
> @@ -3421,7 +3434,7 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
>         }
>  }
>
> -static void device_cancel_bonding(struct btd_device *device, uint8_t status)
> +void device_cancel_bonding(struct btd_device *device, uint8_t status)
>  {
>         struct bonding_req *bonding = device->bonding;
>         DBusMessage *reply;
> @@ -6629,6 +6642,38 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
>         return err;
>  }
>
> +static gboolean device_is_browsing(struct btd_device *device, uint8_t bdaddr_type)
> +{
> +       if (!device->browse)
> +               return FALSE;
> +
> +       if (bdaddr_type == BDADDR_BREDR && device->browse->type == BROWSE_SDP)
> +               return TRUE;
> +
> +       if (bdaddr_type != BDADDR_BREDR && device->browse->type == BROWSE_GATT)
> +               return TRUE;
> +
> +       return FALSE;
> +}
> +
> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type)
> +{
> +       struct btd_adapter *adapter = device->adapter;
> +
> +       DBG("");
> +
> +       if (!device_is_browsing(device, bdaddr_type))
> +               return;
> +
> +       if (bdaddr_type == BDADDR_BREDR)
> +               bt_cancel_discovery(btd_adapter_get_address(adapter),
> +                                                       &device->bdaddr);
> +       else
> +               attio_cleanup(device);
> +
> +       browse_request_free(device->browse);
> +}
> +
>  int device_discover_services(struct btd_device *device)
>  {
>         int err;
> @@ -8088,3 +8133,48 @@ void btd_device_foreach_service_data(struct btd_device *dev, bt_ad_func_t func,
>  {
>         bt_ad_foreach_service_data(dev->ad, func, data);
>  }
> +
> +
> +void btd_device_foreach_service(struct btd_device *dev,
> +                               btd_device_service_func_t func,
> +                               void *user_data)
> +{
> +       GSList *l;
> +
> +       for (l = dev->services; l; l = l->next)
> +               func(l->data, user_data);
> +}
> +
> +void device_remove_pending_services(struct btd_device *dev,
> +                                       uint8_t bdaddr_type)
> +{
> +       GSList *l = dev->pending;
> +       GSList *next;
> +       struct btd_service *service;
> +       struct btd_profile *profile;
> +       bt_uuid_t uuid;
> +       bool match = false;
> +
> +       while (l) {
> +               next = l->next;
> +               service = l->data;
> +
> +               profile = btd_service_get_profile(service);
> +               if (profile && profile->remote_uuid &&
> +                       bt_string_to_uuid(&uuid, profile->remote_uuid) == 0) {
> +
> +                       if (bdaddr_type == BDADDR_BREDR)
> +                               match = uuid.value.u16 >= 0x1100 ||
> +                                       uuid.value.u16 <= 0x11FF;
> +                       else
> +                               match = uuid.value.u16 >= 0x1800 ||
> +                                       uuid.value.u16 <= 0x18FF;
> +
> +                       if (match)
> +                               dev->pending = g_slist_remove(dev->pending,
> +                                                               service);
> +               }
> +
> +               l = next;
> +       }
> +}
> \ No newline at end of file
> diff --git a/src/device.h b/src/device.h
> index 6ed8affa0d4a9274d30cac9b48e8a6826edefd64..fee1ad50dc9c7ae248acd819dae07762c783ac35 100644
> --- a/src/device.h
> +++ b/src/device.h
> @@ -120,6 +120,7 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
>  gboolean device_is_bonding(struct btd_device *device, const char *sender);
>  void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
>  void device_bonding_failed(struct btd_device *device, uint8_t status);
> +void device_cancel_bonding(struct btd_device *device, uint8_t status);
>  struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device);
>  int device_bonding_attempt_retry(struct btd_device *device);
>  long device_bonding_last_duration(struct btd_device *device);
> @@ -134,6 +135,9 @@ int device_notify_pincode(struct btd_device *device, gboolean secure,
>                                                         const char *pincode);
>  void device_cancel_authentication(struct btd_device *device, gboolean aborted);
>  gboolean device_is_authenticating(struct btd_device *device);
> +
> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type);
> +
>  void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type,
>                                                         uint32_t flags);
>  void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
> @@ -141,6 +145,7 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
>                                                         uint8_t reason);
>  void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
>  bool device_is_disconnecting(struct btd_device *device);
> +bool device_is_connecting(struct btd_device *device);
>  void device_set_ltk(struct btd_device *device, const uint8_t val[16],
>                                 bool central, uint8_t enc_size);
>  bool btd_device_get_ltk(struct btd_device *device, uint8_t val[16],
> @@ -170,6 +175,7 @@ guint device_add_disconnect_watch(struct btd_device *device,
>                                 disconnect_watch watch, void *user_data,
>                                 GDestroyNotify destroy);
>  void device_remove_disconnect_watch(struct btd_device *device, guint id);
> +void device_disconnect_watches_callback(struct btd_device *device);
>  int device_get_appearance(struct btd_device *device, uint16_t *value);
>  void device_set_appearance(struct btd_device *device, uint16_t value);
>
> @@ -184,6 +190,9 @@ void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
>
>  int device_connect_le(struct btd_device *dev);
>
> +DBusMessage *device_connect_profiles(struct btd_device *dev,
> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid);
> +
>  typedef void (*device_svc_cb_t) (struct btd_device *dev, int err,
>                                                         void *user_data);
>
> @@ -225,3 +234,11 @@ void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval,
>  void btd_device_foreach_service_data(struct btd_device *dev,
>                                         bt_device_ad_func_t func,
>                                         void *data);
> +
> +typedef void (*btd_device_service_func_t)(struct btd_service *service,
> +                                       void *user_data);
> +void btd_device_foreach_service(struct btd_device *dev,
> +                               btd_device_service_func_t func,
> +                               void *user_data);
> +void device_remove_pending_services(struct btd_device *dev,
> +                                       uint8_t bdaddr_type);
> \ No newline at end of file
>
> ---
> base-commit: 5e41d1e1d361e7288964e4c2c5ed90736025662f
> change-id: 20251111-bearer-impl-5f7e396174aa
>
> Best regards,
> --
> Ye He <ye.he@amlogic.com>
>
>
>
>
> --
> Luiz Augusto von Dentz



-- 
Luiz Augusto von Dentz

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

* Re: [PATCH bluez v3] bearer: Implement Connect/Disconnect methods
  2025-11-20 16:22     ` Luiz Augusto von Dentz
@ 2025-11-21  1:59       ` Ye He
  0 siblings, 0 replies; 7+ messages in thread
From: Ye He @ 2025-11-21  1:59 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: Linux Bluetooth

Hi Luiz,

> Hi,
>
> On Tue, Nov 18, 2025 at 9:40 PM Ye He <ye.he@amlogic.com> wrote:
>> Hi Luiz,
>>
>> Hi,
>>
>> On Tue, Nov 18, 2025 at 4:32 AM Ye He via B4 Relay
>> <devnull+ye.he.amlogic.com@kernel.org> wrote:
>>
>> From: Ye He <ye.he@amlogic.com>
>>
>> This patch provides implementations for the Connect and Disconnect
>> methods of the org.bluez.Bearer.LE1 and org.bluez.Bearer.BREDR1
>> interfaces.
>>
>> Ok, we will need some coverage in bluetoothctl in order to exercise this API.
>>
>> OK, I will add the shell commands of bearer into bluetoothctl.
>>
>> Signed-off-by: Ye He <ye.he@amlogic.com>
>> ---
>> Changes in v3:
>> - Move the modification logic from the device to bearer, and add
>> - some helper functions to the device.
>> - Link to v2: https://patch.msgid.link/20251113-bearer-impl-v2-1-c3e825cc6758@amlogic.com
>>
>> Changes in v2:
>> - Fix build error & warning.
>> - Link to v1: https://patch.msgid.link/20251111-bearer-impl-v1-1-f41585144218@amlogic.com
>> ---
>>   src/bearer.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>>   src/device.c | 130 ++++++++++++++++++++++++++++++++++++++++--------
>>   src/device.h |  17 +++++++
>>   3 files changed, 285 insertions(+), 22 deletions(-)
>>
>> diff --git a/src/bearer.c b/src/bearer.c
>> index 9723b59e2e7b39b2974e258363aa0ace95862651..46e272b16096740ce28bcc80d0e2426df8538a2e 100644
>> --- a/src/bearer.c
>> +++ b/src/bearer.c
>> @@ -25,23 +25,38 @@
>>
>>   #include "bluetooth/bluetooth.h"
>>   #include "bluetooth/mgmt.h"
>> +#include "bluetooth/uuid.h"
>>
>>   #include "gdbus/gdbus.h"
>>   #include "src/shared/util.h"
>> +#include "src/shared/queue.h"
>> +#include "src/shared/timeout.h"
>>
>>   #include "log.h"
>>   #include "error.h"
>>   #include "adapter.h"
>>   #include "device.h"
>> +#include "profile.h"
>> +#include "service.h"
>>   #include "dbus-common.h"
>>   #include "bearer.h"
>>
>> +#define DISCONNECT_TIMER       2
>> +
>>   struct btd_bearer {
>>          struct btd_device *device;
>>          uint8_t type;
>>          const char *path;
>> +       unsigned int disconn_timer;
>> +       struct queue *disconnects; /* disconnects message */
>> +       DBusMessage *connect; /* connect message */
>>
>> This doesn't sound quite right, why don't we queue connect messages?
>>
>> Just like Connect() in org.bluez.Device1, the connect operation is defined
>> to allow only a single in-flight request. Queuing additional Connect
>> calls would change the existing API semantics and could lead to
>> unexpected state transitions—for example, duplicated browse attempts
>> or redundant link-level connection requests.
> I think that was done because there is the likes of ConnectProfile,
> which is not the case here, anyway I'd probably add a comment that
> this is done in order to keep API semantics from Device.Connect.

OK, I will keep avoiding queuing Connect messages and add a comment
explaining that this is intentional in order to preserve the same API
semantics as Device.Connect.

>> Disconnect() is different: queuing only affects how replies are
>> delivered, not the device’s actual behavior.
>>
>>   };
>>
>> +static void bearer_free_dbus_message(void *data)
>> +{
>> +    dbus_message_unref((DBusMessage *)data);
>> +}
>> +
>>   static void bearer_free(void *data)
>>   {
>>          struct btd_bearer *bearer = data;
>> @@ -49,17 +64,126 @@ static void bearer_free(void *data)
>>          free(bearer);
>>   }
>>
>> +static void bearer_disconnect_service(struct btd_service *service, void *user_data)
>> +{
>> +       uint8_t bdaddr_type = *(uint8_t *)user_data;
>> +       struct btd_profile *profile;
>> +       bt_uuid_t uuid;
>> +
>> +       profile = btd_service_get_profile(service);
>> +       if (!profile || !profile->remote_uuid)
>> +               return;
>> +
>> +       if (bt_string_to_uuid(&uuid, profile->remote_uuid) != 0)
>> +               return;
>> +
>> +       if (bdaddr_type == BDADDR_BREDR) {
>> +               if (uuid.value.u16 < 0x1100 || uuid.value.u16 > 0x11FF)
>> +                       return;
>> +       } else {
>> +               if (uuid.value.u16 < 0x1800 || uuid.value.u16 > 0x18FF)
>> +                       return;
>> +       }
>>
>> What are these ranges above?
>>
>> The UUID ranges are the SIG-assigned blocks for BR/EDR profiles
>> (0x1100–0x11FF) and GATT services (0x1800–0x18FF).
>> I originally used them to guess which bearer a service belongs to, but
>> this seems unreliable — some profiles exist on both BR/EDR and LE.
>> I’m open to better guidance on how to classify services/profiles
>> per bearer.
> If we really need to distinct the services per bearer Id add a bearer
> field to btd_profile, because as you say above there are profiles that
> can exist in both BR/EDR and LE, anyway if you look at util.c where
> there is the table of UUIDs it seems that the service UUID can extend
> from 0x1800 to 0x2800, but there could also be vendor ones which are
> on 0xfxxx range, meaning there could be multiple ranges we would need
> to check so I don't think this would be reliable in the end.

Thanks for the guidance. I will add a bearer field to btd_profile to
indicate the bearer type.

>> +
>> +       DBG("Disconnecting profile %s (UUID 0x%04x) for bearer addr type %u",
>> +       profile->name ?: "(unknown)", uuid.value.u16, bdaddr_type);
>> +
>> +       btd_service_disconnect(service);
>> +}
>> +
>> +static bool bearer_disconnect_link(gpointer user_data)
>> +{
>> +       struct btd_bearer *bearer = user_data;
>> +       struct btd_device *device = bearer->device;
>> +
>> +       bearer->disconn_timer = 0;
>> +
>> +       if (btd_device_bdaddr_type_connected(device, bearer->type))
>> +               btd_adapter_disconnect_device(device_get_adapter(device),
>> +                                               device_get_address(device),
>> +                                               bearer->type);
>> +       return FALSE;
>> +}
>> +
>>   static DBusMessage *bearer_connect(DBusConnection *conn, DBusMessage *msg,
>>                                                          void *user_data)
>>   {
>> -       /* TODO */
>> +       struct btd_bearer *bearer = user_data;
>> +       struct btd_device *device = bearer->device;
>> +       int err;
>> +
>> +       if (btd_device_bdaddr_type_connected(device, bearer->type)) {
>> +               if (msg)
>> +                       return btd_error_already_connected(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (device_is_bonding(device, NULL)) {
>> +               if (msg)
>> +                       return btd_error_in_progress(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (device_is_connecting(device) ||
>> +               bearer->connect) {
>> +               if (msg)
>> +                       return btd_error_in_progress(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (msg)
>> +               bearer->connect = dbus_message_ref(msg);
>> +
>> +       if (bearer->type == BDADDR_BREDR)
>> +               return device_connect_profiles(device, BDADDR_BREDR,
>> +                                                               msg, NULL);
>> +       else {
>> +               btd_device_set_temporary(device, false);
>> +               err = device_connect_le(device);
>> +               if (err < 0)
>> +                       return btd_error_failed(msg, strerror(-err));
>> +       }
>> +
>>          return NULL;
>>   }
>>
>>   static DBusMessage *bearer_disconnect(DBusConnection *conn, DBusMessage *msg,
>>                                                          void *user_data)
>>   {
>> -       /* TODO */
>> +       struct btd_bearer *bearer = user_data;
>> +       struct btd_device *device = bearer->device;
>> +
>> +       if (!btd_device_bdaddr_type_connected(device, bearer->type)) {
>> +               if (msg)
>> +                       return btd_error_not_connected(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (device_is_disconnecting(device)) {
>> +               if (msg)
>> +                       return btd_error_in_progress(msg);
>> +               return NULL;
>> +       }
>> +
>> +       if (msg)
>> +               queue_push_tail(bearer->disconnects, dbus_message_ref(msg));
>>
>> This also makes no sense, if you are going to queue the disconnect
>> message why are you sending in progress error if it is already
>> disconnecting?
>>
>> device_is_disconnecting(device) indicates that the org.bluez.Device1.Disconnect()
>> operation is in progress. This call has the higher priority because
>> it tears down both LE and BR/EDR bearers. Any bearer-level disconnect
>> requests should therefore be dropped once this state is active.
>>
>> +       device_cancel_bonding(device, MGMT_STATUS_DISCONNECTED);
>> +
>> +       device_cancel_browse(device, bearer->type);
>> +
>> +       btd_device_foreach_service(device, bearer_disconnect_service,
>> +                                                       &bearer->type);
>> +
>> +       device_remove_pending_services(device, bearer->type);
>> +
>> +       if (bearer->disconn_timer)
>> +               return NULL;
>> +
>> +       bearer->disconn_timer = timeout_add_seconds(DISCONNECT_TIMER,
>> +                                                       bearer_disconnect_link,
>> +                                                       bearer, NULL);
>> +
>>          return NULL;
>>   }
>>
>> @@ -151,6 +275,7 @@ struct btd_bearer *btd_bearer_new(struct btd_device *device, uint8_t type)
>>          bearer->device = device;
>>          bearer->type = type;
>>          bearer->path = device_get_path(device);
>> +       bearer->disconnects = queue_new();
>>
>>          if (!g_dbus_register_interface(btd_get_dbus_connection(),
>>                                          bearer->path, bearer_interface(type),
>> @@ -174,6 +299,16 @@ void btd_bearer_destroy(struct btd_bearer *bearer)
>>                  return;
>>          }
>>
>> +       if (bearer->disconnects) {
>> +               queue_destroy(bearer->disconnects, bearer_free_dbus_message);
>> +               bearer->disconnects = NULL;
>> +       }
>> +
>> +       if (bearer->connect) {
>> +               dbus_message_unref(bearer->connect);
>> +               bearer->connect = NULL;
>> +       }
>> +
>>          g_dbus_unregister_interface(btd_get_dbus_connection(), bearer->path,
>>                                          bearer_interface(bearer->type));
>>   }
>> @@ -203,6 +338,13 @@ void btd_bearer_connected(struct btd_bearer *bearer)
>>          if (!bearer || !bearer->path)
>>                  return;
>>
>> +       if (bearer->connect) {
>> +               g_dbus_send_reply(btd_get_dbus_connection(), bearer->connect,
>> +                                               DBUS_TYPE_INVALID);
>> +               dbus_message_unref(bearer->connect);
>> +               bearer->connect = NULL;
>> +       }
>> +
>>          g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>>                                          bearer_interface(bearer->type),
>>                                          "Connected");
>> @@ -212,10 +354,24 @@ void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason)
>>   {
>>          const char *name;
>>          const char *message;
>> +       DBusMessage *msg;
>> +       const struct queue_entry *entry;
>>
>>          if (!bearer || !bearer->path)
>>                  return;
>>
>> +       if (!btd_device_is_connected(bearer->device))
>> +               device_disconnect_watches_callback(bearer->device);
>> +
>> +       while (!queue_isempty(bearer->disconnects)) {
>> +               entry = queue_get_entries(bearer->disconnects);
>> +               msg = entry->data;
>> +               g_dbus_send_reply(btd_get_dbus_connection(), msg,
>> +                                               DBUS_TYPE_INVALID);
>> +               queue_remove(bearer->disconnects, msg);
>> +               dbus_message_unref(msg);
>> +       }
>> +
>>          g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
>>                                          bearer_interface(bearer->type),
>>                                          "Connected");
>> diff --git a/src/device.c b/src/device.c
>> index 91b6cc0c65eaae8058cd445c0942ffee57289f0d..918f1e51acd3a9cd6b776cc080c198885511fb20 100644
>> --- a/src/device.c
>> +++ b/src/device.c
>> @@ -2022,6 +2022,28 @@ static void dev_disconn_service(gpointer a, gpointer b)
>>          btd_service_disconnect(a);
>>   }
>>
>> +void device_disconnect_watches_callback(struct btd_device *device)
>> +{
>> +       if (!device || !device->watches)
>> +               return;
>> +
>> +       while (device->watches) {
>> +               struct btd_disconnect_data *data = device->watches->data;
>> +
>> +               if (data->watch)
>> +                       /* temporary is set if device is going to be removed */
>> +                       data->watch(device, device->temporary,
>> +                                                       data->user_data);
>> +
>> +               /* Check if the watch has been removed by callback function */
>> +               if (!g_slist_find(device->watches, data))
>> +                       continue;
>> +
>> +               device->watches = g_slist_remove(device->watches, data);
>> +               g_free(data);
>> +       }
>> +}
>> +
>>   void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>>   {
>>          if (device->bonding)
>> @@ -2063,21 +2085,7 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
>>          g_slist_free(device->pending);
>>          device->pending = NULL;
>>
>> -       while (device->watches) {
>> -               struct btd_disconnect_data *data = device->watches->data;
>> -
>> -               if (data->watch)
>> -                       /* temporary is set if device is going to be removed */
>> -                       data->watch(device, device->temporary,
>> -                                                       data->user_data);
>> -
>> -               /* Check if the watch has been removed by callback function */
>> -               if (!g_slist_find(device->watches, data))
>> -                       continue;
>> -
>> -               device->watches = g_slist_remove(device->watches, data);
>> -               g_free(data);
>> -       }
>> +       device_disconnect_watches_callback(device);
>>
>>          if (!btd_device_is_connected(device)) {
>>                  if (msg)
>> @@ -2095,6 +2103,11 @@ bool device_is_disconnecting(struct btd_device *device)
>>          return device->disconn_timer > 0;
>>   }
>>
>> +bool device_is_connecting(struct btd_device *device)
>> +{
>> +       return device->connect != NULL;
>> +}
>> +
>>   static void add_set(void *data, void *user_data)
>>   {
>>          struct sirk_info *sirk = data;
>> @@ -2712,8 +2725,8 @@ int btd_device_connect_services(struct btd_device *dev, GSList *services)
>>          return connect_next(dev);
>>   }
>>
>> -static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type,
>> -                                       DBusMessage *msg, const char *uuid)
>> +DBusMessage *device_connect_profiles(struct btd_device *dev,
>> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid)
>>   {
>>          struct bearer_state *state = get_state(dev, bdaddr_type);
>>          int err;
>> @@ -2826,7 +2839,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
>>                  return NULL;
>>          }
>>
>> -       return connect_profiles(dev, bdaddr_type, msg, NULL);
>> +       return device_connect_profiles(dev, bdaddr_type, msg, NULL);
>>   }
>>
>>   static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
>> @@ -2848,7 +2861,7 @@ static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
>>                  return btd_error_invalid_args_str(msg,
>>                                          ERR_BREDR_CONN_INVALID_ARGUMENTS);
>>
>> -       reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid);
>> +       reply = device_connect_profiles(dev, BDADDR_BREDR, msg, uuid);
>>          free(uuid);
>>
>>          return reply;
>> @@ -3421,7 +3434,7 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
>>          }
>>   }
>>
>> -static void device_cancel_bonding(struct btd_device *device, uint8_t status)
>> +void device_cancel_bonding(struct btd_device *device, uint8_t status)
>>   {
>>          struct bonding_req *bonding = device->bonding;
>>          DBusMessage *reply;
>> @@ -6629,6 +6642,38 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
>>          return err;
>>   }
>>
>> +static gboolean device_is_browsing(struct btd_device *device, uint8_t bdaddr_type)
>> +{
>> +       if (!device->browse)
>> +               return FALSE;
>> +
>> +       if (bdaddr_type == BDADDR_BREDR && device->browse->type == BROWSE_SDP)
>> +               return TRUE;
>> +
>> +       if (bdaddr_type != BDADDR_BREDR && device->browse->type == BROWSE_GATT)
>> +               return TRUE;
>> +
>> +       return FALSE;
>> +}
>> +
>> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type)
>> +{
>> +       struct btd_adapter *adapter = device->adapter;
>> +
>> +       DBG("");
>> +
>> +       if (!device_is_browsing(device, bdaddr_type))
>> +               return;
>> +
>> +       if (bdaddr_type == BDADDR_BREDR)
>> +               bt_cancel_discovery(btd_adapter_get_address(adapter),
>> +                                                       &device->bdaddr);
>> +       else
>> +               attio_cleanup(device);
>> +
>> +       browse_request_free(device->browse);
>> +}
>> +
>>   int device_discover_services(struct btd_device *device)
>>   {
>>          int err;
>> @@ -8088,3 +8133,48 @@ void btd_device_foreach_service_data(struct btd_device *dev, bt_ad_func_t func,
>>   {
>>          bt_ad_foreach_service_data(dev->ad, func, data);
>>   }
>> +
>> +
>> +void btd_device_foreach_service(struct btd_device *dev,
>> +                               btd_device_service_func_t func,
>> +                               void *user_data)
>> +{
>> +       GSList *l;
>> +
>> +       for (l = dev->services; l; l = l->next)
>> +               func(l->data, user_data);
>> +}
>> +
>> +void device_remove_pending_services(struct btd_device *dev,
>> +                                       uint8_t bdaddr_type)
>> +{
>> +       GSList *l = dev->pending;
>> +       GSList *next;
>> +       struct btd_service *service;
>> +       struct btd_profile *profile;
>> +       bt_uuid_t uuid;
>> +       bool match = false;
>> +
>> +       while (l) {
>> +               next = l->next;
>> +               service = l->data;
>> +
>> +               profile = btd_service_get_profile(service);
>> +               if (profile && profile->remote_uuid &&
>> +                       bt_string_to_uuid(&uuid, profile->remote_uuid) == 0) {
>> +
>> +                       if (bdaddr_type == BDADDR_BREDR)
>> +                               match = uuid.value.u16 >= 0x1100 ||
>> +                                       uuid.value.u16 <= 0x11FF;
>> +                       else
>> +                               match = uuid.value.u16 >= 0x1800 ||
>> +                                       uuid.value.u16 <= 0x18FF;
>> +
>> +                       if (match)
>> +                               dev->pending = g_slist_remove(dev->pending,
>> +                                                               service);
>> +               }
>> +
>> +               l = next;
>> +       }
>> +}
>> \ No newline at end of file
>> diff --git a/src/device.h b/src/device.h
>> index 6ed8affa0d4a9274d30cac9b48e8a6826edefd64..fee1ad50dc9c7ae248acd819dae07762c783ac35 100644
>> --- a/src/device.h
>> +++ b/src/device.h
>> @@ -120,6 +120,7 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
>>   gboolean device_is_bonding(struct btd_device *device, const char *sender);
>>   void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
>>   void device_bonding_failed(struct btd_device *device, uint8_t status);
>> +void device_cancel_bonding(struct btd_device *device, uint8_t status);
>>   struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device);
>>   int device_bonding_attempt_retry(struct btd_device *device);
>>   long device_bonding_last_duration(struct btd_device *device);
>> @@ -134,6 +135,9 @@ int device_notify_pincode(struct btd_device *device, gboolean secure,
>>                                                          const char *pincode);
>>   void device_cancel_authentication(struct btd_device *device, gboolean aborted);
>>   gboolean device_is_authenticating(struct btd_device *device);
>> +
>> +void device_cancel_browse(struct btd_device *device, uint8_t bdaddr_type);
>> +
>>   void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type,
>>                                                          uint32_t flags);
>>   void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
>> @@ -141,6 +145,7 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
>>                                                          uint8_t reason);
>>   void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
>>   bool device_is_disconnecting(struct btd_device *device);
>> +bool device_is_connecting(struct btd_device *device);
>>   void device_set_ltk(struct btd_device *device, const uint8_t val[16],
>>                                  bool central, uint8_t enc_size);
>>   bool btd_device_get_ltk(struct btd_device *device, uint8_t val[16],
>> @@ -170,6 +175,7 @@ guint device_add_disconnect_watch(struct btd_device *device,
>>                                  disconnect_watch watch, void *user_data,
>>                                  GDestroyNotify destroy);
>>   void device_remove_disconnect_watch(struct btd_device *device, guint id);
>> +void device_disconnect_watches_callback(struct btd_device *device);
>>   int device_get_appearance(struct btd_device *device, uint16_t *value);
>>   void device_set_appearance(struct btd_device *device, uint16_t value);
>>
>> @@ -184,6 +190,9 @@ void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
>>
>>   int device_connect_le(struct btd_device *dev);
>>
>> +DBusMessage *device_connect_profiles(struct btd_device *dev,
>> +               uint8_t bdaddr_type, DBusMessage *msg, const char *uuid);
>> +
>>   typedef void (*device_svc_cb_t) (struct btd_device *dev, int err,
>>                                                          void *user_data);
>>
>> @@ -225,3 +234,11 @@ void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval,
>>   void btd_device_foreach_service_data(struct btd_device *dev,
>>                                          bt_device_ad_func_t func,
>>                                          void *data);
>> +
>> +typedef void (*btd_device_service_func_t)(struct btd_service *service,
>> +                                       void *user_data);
>> +void btd_device_foreach_service(struct btd_device *dev,
>> +                               btd_device_service_func_t func,
>> +                               void *user_data);
>> +void device_remove_pending_services(struct btd_device *dev,
>> +                                       uint8_t bdaddr_type);
>> \ No newline at end of file
>>
>> ---
>> base-commit: 5e41d1e1d361e7288964e4c2c5ed90736025662f
>> change-id: 20251111-bearer-impl-5f7e396174aa
>>
>> Best regards,
>> --
>> Ye He <ye.he@amlogic.com>
>>
>>
>>
>>
>> --
>> Luiz Augusto von Dentz
>
>
> --
> Luiz Augusto von Dentz

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

end of thread, other threads:[~2025-11-21  2:00 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-18  9:29 [PATCH bluez v3] bearer: Implement Connect/Disconnect methods Ye He via B4 Relay
2025-11-18 10:23 ` [bluez,v3] " bluez.test.bot
2025-11-18 14:14 ` [PATCH bluez v3] " Luiz Augusto von Dentz
2025-11-19  3:26   ` Ye He
     [not found]   ` <09087c54-4d27-460e-a6cb-ac74833068b6@amlogic.com>
2025-11-20 16:22     ` Luiz Augusto von Dentz
2025-11-21  1:59       ` Ye He
2025-11-20 16:24     ` Luiz Augusto von Dentz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).