All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kyle Pepper <cochcoder@proton.me>
To: linux-bluetooth@vger.kernel.org
Cc: marcel@holtmann.org, luiz.dentz@gmail.com
Subject: [RFC PATCH v3] asha: Defer BR/EDR inquiry while streaming
Date: Sat, 07 Nov 2026 02:47:07 +0000	[thread overview]
Message-ID: <20261107024655.1481992-1-cochcoder@proton.me> (raw)
In-Reply-To: <20261107014841.1452698-1-cochcoder@proton.me>

Defer BR/EDR inquiry while ASHA streams are active. Keep LE discovery
available and preserve client filters for restoration after audio stops.
Return Busy if a new request leaves no permitted scan type. Recheck
pending discovery starts when audio changes.

Release the stream count when START cannot be queued or its callback
reports failure, so a failed acquisition cannot keep inquiry suppressed.

Assisted-by: OpenCode:gpt-6.1-sol
Assisted-by: Codex:gpt-6
---
v3: Wrap the unref call and shorten a comment to meet the
80-column limit. No behavior changes from v2.

Previous version:
https://lore.kernel.org/linux-bluetooth/20261107014841.1452698-1-cochcoder@proton.me/

v2 CI: CheckPatch failed on these two long lines; the other
11 checks passed. Native MGMT/client arbitration and isolated
hardware validation remain open.

 profiles/audio/asha.c | 47 ++++++++++++++++++++++++---
 src/adapter.c         | 75 ++++++++++++++++++++++++++++++++++++++++---
 src/adapter.h         |  4 +++
 3 files changed, 117 insertions(+), 9 deletions(-)

diff --git a/profiles/audio/asha.c b/profiles/audio/asha.c
index 8e625fdca3..d1fcddbcee 100644
--- a/profiles/audio/asha.c
+++ b/profiles/audio/asha.c
@@ -60,8 +60,36 @@ struct bt_asha_device {
 	uint16_t imtu, omtu;
 
 	unsigned int resume_id;
+	bool streaming_counted;
+	bt_asha_cb_t start_cb;
+	void *start_cb_data;
 };
 
