All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH] asha: Defer BR/EDR inquiry while streaming
@ 2026-10-13  3:54 Kyle Pepper
  2026-10-13  4:53 ` [RFC] " bluez.test.bot
  0 siblings, 1 reply; 2+ messages in thread
From: Kyle Pepper @ 2026-10-13  3:54 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Marcel Holtmann, Luiz Augusto von Dentz

Defer BR/EDR discovery while ASHA streams are active on the adapter.
Keep client filters intact and leave LE discovery available.

Reject a new BR/EDR-only discovery request with Busy rather than
leaving its reply pending. Re-evaluate the scan type after START
completes, and leave suspended discovery suspended.

Assisted-by: OpenCode:gpt-6.1-sol
---
Submission review notes (not part of the commit message):
Local validation is for base 7428ca2df9356363079db0f48412c20cbdb9471f.
Compatibility with newer upstream tips has not been verified locally.
October 12 N8/UB500 capture 182750 has successful LE discovery during
streaming, 5342 audio SDUs, no in-stream Inquiry, and BR/EDR Inquiry after
STOP. This tests the combined package. Native MGMT acknowledgments,
cancellation, failure unwind and concurrent-client arbitration remain
review gates. A separate baseline SPA plugin crash occurs at daemon swap;
the capture does not establish the source-level cause of that crash.

 profiles/audio/asha.c |  30 ++++++++++-
 src/adapter.c         | 119 ++++++++++++++++++++++++++++++++++++++++--
 src/adapter.h         |   5 ++
 3 files changed, 147 insertions(+), 7 deletions(-)

diff --git a/profiles/audio/asha.c b/profiles/audio/asha.c
index 8e625fdca3..602da68da4 100644
--- a/profiles/audio/asha.c
+++ b/profiles/audio/asha.c
@@ -60,8 +60,26 @@ struct bt_asha_device {
 	uint16_t imtu, omtu;
 
 	unsigned int resume_id;
+	bool streaming_counted;
 };
 
+/*
+ * Count each device once so inquiry stays deferred until all streams stop.
+ */
+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 char *make_endpoint_path(struct bt_asha_device *asha_dev)
 {
 	char *path;
@@ -91,6 +109,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;
 	}
 
@@ -171,12 +190,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 +212,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 +395,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 d3aa03fb70..28c58106dd 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -302,6 +302,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 */
@@ -1833,6 +1834,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, uint8_t type);
 
 static struct discovery_client *discovery_complete(struct btd_adapter *adapter,
 						uint8_t status)
@@ -1867,6 +1870,7 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
 	struct btd_adapter *adapter = user_data;
 	struct discovery_client *client;
 	const struct mgmt_cp_start_discovery *rp = param;
+	uint8_t type;
 
 	DBG("status 0x%02x", status);
 
@@ -1911,6 +1915,16 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
 
 		discovery_complete(adapter, status);
 
+		/* Audio or filters may change while START is pending. */
+		type = adapter->current_discovery_filter ?
+				adapter->current_discovery_filter->type :
+				get_scan_type(adapter);
+		type = discovery_scan_type(adapter, type);
+
+		if (!adapter->discovery_suspended &&
+						adapter->discovery_type != type)
+			trigger_start_discovery(adapter, 0);
+
 		if (adapter->discovering)
 			return;
 
@@ -1934,23 +1948,60 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
 	trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2);
 }
 
+/*
+ * Defer BR/EDR discovery during ASHA streaming, leaving LE discovery available.
+ * A zero result means the client requested only BR/EDR discovery.
+ */
+static uint8_t discovery_scan_type(struct btd_adapter *adapter, uint8_t type)
+{
+	if ((type & SCAN_TYPE_BREDR) && btd_adapter_le_audio_active(adapter)) {
+		DBG("LE audio active: deferring BR/EDR inquiry");
+		type &= ~SCAN_TYPE_BREDR;
+	}
+
+	return type;
+}
+
+static void defer_discovery(struct btd_adapter *adapter)
+{
+	struct discovery_client *client;
+
+	/* Do not leave a new BR/EDR-only StartDiscovery call unanswered. */
+	client = discovery_complete(adapter, MGMT_STATUS_BUSY);
+	if (client) {
+		discovery_remove(client);
+
+		if (adapter->discovery_list)
+			update_discovery_filter(adapter);
+
+		return;
+	}
+
+	/* Keep accepted sessions until audio releases the adapter. */
+	trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
+}
+
 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, get_scan_type(adapter));
 
 	if (adapter->discovery_enable == 0x01) {
 		struct mgmt_cp_stop_discovery cp;
@@ -1999,6 +2050,12 @@ static bool start_discovery_timeout(gpointer user_data)
 		struct mgmt_cp_start_discovery cp;
 
 		cp.type = new_type;
+
+		if (!cp.type) {
+			defer_discovery(adapter);
+			return FALSE;
+		}
+
 		mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
 					adapter->dev_id, sizeof(cp), &cp,
 					start_discovery_complete, adapter,
@@ -2007,18 +2064,32 @@ static bool start_discovery_timeout(gpointer user_data)
 		return FALSE;
 	}
 
-	/* Filtered discovery is required */
-	sd_cp = adapter->current_discovery_filter;
+	/* Filtered discovery is required. Keep the cached client filter intact;
+	 * it is needed to restore BR/EDR discovery after 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 = discovery_scan_type(adapter, sd_cp->type);
+	if (!sd_cp->type) {
+		free(sd_cp);
+		defer_discovery(adapter);
+		return FALSE;
+	}
 
 	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;
 }
 
@@ -6266,6 +6337,44 @@ struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter)
 	return adapter;
 }
 
+void btd_adapter_le_audio_ref(struct btd_adapter *adapter)
+{
+	if (!adapter)
+		return;
+
+	adapter->le_audio_active++;
+
+	/* First active stream: re-evaluate any running discovery so an
+	 * in-progress BR/EDR inquiry is dropped right away rather than only at
+	 * the next ~5 s re-arm.
+	 */
+	if (adapter->le_audio_active == 1 && adapter->discovery_list &&
+						!adapter->discovery_suspended) {
+		DBG("LE audio active: restarting discovery without BR/EDR");
+		trigger_start_discovery(adapter, 0);
+	}
+}
+
+void btd_adapter_le_audio_unref(struct btd_adapter *adapter)
+{
+	if (!adapter || !adapter->le_audio_active)
+		return;
+
+	adapter->le_audio_active--;
+
+	/* Restore requested discovery after the last stream stops. */
+	if (adapter->le_audio_active == 0 && adapter->discovery_list &&
+						!adapter->discovery_suspended) {
+		DBG("LE audio idle: restoring BR/EDR discovery");
+		trigger_start_discovery(adapter, 0);
+	}
+}
+
+bool btd_adapter_le_audio_active(struct btd_adapter *adapter)
+{
+	return adapter && adapter->le_audio_active > 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..6ad8fb096f 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -119,6 +119,11 @@ 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);
+bool btd_adapter_le_audio_active(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: 7428ca2df9356363079db0f48412c20cbdb9471f


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

* RE: [RFC] asha: Defer BR/EDR inquiry while streaming
  2026-10-13  3:54 [RFC PATCH] asha: Defer BR/EDR inquiry while streaming Kyle Pepper
@ 2026-10-13  4:53 ` bluez.test.bot
  0 siblings, 0 replies; 2+ messages in thread
