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
next prev 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