+static void asha_set_streaming(struct bt_asha_device *asha_dev, bool streaming)
+{
+	if (streaming == asha_dev->streaming_counted)
+		return;
+
+	if (streaming)
+		btd_adapter_le_audio_ref(device_get_adapter(asha_dev->device));
+	else
+		btd_adapter_le_audio_unref(
+					device_get_adapter(asha_dev->device));
+
+	asha_dev->streaming_counted = streaming;
+}
+
+static void asha_start_cb(int status, void *user_data)
+{
+	struct bt_asha_device *asha_dev = user_data;
+
+	if (status)
+		asha_set_streaming(asha_dev, false);
+
+	if (asha_dev->start_cb)
+		asha_dev->start_cb(status, asha_dev->start_cb_data);
+}
+
 static char *make_endpoint_path(struct bt_asha_device *asha_dev)
 {
 	char *path;
@@ -91,6 +119,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
 
 	if (err) {
 		error("%s", err->message);
+		asha_set_streaming(asha_dev, false);
 		return;
 	}
 
@@ -107,8 +136,11 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
 
 	asha_dev->io = io;
 
-	bt_asha_start(asha_dev->asha, conn_data->cb,
-					conn_data->cb_user_data);
+	asha_dev->start_cb = conn_data->cb;
+	asha_dev->start_cb_data = conn_data->cb_user_data;
+
+	if (bt_asha_start(asha_dev->asha, asha_start_cb, asha_dev))
+		asha_set_streaming(asha_dev, false);
 }
 
 static int asha_connect_socket(struct bt_asha_device *asha_dev,
@@ -171,12 +203,15 @@ unsigned int bt_asha_device_start(struct bt_asha_device *asha_dev,
 
 	if (ret < 0)
 		return 0;
-	else
-		return (++asha_dev->resume_id);
+
+	asha_set_streaming(asha_dev, true);
+	return (++asha_dev->resume_id);
 }
 
 unsigned int bt_asha_device_stop(struct bt_asha_device *asha_dev)
 {
+	asha_set_streaming(asha_dev, false);
+
 	bt_asha_stop(asha_dev->asha);
 
 	if (asha_dev->io) {
@@ -190,6 +225,8 @@ unsigned int bt_asha_device_stop(struct bt_asha_device *asha_dev)
 
 void bt_asha_device_state_reset(struct bt_asha_device *asha_dev)
 {
+	asha_set_streaming(asha_dev, false);
+
 	if (asha_dev->io) {
 		g_io_channel_shutdown(asha_dev->io, TRUE, NULL);
 		g_io_channel_unref(asha_dev->io);
@@ -371,6 +408,8 @@ static void asha_source_device_remove(struct btd_service *service)
 		return;
 	}
 
+	asha_set_streaming(asha_dev, false);
+
 	bt_asha_free(asha_dev->asha);
 	g_free(asha_dev);
 }
diff --git a/src/adapter.c b/src/adapter.c
index 6588a6e2bd..16a0920923 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -304,6 +304,7 @@ struct btd_adapter {
 					 * without delay */
 	uint8_t discovery_type;		/* current active discovery type */
 	uint8_t discovery_enable;	/* discovery enabled/disabled */
+	unsigned int le_audio_active;	/* Active ASHA streams */
 	bool discovery_suspended;	/* discovery has been suspended */
 	bool discovery_discoverable;	/* discoverable while discovering */
 	GSList *discovery_list;		/* list of discovery clients */
@@ -1886,6 +1887,8 @@ static void discovery_remove(struct discovery_client *client)
 }
 
 static void trigger_start_discovery(struct btd_adapter *adapter, guint delay);
+static int update_discovery_filter(struct btd_adapter *adapter);
+static uint8_t discovery_scan_type(struct btd_adapter *adapter);
 
 static struct discovery_client *discovery_complete(struct btd_adapter *adapter,
 						uint8_t status)
@@ -1964,6 +1967,11 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
 
 		discovery_complete(adapter, status);
 
+		/* Audio or filters may change while START is pending. */
+		if (!adapter->discovery_suspended &&
+		    adapter->discovery_type != discovery_scan_type(adapter))
+			trigger_start_discovery(adapter, 0);
+
 		if (adapter->discovering)
 			return;
 
@@ -1987,23 +1995,39 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
 	trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2);
 }
 
+static uint8_t discovery_scan_type(struct btd_adapter *adapter)
+{
+	uint8_t type = adapter->current_discovery_filter ?
+				adapter->current_discovery_filter->type :
+				get_scan_type(adapter);
+
+	if (adapter->le_audio_active)
+		type &= ~SCAN_TYPE_BREDR;
+
+	return type;
+}
+
 static bool start_discovery_timeout(gpointer user_data)
 {
 	struct btd_adapter *adapter = user_data;
 	struct mgmt_cp_start_service_discovery *sd_cp;
+	size_t sd_len;
 	uint8_t new_type;
 
 	DBG("");
 
 	adapter->discovery_idle_timeout = 0;
 
+	if (!adapter->discovery_list || adapter->discovery_suspended)
+		return FALSE;
+
 	/* If we're doing filtered discovery, it must be quickly restarted */
 	adapter->no_scan_restart_delay = !!adapter->current_discovery_filter;
 
 	DBG("adapter->current_discovery_filter == %d",
 	    !!adapter->current_discovery_filter);
 
-	new_type = get_scan_type(adapter);
+	new_type = discovery_scan_type(adapter);
 
 	if (adapter->discovery_enable == 0x01) {
 		struct mgmt_cp_stop_discovery cp;
@@ -2047,11 +2071,27 @@ static bool start_discovery_timeout(gpointer user_data)
 
 	}
 
+	if (!new_type) {
+		struct discovery_client *client;
+
+		/* Reject new requests; keep sessions until audio stops. */
+		client = discovery_complete(adapter, MGMT_STATUS_BUSY);
+		if (client) {
+			discovery_remove(client);
+
+			if (adapter->discovery_list)
+				update_discovery_filter(adapter);
+		}
+
+		return FALSE;
+	}
+
 	/* Regular discovery is required */
 	if (!adapter->current_discovery_filter) {
 		struct mgmt_cp_start_discovery cp;
 
 		cp.type = new_type;
+
 		mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
 					adapter->dev_id, sizeof(cp), &cp,
 					start_discovery_complete, adapter,
@@ -2060,18 +2100,25 @@ static bool start_discovery_timeout(gpointer user_data)
 		return FALSE;
 	}
 
-	/* Filtered discovery is required */
-	sd_cp = adapter->current_discovery_filter;
+	/* Keep the requested filter intact for when audio stops. */
+	sd_len = sizeof(*sd_cp) +
+		btohs(adapter->current_discovery_filter->uuid_count) * 16;
+	sd_cp = util_memdup(adapter->current_discovery_filter, sd_len);
+	if (!sd_cp)
+		return FALSE;
+
+	sd_cp->type = new_type;
 
 	DBG("sending MGMT_OP_START_SERVICE_DISCOVERY %d, %d, %d",
 				sd_cp->rssi, sd_cp->type,
 				btohs(sd_cp->uuid_count));
 
 	mgmt_send(adapter->mgmt, MGMT_OP_START_SERVICE_DISCOVERY,
-		  adapter->dev_id, sizeof(*sd_cp) +
-		  btohs(sd_cp->uuid_count) * 16,
+		  adapter->dev_id, sd_len,
 		  sd_cp, start_discovery_complete, adapter, NULL);
 
+	free(sd_cp);
+
 	return FALSE;
 }
 
@@ -6358,6 +6405,24 @@ struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter)
 	return adapter;
 }
 
+void btd_adapter_le_audio_ref(struct btd_adapter *adapter)
+{
+	if (adapter->le_audio_active++)
+		return;
+
+	if (adapter->discovery_list && !adapter->discovery_suspended)
+		trigger_start_discovery(adapter, 0);
+}
+
+void btd_adapter_le_audio_unref(struct btd_adapter *adapter)
+{
+	if (!adapter->le_audio_active || --adapter->le_audio_active)
+		return;
+
+	if (adapter->discovery_list && !adapter->discovery_suspended)
+		trigger_start_discovery(adapter, 0);
+}
+
 void btd_adapter_unref(struct btd_adapter *adapter)
 {
 	if (__sync_sub_and_fetch(&adapter->ref_count, 1))
diff --git a/src/adapter.h b/src/adapter.h
index 583168f4bc..d5cda2d96b 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -119,6 +119,10 @@ bool btd_adapter_uuid_is_allowed(struct btd_adapter *adapter, const char *uuid);
 struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter);
 void btd_adapter_unref(struct btd_adapter *adapter);
 
+/* Defer BR/EDR discovery while ASHA streams are active. */
+void btd_adapter_le_audio_ref(struct btd_adapter *adapter);
+void btd_adapter_le_audio_unref(struct btd_adapter *adapter);
+
 void btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
 							uint8_t minor);
 int btd_adapter_set_name(struct btd_adapter *adapter, const char *name);

base-commit: a8067198717b38391ca183ed7f41a69ec4b81b6e
-- 
2.56.0



  parent reply	other threads:[~2026-11-07  2:47 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28 13:32 [RFC PATCH RESEND] asha: Defer BR/EDR inquiry while streaming Kyle Pepper
2026-10-28 16:24 ` [RFC,RESEND] " bluez.test.bot
2026-11-07  1:49 ` [RFC PATCH v2] " Kyle Pepper
2026-11-07  2:32   ` [RFC,v2] " bluez.test.bot
2026-11-07  2:47   ` Kyle Pepper [this message]
2026-11-07  8:58     ` [RFC,v3] " bluez.test.bot
2026-11-10 21:30     ` [RFC PATCH v3] " Luiz Augusto von Dentz
2026-11-12 21:42       ` cochcoder

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261107024655.1481992-1-cochcoder@proton.me \
    --to=cochcoder@proton.me \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    --cc=marcel@holtmann.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.