Linux bluetooth development
 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 v2] asha: Defer BR/EDR inquiry while streaming
Date: Sat, 07 Nov 2026 01:49:08 +0000	[thread overview]
Message-ID: <20261107014841.1452698-1-cochcoder@proton.me> (raw)
In-Reply-To: <20261028133243.262314-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
---
v2: Consolidate scan selection and remove periodic polling. Release the
stream count on START queue and callback failures. Clarify Busy behavior.

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

The resend passed all 12 archived CI checks. Local mocked tests exercise
discovery and START failures; native MGMT/client arbitration and isolated
hardware validation remain. Full local make check is blocked by an SDP
debug-symbol linker failure also reproduced on unchanged upstream.


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

diff --git a/profiles/audio/asha.c b/profiles/audio/asha.c
index 8e625fdca3..59a0247b37 100644
--- a/profiles/audio/asha.c
+++ b/profiles/audio/asha.c
@@ -60,8 +60,35 @@ 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 +118,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 +135,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 +202,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 +224,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 +407,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..74fdfb41c4 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; retain accepted 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  1:49 UTC|newest]

Thread overview: 7+ 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 ` Kyle Pepper [this message]
2026-11-07  2:32   ` [RFC,v2] " bluez.test.bot
2026-11-07  2:47   ` [RFC PATCH v3] " Kyle Pepper
2026-11-07  8:58     ` [RFC,v3] " bluez.test.bot
2026-11-10 21:30     ` [RFC PATCH v3] " Luiz Augusto von Dentz

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=20261107014841.1452698-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox