* [PATCH 0/3] Bluetooth: Fix SCO setup failures after an abandoned setup
@ 2026-10-07 15:43 Hitalo Souza
2026-10-07 15:43 ` [PATCH 1/3] Bluetooth: hci_event: Don't fail a connected SCO link on setup errors Hitalo Souza
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Hitalo Souza @ 2026-10-07 15:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: marcel, luiz.dentz, linux-kernel, Hitalo Souza
Since commit a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for
aborting connections"), closing a SCO socket while its setup is pending
deletes the hci_conn, after a Create Connection Cancel that cannot
cancel a synchronous connection. When a new socket connects right away,
e.g. PipeWire recreating the HFP transport after a codec negotiation as
in bluez/bluez#2562, the abandoned setup can still complete, and:
1. It is matched to the new connection by address, the controller then
rejects the new connection's own setup, and the rejection handler
fails the first link on the ACL, which is now the connected one,
without disconnecting it (patch 1).
2. It is matched to the new connection before that connection's own
setup was sent: Enhanced Setup Synchronous Connection goes through
the cmd_sync queue, and any setup waits for the ACL to leave sniff
mode. The setup is then sent anyway and rejected. This is the order
of events in the report (patch 2).
3. Nothing takes it over, it completes while its abort is still
queued, or it is a second link for a connection that is already up:
the link stays up in the controller with no connection for it, and
the controller rejects every further setup for the device until the
ACL drops (patch 3).
In 1 and 2 the new connection keeps the link of the abandoned setup,
with the parameters of that setup: the completion event does not say
which setup it belongs to. Its Air_Mode would show a change between
CVSD and transparent data, but the series does not act on it. An
abandoned setup that fails after a new connection was made is likewise
applied to that connection. Before a13f316e90fd both happened in
another way, as the next socket reused the pending hci_conn.
With the disable_esco parameter of sco.c, a SCO_LINK connection can get
an eSCO link, which it does not take: its connect times out and the
link stays up until the ACL drops. That predates a13f316e90fd and is
not changed here; patch 3 only makes sure such a link is not
disconnected while the connection waits for it.
Patch 2 relies on hci_enhanced_setup_sync() taking hdev->lock, since
024e05f73a4c ("Bluetooth: hci_conn: Lock parent access during enhanced
SCO setup"); a backport to a kernel without that commit needs the lock
taken around the new checks.
Testing:
- QEMU/KVM, bluetooth-next at c85976511aa9 and with each patch applied
in turn, x86_64 defconfig + kvm_guest.config + BT=y, BT_HCIVHCI=y. A
userspace program emulates the controller and the headset over
hci_vhci; the controller allows one eSCO link per ACL and rejects
further setups with 0x0a (0x12 in T1, as in the report), except in
T5, where it allows a second one. Each case was run over Setup
Synchronous Connection (L), Enhanced Setup Synchronous Connection (E)
and Add SCO Connection (A, controller without eSCO):
T0 connect, receive SCO data, close
T1 close a socket with its setup pending, connect a new one; the
first setup completes, then the new one is rejected
T1b like T1, but the first setup completes before the new one is
sent, which waits for the ACL to leave sniff mode
T1c like T1b, but the new setup waits in the cmd_sync queue (E only)
T2 close a socket with its setup pending, the setup completes
afterwards, connect a new socket (cancel answered 0x0b and 0x02)
T2b like T2, but the setup completes while the abort still waits in
the cmd_sync queue
T3 a setup rejected with 0x0d in Command Status
T4 like T2, but the abandoned setup completes with 0x22
T5 like T1, but the new setup completes too, with a second link
T6 disable_esco set, the link comes up as eSCO (L and E): no
Disconnect while the SCO connection waits for it (the connect
still times out, with and without the series)
In T1c the cmd_sync queue waits for a Read RSSI (from mgmt Get
Connection Information) that the emulator leaves unanswered; in T2b
it waits for an ACL connection to another device, whose Connection
Complete the emulator holds as during a page.
T0 T1 T1b T1c T2 T2b T3 T4 T5 T6
bluetooth-next pass FAIL FAIL FAIL FAIL FAIL pass pass FAIL pass
+ patch 1 pass pass FAIL FAIL FAIL FAIL pass pass FAIL pass
+ patches 1-2 pass pass (*) pass FAIL FAIL pass pass FAIL pass
+ patches 1-3 pass pass pass pass pass pass pass pass pass pass
The results are the same on L, E and A, except (*): pass on L and E,
FAIL on A, where the abandoned link is not taken over by a connection
that has not started its setup, and stays up until patch 3.
Without the series, the new socket gets EINVAL in T1 (the emulator
rejects with 0x12) and EMLINK in T1b and T1c (its duplicate setup is
rejected with 0x0a), the next connect gets EMLINK in T2 and T2b, and
the second link in T5 is never disconnected. With it, the new sockets
receive SCO data and no Create Connection Cancel is sent; the links
that nothing takes over (T1b on A, T2, T2b) and the second link in T5
are disconnected as soon as they complete, and T4 and T6 send no
Disconnect. No kernel warnings.
- BlueZ tools/sco-tester (master f8f352d) in the same VM, on the
debug kernel below plus CRYPTO_USER_API_{HASH,SKCIPHER}: 30 of 30
pass with and without the series.
- Real hardware: a MediaTek MT7921 (USB 04ca:3802, legacy Setup
Synchronous Connection because of
HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN) with a Sony WF-1000XM6, on
v7.1.13 with the series built as the bluetooth module (patch 2 needs
one change there, as hci_enhanced_setup_sync() does not take
hdev->lock in that version; the other functions touched are the same
as in bluetooth-next). With PipeWire 1.6.8 the abandoned setups were
first seen there: Create Connection Cancel was answered with Unknown
Connection Identifier, and the setup still completed afterwards.
A SCO socket was closed 150 ms into its setup and a new one connected
0.3 s or 2.5 s later, five times; each abandoned setup completed about
0.2 s after it started, before the new connect. Without the series the
first abandoned link after each ACL connect (two in the run) was left
up, and from then on the controller rejected every setup, plain
connects included, with Unsupported LMP Parameter Value (0x20) until
the ACL was dropped. With the series no Create Connection Cancel was
sent, each abandoned link was disconnected as soon as it completed,
and every new socket and the plain connects after them received SCO
data. An abandoned setup that failed (the headset rejects CVSD with
0x20) caused no Disconnect. Between the groups PipeWire switched the
headset from A2DP to HFP (mSBC) for a recording while I counted
aloud: speech was recorded all three times, with and without the
series. The cases of patches 1 and 2 did not occur there.
- W=1 builds of net/bluetooth/ and drivers/bluetooth/, no warnings:
GCC 16.2.1 x86_64 allmodconfig and i386 defconfig, LLVM 22.1.8 arm64
defconfig, arm multi_v7_defconfig, riscv defconfig and powerpc
ppc64_defconfig (big-endian), with BT, BT_HCIBTUSB{,_MTK,_QCOM} and
BT_VIRTIO.
- sparse v0.6.5-rc1 on the same directories: the same 16 reports with
and without the series, none on the lines touched here.
- With the series, the same tests on a kernel with PROVE_LOCKING,
PROVE_RCU, KASAN, DEBUG_ATOMIC_SLEEP, DEBUG_LIST and
DEBUG_OBJECTS{,_WORK,_TIMERS} enabled: all pass on the three paths,
with no lockdep, KASAN or debugobjects reports.
The patches, the commit messages, this letter and the test programs
were prepared with an AI assistant (see the Assisted-by tags), which
reproduced the report in the emulator and wrote and tested the fixes;
the real-hardware runs were done on my laptop with my headset.
Hitalo Souza (3):
Bluetooth: hci_event: Don't fail a connected SCO link on setup errors
Bluetooth: hci_conn: Don't set up a SCO link that is already up
Bluetooth: Don't leave abandoned SCO links up in the controller
net/bluetooth/hci_conn.c | 24 ++++++++++-
net/bluetooth/hci_event.c | 85 ++++++++++++++++++++++++++++++++++-----
net/bluetooth/hci_sync.c | 8 ++++
3 files changed, 104 insertions(+), 13 deletions(-)
base-commit: c85976511aa95b5ba68b57b0b3c87c67f3cedcce
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 1/3] Bluetooth: hci_event: Don't fail a connected SCO link on setup errors
2026-10-07 15:43 [PATCH 0/3] Bluetooth: Fix SCO setup failures after an abandoned setup Hitalo Souza
@ 2026-10-07 15:43 ` Hitalo Souza
2026-10-07 15:43 ` [PATCH 2/3] Bluetooth: hci_conn: Don't set up a SCO link that is already up Hitalo Souza
2026-10-07 15:43 ` [PATCH 3/3] Bluetooth: Don't leave abandoned SCO links up in the controller Hitalo Souza
2 siblings, 0 replies; 5+ messages in thread
From: Hitalo Souza @ 2026-10-07 15:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: marcel, luiz.dentz, linux-kernel, Hitalo Souza
When Add SCO Connection, Setup Synchronous Connection or Enhanced Setup
Synchronous Connection fails in Command Status, the error handlers fail
the first link on the ACL, whatever its state. If that link is already
up, it is deleted while the controller keeps it: its socket gets an
error and, as no Disconnect is sent, later setups to the device are
rejected until the ACL drops.
A link that is up can be the first one on the ACL since commit
a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting
connections"). Aborting a pending SCO/eSCO setup, e.g. because its
socket was closed, now deletes its hci_conn, so a socket that connects
right after gets a new one and sends its own setup. The abandoned setup
can still complete, and it is then matched to the new connection by
address. When the controller rejects the new setup because a link
already exists, the handler fails the connection that just came up.
This was reported with an HFP headset, where the rejection was Invalid
HCI Command Parameters and the microphone stayed silent.
Only fail a link that is still waiting for its setup to complete.
Link: https://github.com/bluez/bluez/issues/2562
Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
Assisted-by: LLM
Signed-off-by: Hitalo Souza <enghitalo@gmail.com>
---
net/bluetooth/hci_event.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index c055d16cf..cbe53e19e 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2400,13 +2400,19 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status)
acl = hci_conn_hash_lookup_handle(hdev, handle);
if (acl) {
- link = list_first_entry_or_null(&acl->link_list,
- struct hci_link, list);
- if (link && link->conn) {
+ /* Only a link still waiting for its setup can be the one the
+ * failed command was for: one that is already up must be kept.
+ */
+ list_for_each_entry(link, &acl->link_list, list) {
+ if (link->conn->state != BT_CONNECT ||
+ !HCI_CONN_HANDLE_UNSET(link->conn->handle))
+ continue;
+
link->conn->state = BT_CLOSED;
hci_connect_cfm(link->conn, status);
hci_conn_del(link->conn);
+ break;
}
}
@@ -2683,13 +2689,19 @@ static void hci_setup_sync_conn_status(struct hci_dev *hdev, __u16 handle,
acl = hci_conn_hash_lookup_handle(hdev, handle);
if (acl) {
- link = list_first_entry_or_null(&acl->link_list,
- struct hci_link, list);
- if (link && link->conn) {
+ /* Only a link still waiting for its setup can be the one the
+ * failed command was for: one that is already up must be kept.
+ */
+ list_for_each_entry(link, &acl->link_list, list) {
+ if (link->conn->state != BT_CONNECT ||
+ !HCI_CONN_HANDLE_UNSET(link->conn->handle))
+ continue;
+
link->conn->state = BT_CLOSED;
hci_connect_cfm(link->conn, status);
hci_conn_del(link->conn);
+ break;
}
}
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/3] Bluetooth: hci_conn: Don't set up a SCO link that is already up
2026-10-07 15:43 [PATCH 0/3] Bluetooth: Fix SCO setup failures after an abandoned setup Hitalo Souza
2026-10-07 15:43 ` [PATCH 1/3] Bluetooth: hci_event: Don't fail a connected SCO link on setup errors Hitalo Souza
@ 2026-10-07 15:43 ` Hitalo Souza
2026-10-07 15:43 ` [PATCH 3/3] Bluetooth: Don't leave abandoned SCO links up in the controller Hitalo Souza
2 siblings, 0 replies; 5+ messages in thread
From: Hitalo Souza @ 2026-10-07 15:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: marcel, luiz.dentz, linux-kernel, Hitalo Souza
Since commit a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for
aborting connections"), a SCO/eSCO setup abandoned while pending can
complete after a new connection to the same device was made, and it is
then matched to that connection by address. The new connection's own
setup may not have been sent yet at that point: Enhanced Setup
Synchronous Connection is sent later from the cmd_sync queue, and any
setup is deferred while the ACL leaves sniff mode. When it runs, it
sets the connection back to BT_CONNECT and sends a second setup, which
the controller rejects since a link already exists. The connection that
is up is then failed by the rejection, or left in BT_CONNECT and later
deleted without a Disconnect when its socket is closed.
This is the order of events in the report: the second Enhanced Setup
Synchronous Connection was sent after the first setup had completed,
and was rejected with Invalid HCI Command Parameters.
Don't send a setup for a connection that already has a handle. In
hci_enhanced_setup_sync(), take hdev->lock before checking that, and
check under it that the connection still exists, as
configure_datapath_sync() runs without the lock.
Closes: https://github.com/bluez/bluez/issues/2562
Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
Assisted-by: LLM
Signed-off-by: Hitalo Souza <enghitalo@gmail.com>
---
net/bluetooth/hci_conn.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 29da3fe2b..399c7db77 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -290,6 +290,22 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
configure_datapath_sync(hdev, &conn->codec);
+ hci_dev_lock(hdev);
+
+ /* configure_datapath_sync() runs without the lock */
+ if (!hci_conn_valid(hdev, conn)) {
+ hci_dev_unlock(hdev);
+ return -ECANCELED;
+ }
+
+ /* The link may have come up while this was queued, see
+ * hci_sco_setup().
+ */
+ if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
+ hci_dev_unlock(hdev);
+ return 0;
+ }
+
conn->state = BT_CONNECT;
conn->out = true;
@@ -302,8 +318,6 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
- hci_dev_lock(hdev);
-
switch (conn->codec.id) {
case BT_CODEC_MSBC:
if (!find_next_esco_param(conn, esco_param_msbc,
@@ -625,6 +639,12 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
if (!link || !link->conn)
return;
+ /* The link may already be up: a setup abandoned while pending can
+ * complete after a new connection was made and be matched to it.
+ */
+ if (!HCI_CONN_HANDLE_UNSET(link->conn->handle))
+ return;
+
BT_DBG("hcon %p", conn);
if (!status) {
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 3/3] Bluetooth: Don't leave abandoned SCO links up in the controller
2026-10-07 15:43 [PATCH 0/3] Bluetooth: Fix SCO setup failures after an abandoned setup Hitalo Souza
2026-10-07 15:43 ` [PATCH 1/3] Bluetooth: hci_event: Don't fail a connected SCO link on setup errors Hitalo Souza
2026-10-07 15:43 ` [PATCH 2/3] Bluetooth: hci_conn: Don't set up a SCO link that is already up Hitalo Souza
@ 2026-10-07 15:43 ` Hitalo Souza
2026-10-07 18:15 ` Luiz Augusto von Dentz
2 siblings, 1 reply; 5+ messages in thread
From: Hitalo Souza @ 2026-10-07 15:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: marcel, luiz.dentz, linux-kernel, Hitalo Souza
Since commit a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for
aborting connections"), aborting a SCO/eSCO connection whose setup is
pending, e.g. because its socket was closed, sends Create Connection
Cancel and deletes the hci_conn. That command cannot cancel a
synchronous connection, controllers just fail it (ACL Connection Already
Exists or Unknown Connection Identifier), and the link may still
complete afterwards. Unless another connection to the device takes it
over, its completion event then finds no connection waiting for it and
is ignored; if it comes while the abort is still queued, the connection
refuses the handle as it is being aborted and is deleted all the same.
Either way the link stays up in the controller, which rejects every
later setup for the device until the ACL drops (with Unsupported LMP
Parameter Value on a MediaTek MT7921). The same happens to a second
link completing for a connection that is already up, e.g. its own setup
after it took over the abandoned one.
There is no command to cancel a pending SCO/eSCO setup, so don't send
Create Connection Cancel for one, and disconnect a SCO/eSCO link that
completes with no connection to take it.
With the disable_esco parameter of sco.c, a SCO_LINK connection can get
an eSCO link, which it does not take: its connect times out and the link
stays up until the ACL drops. That is not changed here, and such a link
is not disconnected while a SCO_LINK connection waits for its link.
Link: https://github.com/bluez/bluez/issues/2562
Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
Assisted-by: LLM
Signed-off-by: Hitalo Souza <enghitalo@gmail.com>
---
net/bluetooth/hci_event.c | 61 +++++++++++++++++++++++++++++++++++----
net/bluetooth/hci_sync.c | 8 +++++
2 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index cbe53e19e..102a0a4d1 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3217,6 +3217,27 @@ static int hci_read_enc_key_size(struct hci_dev *hdev, struct hci_conn *conn)
return hci_send_cmd(hdev, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
}
+/* A SCO/eSCO link that completes with no connection to take it, e.g.
+ * because its setup was abandoned while pending, must be disconnected:
+ * otherwise it stays up in the controller, which then rejects every further
+ * setup for the device.
+ */
+static void hci_sco_disconnect_orphan(struct hci_dev *hdev, __le16 handle)
+{
+ struct hci_cp_disconnect cp;
+ u16 h = __le16_to_cpu(handle);
+
+ /* Never for an invalid handle or one that a connection uses */
+ if (h > HCI_CONN_HANDLE_MAX || hci_conn_hash_lookup_handle(hdev, h))
+ return;
+
+ bt_dev_dbg(hdev, "handle 0x%4.4x", h);
+
+ cp.handle = handle;
+ cp.reason = HCI_ERROR_REMOTE_USER_TERM;
+ hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
+}
+
static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@@ -3273,8 +3294,10 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK,
&ev->bdaddr);
- if (!conn)
+ if (!conn) {
+ hci_sco_disconnect_orphan(hdev, ev->handle);
goto unlock;
+ }
conn->type = SCO_LINK;
}
@@ -3293,8 +3316,12 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
if (!status) {
status = hci_conn_set_handle(conn, __le16_to_cpu(ev->handle));
- if (status)
+ if (status) {
+ /* e.g. it is being aborted: the link is not taken */
+ if (ev->link_type == SCO_LINK)
+ hci_sco_disconnect_orphan(hdev, ev->handle);
goto done;
+ }
if (conn->type == ACL_LINK) {
conn->state = BT_CONFIG;
@@ -5178,8 +5205,18 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
if (!conn) {
- if (ev->link_type == ESCO_LINK)
- goto unlock;
+ if (ev->link_type == ESCO_LINK) {
+ /* A SCO_LINK connection still waiting for its link can
+ * get an eSCO one, see disable_esco in sco.c. It does
+ * not take it, as before, and the link is left alone.
+ */
+ conn = hci_conn_hash_lookup_ba(hdev, SCO_LINK,
+ &ev->bdaddr);
+ if (conn && HCI_CONN_HANDLE_UNSET(conn->handle))
+ goto unlock;
+
+ goto orphan;
+ }
/* When the link type in the event indicates SCO connection
* and lookup of the connection object fails, then check
@@ -5192,7 +5229,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
*/
conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr);
if (!conn)
- goto unlock;
+ goto orphan;
}
/* The HCI_Synchronous_Connection_Complete event is only sent once per connection.
@@ -5202,6 +5239,13 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
* whether the connection is already set up.
*/
if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
+ /* Another link for a connection that is already up, e.g. its
+ * own setup completing after it took over an abandoned one,
+ * has no connection waiting for it either.
+ */
+ if (__le16_to_cpu(ev->handle) != conn->handle)
+ goto orphan;
+
bt_dev_err(hdev, "Ignoring HCI_Sync_Conn_Complete event for existing connection");
goto unlock;
}
@@ -5210,6 +5254,8 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
case 0x00:
status = hci_conn_set_handle(conn, __le16_to_cpu(ev->handle));
if (status) {
+ /* e.g. it is being aborted: the link is not taken */
+ hci_sco_disconnect_orphan(hdev, ev->handle);
conn->state = BT_CLOSED;
break;
}
@@ -5260,6 +5306,11 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
hci_connect_cfm(conn, status);
if (status)
hci_conn_del(conn);
+ goto unlock;
+
+orphan:
+ if (!status)
+ hci_sco_disconnect_orphan(hdev, ev->handle);
unlock:
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 9eca6757d..4e1c72fd2 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5940,6 +5940,14 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
return 0;
}
+ if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
+ /* There is no command to cancel a pending SCO/eSCO setup. If
+ * the link completes anyway, it is disconnected then, unless
+ * a new connection to the device takes it.
+ */
+ return 0;
+ }
+
if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return 0;
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH 3/3] Bluetooth: Don't leave abandoned SCO links up in the controller
2026-10-07 15:43 ` [PATCH 3/3] Bluetooth: Don't leave abandoned SCO links up in the controller Hitalo Souza
@ 2026-10-07 18:15 ` Luiz Augusto von Dentz
0 siblings, 0 replies; 5+ messages in thread
From: Luiz Augusto von Dentz @ 2026-10-07 18:15 UTC (permalink / raw)
To: Hitalo Souza; +Cc: linux-bluetooth, marcel, linux-kernel
Hi Hitalo,
On Wed, Oct 7, 2026 at 11:44 AM Hitalo Souza <enghitalo@gmail.com> wrote:
>
> Since commit a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for
> aborting connections"), aborting a SCO/eSCO connection whose setup is
> pending, e.g. because its socket was closed, sends Create Connection
> Cancel and deletes the hci_conn. That command cannot cancel a
> synchronous connection, controllers just fail it (ACL Connection Already
> Exists or Unknown Connection Identifier), and the link may still
> complete afterwards. Unless another connection to the device takes it
> over, its completion event then finds no connection waiting for it and
> is ignored; if it comes while the abort is still queued, the connection
> refuses the handle as it is being aborted and is deleted all the same.
> Either way the link stays up in the controller, which rejects every
> later setup for the device until the ACL drops (with Unsupported LMP
> Parameter Value on a MediaTek MT7921). The same happens to a second
> link completing for a connection that is already up, e.g. its own setup
> after it took over the abandoned one.
>
> There is no command to cancel a pending SCO/eSCO setup, so don't send
> Create Connection Cancel for one, and disconnect a SCO/eSCO link that
> completes with no connection to take it.
>
> With the disable_esco parameter of sco.c, a SCO_LINK connection can get
> an eSCO link, which it does not take: its connect times out and the link
> stays up until the ACL drops. That is not changed here, and such a link
> is not disconnected while a SCO_LINK connection waits for its link.
>
> Link: https://github.com/bluez/bluez/issues/2562
> Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
> Assisted-by: LLM
> Signed-off-by: Hitalo Souza <enghitalo@gmail.com>
> ---
> net/bluetooth/hci_event.c | 61 +++++++++++++++++++++++++++++++++++----
> net/bluetooth/hci_sync.c | 8 +++++
> 2 files changed, 64 insertions(+), 5 deletions(-)
>
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index cbe53e19e..102a0a4d1 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -3217,6 +3217,27 @@ static int hci_read_enc_key_size(struct hci_dev *hdev, struct hci_conn *conn)
> return hci_send_cmd(hdev, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
> }
>
> +/* A SCO/eSCO link that completes with no connection to take it, e.g.
> + * because its setup was abandoned while pending, must be disconnected:
> + * otherwise it stays up in the controller, which then rejects every further
> + * setup for the device.
> + */
> +static void hci_sco_disconnect_orphan(struct hci_dev *hdev, __le16 handle)
> +{
> + struct hci_cp_disconnect cp;
> + u16 h = __le16_to_cpu(handle);
> +
> + /* Never for an invalid handle or one that a connection uses */
> + if (h > HCI_CONN_HANDLE_MAX || hci_conn_hash_lookup_handle(hdev, h))
> + return;
> +
> + bt_dev_dbg(hdev, "handle 0x%4.4x", h);
> +
> + cp.handle = handle;
> + cp.reason = HCI_ERROR_REMOTE_USER_TERM;
> + hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
> +}
> +
> static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
> struct sk_buff *skb)
> {
> @@ -3273,8 +3294,10 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
>
> conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK,
> &ev->bdaddr);
> - if (!conn)
> + if (!conn) {
> + hci_sco_disconnect_orphan(hdev, ev->handle);
> goto unlock;
> + }
>
> conn->type = SCO_LINK;
> }
> @@ -3293,8 +3316,12 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
>
> if (!status) {
> status = hci_conn_set_handle(conn, __le16_to_cpu(ev->handle));
> - if (status)
> + if (status) {
> + /* e.g. it is being aborted: the link is not taken */
> + if (ev->link_type == SCO_LINK)
> + hci_sco_disconnect_orphan(hdev, ev->handle);
> goto done;
> + }
>
> if (conn->type == ACL_LINK) {
> conn->state = BT_CONFIG;
> @@ -5178,8 +5205,18 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
>
> conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
> if (!conn) {
> - if (ev->link_type == ESCO_LINK)
> - goto unlock;
> + if (ev->link_type == ESCO_LINK) {
> + /* A SCO_LINK connection still waiting for its link can
> + * get an eSCO one, see disable_esco in sco.c. It does
> + * not take it, as before, and the link is left alone.
> + */
> + conn = hci_conn_hash_lookup_ba(hdev, SCO_LINK,
> + &ev->bdaddr);
> + if (conn && HCI_CONN_HANDLE_UNSET(conn->handle))
> + goto unlock;
> +
> + goto orphan;
> + }
>
> /* When the link type in the event indicates SCO connection
> * and lookup of the connection object fails, then check
> @@ -5192,7 +5229,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
> */
> conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr);
> if (!conn)
> - goto unlock;
> + goto orphan;
> }
>
> /* The HCI_Synchronous_Connection_Complete event is only sent once per connection.
> @@ -5202,6 +5239,13 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
> * whether the connection is already set up.
> */
> if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
> + /* Another link for a connection that is already up, e.g. its
> + * own setup completing after it took over an abandoned one,
> + * has no connection waiting for it either.
> + */
> + if (__le16_to_cpu(ev->handle) != conn->handle)
> + goto orphan;
> +
> bt_dev_err(hdev, "Ignoring HCI_Sync_Conn_Complete event for existing connection");
> goto unlock;
> }
> @@ -5210,6 +5254,8 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
> case 0x00:
> status = hci_conn_set_handle(conn, __le16_to_cpu(ev->handle));
> if (status) {
> + /* e.g. it is being aborted: the link is not taken */
> + hci_sco_disconnect_orphan(hdev, ev->handle);
> conn->state = BT_CLOSED;
> break;
> }
> @@ -5260,6 +5306,11 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
> hci_connect_cfm(conn, status);
> if (status)
> hci_conn_del(conn);
> + goto unlock;
> +
> +orphan:
> + if (!status)
> + hci_sco_disconnect_orphan(hdev, ev->handle);
>
> unlock:
> hci_dev_unlock(hdev);
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index 9eca6757d..4e1c72fd2 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -5940,6 +5940,14 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
> return 0;
> }
>
> + if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
> + /* There is no command to cancel a pending SCO/eSCO setup. If
> + * the link completes anyway, it is disconnected then, unless
> + * a new connection to the device takes it.
> + */
> + return 0;
> + }
> +
> if (hdev->hci_ver < BLUETOOTH_VER_1_2)
> return 0;
>
> --
> 2.55.0
Sashiko flagged quite a few problems:
https://sashiko.dev/#/patchset/20261007154353.148223-1-enghitalo%40gmail.com
We need to check if the logic of hci_sco_disconnect_orphan couldn't be
made more generically, so in case the connection was aborted but we
received the connection complete that shall always result in
HCI_OP_DISCONNECT so the handle don't stay active in the controller.
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-07 18:15 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-07 15:43 [PATCH 0/3] Bluetooth: Fix SCO setup failures after an abandoned setup Hitalo Souza
2026-10-07 15:43 ` [PATCH 1/3] Bluetooth: hci_event: Don't fail a connected SCO link on setup errors Hitalo Souza
2026-10-07 15:43 ` [PATCH 2/3] Bluetooth: hci_conn: Don't set up a SCO link that is already up Hitalo Souza
2026-10-07 15:43 ` [PATCH 3/3] Bluetooth: Don't leave abandoned SCO links up in the controller Hitalo Souza
2026-10-07 18:15 ` 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
all inboxes | Powered by JetHome®