From: bluez.test.bot @ 2026-10-13  4:53 UTC (permalink / raw)
  To: linux-bluetooth, cochcoder

[-- Attachment #1: Type: text/plain, Size: 3428 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/series/1184362/

---Test result---

Test Summary:
CheckPatch                    PASS      0.48 seconds
GitLint                       PASS      0.23 seconds
BuildEll                      PASS      18.38 seconds
BluezMake                     PASS      243.53 seconds
MakeCheck                     PASS      2.69 seconds
MakeDistcheck                 PASS      125.54 seconds
CheckValgrind                 PASS      134.45 seconds
CheckSmatch                   PASS      215.20 seconds
bluezmakeextell               PASS      82.08 seconds
TestFunctional                FAIL      1054.53 seconds
IncrementalBuild              PASS      246.17 seconds
ScanBuild                     PASS      833.58 seconds

Details
##############################
Test: TestFunctional - FAIL
Desc: Run test-functional
Output:
FAIL functional.test_bluetoothctl::test_bluetoothctl_pair_bredr[hosts15-vm2]: pytest_bluezenv.rpc.RemoteError: <pexpect.popen_spawn.PopenSpawn object at 0x7fac44e56510>
    searcher: searcher_re:
        0: re.compile(b'Changing pairable on succeeded')
    <pexpect.popen_spawn.PopenSpawn object at 0x7fac44e56510>
    searcher: searcher_re:
        0: re.compile(b'Changing pairable on succeeded')
    Remote traceback:
        Traceback (most recent call last):
          File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
            result = method(*msg["a"], **msg["kw"])
          File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 256, in call_plugin
            return getattr(self.plugins[name], method)(*a, **kw)
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
          File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/host_plugins.py", line 502, in expect
            ret = self.ctl.expect(*a, **kw)
          File "/usr/lib/python3/dist-packages/pexpect/spawnbase.py", line 354, in expect
            return self.expect_list(compiled_pattern_list,
                   ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
                    timeout, searchwindowsize, async_)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          File "/usr/lib/python3/dist-packages/pexpect/spawnbase.py", line 383, in expect_list
            return exp.expect_loop(timeout)
                   ~~~~~~~~~~~~~~~^^^^^^^^^
          File "/usr/lib/python3/dist-packages/pexpect/expect.py", line 181, in expect_loop
            return self.timeout(e)
                   ~~~~~~~~~~~~^^^
          File "/usr/lib/python3/dist-packages/pexpect/expect.py", line 144, in timeout
            raise exc
        pexpect.exceptions.TIMEOUT: <pexpect.popen_spawn.PopenSpawn object at 0x7fac44e56510>
        searcher: searcher_re:
            0: re.compile(b'Changing pairable on succeeded')
        <pexpect.popen_spawn.PopenSpawn object at 0x7fac44e56510>
        searcher: searcher_re:
            0: re.compile(b'Changing pairable on succeeded')
FAIL functional.test_hog::test_hog[sci-hosts18-vm2]: pytest_bluezenv.rpc.RemoteTimeoutError: Connection recv timed out


https://github.com/bluez/bluez/pull/2668

---
Regards,
Linux Bluetooth


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

end of thread, other threads:[~2026-10-13  4:53 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13  3:54 [RFC PATCH] asha: Defer BR/EDR inquiry while streaming Kyle Pepper
2026-10-13  4:53 ` [RFC] " bluez.test.bot

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.