From 8ec03dcf98f985c5c9b512d7e8d04911bce1cf21 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sun, 14 Jun 2026 17:12:58 +0300 Subject: [PATCH 01/84] Bluetooth: btusb: Add new VID/PID 0x0489/0xe156 for MT7902 Add VID 0489 & PID e156 for MediaTek MT7902 USB Bluetooth chip. The information in /sys/kernel/debug/usb/devices about the Bluetooth device is listed as the below. T: Bus=01 Lev=01 Prnt=01 Port=09 Cnt=05 Dev#= 6 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e156 Rev= 1.00 S: Manufacturer=MediaTek Inc. S: Product=Wireless_Device S: SerialNumber=000000000 C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01 I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms I: If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us I:* If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=8a(I) Atr=03(Int.) MxPS= 512 Ivl=125us E: Ad=0a(O) Atr=03(Int.) MxPS= 512 Ivl=125us Co-developed-by: Kirill Shubin Signed-off-by: Kirill Shubin Signed-off-by: Sean Wang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 184e95c1625e..915623b4b4ec 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -679,6 +679,8 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x13d3, 0x3606), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, /* MediaTek MT7902 Bluetooth devices */ + { USB_DEVICE(0x0489, 0xe156), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0e8d, 0x1ede), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3579), .driver_info = BTUSB_MEDIATEK | From 252862a031d90e89e706d17107b3a72fafbd84fd Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 14 Jun 2026 13:27:02 +0300 Subject: [PATCH 02/84] Bluetooth: af_bluetooth: Add minimal context analysis annotations Add minimal compiler context analysis annotations, required for compilation to pass. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/af_bluetooth.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index a2290ffdc2c1..411d66f24393 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -209,6 +209,7 @@ bool bt_sock_linked(struct bt_sock_list *l, struct sock *s) EXPORT_SYMBOL(bt_sock_linked); void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh) + __context_unsafe(/* conditional locking */) { const struct cred *old_cred; struct pid *old_pid; @@ -815,7 +816,8 @@ EXPORT_SYMBOL(bt_sock_wait_ready); #ifdef CONFIG_PROC_FS static void *bt_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(seq->private->l->lock) + __acquires_shared(&((struct bt_sock_list *) + pde_data(file_inode(seq->file)))->lock) { struct bt_sock_list *l = pde_data(file_inode(seq->file)); @@ -831,7 +833,8 @@ static void *bt_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void bt_seq_stop(struct seq_file *seq, void *v) - __releases(seq->private->l->lock) + __releases_shared(&((struct bt_sock_list *) + pde_data(file_inode(seq->file)))->lock) { struct bt_sock_list *l = pde_data(file_inode(seq->file)); From 2c704850f1c7c625729e6f7e16db74ffad4b6752 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 14 Jun 2026 13:27:03 +0300 Subject: [PATCH 03/84] Bluetooth: hci_core: Add minimal context analysis annotations Add minimal compiler context analysis annotations, required for compilation to pass. compiler-context-analysis.h doesn't have tools to deal with the conditional SRCU locking on return value used here, so just disable the analysis in places instead of refactoring, in order to not make code changes here. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5ba9fe8261ec..d1e78ae7728e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -62,6 +62,7 @@ static DEFINE_IDA(hci_index_ida); /* Get HCI device by index. * Device is held on return. */ static struct hci_dev *__hci_dev_get(int index, int *srcu_index) + __context_unsafe(/* conditional locking */) { struct hci_dev *hdev = NULL, *d; @@ -89,11 +90,13 @@ struct hci_dev *hci_dev_get(int index) } static struct hci_dev *hci_dev_get_srcu(int index, int *srcu_index) + __context_unsafe(/* conditional locking vs return */) { return __hci_dev_get(index, srcu_index); } static void hci_dev_put_srcu(struct hci_dev *hdev, int srcu_index) + __context_unsafe(/* conditional locking vs return */) { srcu_read_unlock(&hdev->srcu, srcu_index); hci_dev_put(hdev); From 20b9e52c3035aa20d96d44eef70226eef6bd9402 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 14 Jun 2026 13:27:04 +0300 Subject: [PATCH 04/84] Bluetooth: L2CAP: Add minimal context analysis annotations Add minimal compiler context analysis annotations, required for compilation to pass. Don't check complex conn->lock usage in l2cap_sock_shutdown(). The analysis cannot know that chan->conn pointer is never replaced by a different l2cap_conn. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_sock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4058ff50cc27..735167f73f31 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1365,6 +1365,7 @@ static int __l2cap_wait_ack(struct sock *sk, struct l2cap_chan *chan) } static int l2cap_sock_shutdown(struct socket *sock, int how) + __context_unsafe(/* complex chan->conn locking */) { struct sock *sk = sock->sk; struct l2cap_chan *chan; From 6e06750c45cead7a72fffb492099640ad7691dec Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 14 Jun 2026 13:27:05 +0300 Subject: [PATCH 05/84] Bluetooth: RFCOMM: Add minimal context analysis annotations Add minimal compiler context analysis annotations, required for compilation to pass. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/rfcomm/sock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index feb302a491fa..958081adb9b5 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -60,6 +60,7 @@ static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb) } static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) + __must_hold(&d->lock) { struct sock *sk = d->owner, *parent; From e01e99a5ec2143d441c6ca0ce89abf3adb61cc12 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 14 Jun 2026 13:27:06 +0300 Subject: [PATCH 06/84] Bluetooth: enable context analysis Enable compiler context analysis for Bluetooth subsystem and drivers. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/Makefile | 2 ++ net/bluetooth/Makefile | 2 ++ net/bluetooth/bnep/Makefile | 2 ++ net/bluetooth/hidp/Makefile | 2 ++ net/bluetooth/rfcomm/Makefile | 2 ++ 5 files changed, 10 insertions(+) diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index bafc26250b63..e6b1c1180d1d 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -50,3 +50,5 @@ hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o hci_uart-$(CONFIG_BT_HCIUART_AML) += hci_aml.o hci_uart-objs := $(hci_uart-y) + +CONTEXT_ANALYSIS := y diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 41049b280887..ff466ea97436 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -25,3 +25,5 @@ bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o bluetooth-$(CONFIG_BT_AOSPEXT) += aosp.o bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o + +CONTEXT_ANALYSIS := y diff --git a/net/bluetooth/bnep/Makefile b/net/bluetooth/bnep/Makefile index 8af9d56bb012..f42015cc3245 100644 --- a/net/bluetooth/bnep/Makefile +++ b/net/bluetooth/bnep/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_BT_BNEP) += bnep.o bnep-objs := core.o sock.o netdev.o + +CONTEXT_ANALYSIS := y diff --git a/net/bluetooth/hidp/Makefile b/net/bluetooth/hidp/Makefile index f41b0aa02b23..53e139e41bdc 100644 --- a/net/bluetooth/hidp/Makefile +++ b/net/bluetooth/hidp/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_BT_HIDP) += hidp.o hidp-objs := core.o sock.o + +CONTEXT_ANALYSIS := y diff --git a/net/bluetooth/rfcomm/Makefile b/net/bluetooth/rfcomm/Makefile index 593e5c48c131..15f909f40f25 100644 --- a/net/bluetooth/rfcomm/Makefile +++ b/net/bluetooth/rfcomm/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm.o rfcomm-y := core.o sock.o rfcomm-$(CONFIG_BT_RFCOMM_TTY) += tty.o + +CONTEXT_ANALYSIS := y From 288f31431be58d8d8b5f8524a8dd8e2aa2713fc0 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Wed, 17 Jun 2026 18:30:19 +0300 Subject: [PATCH 07/84] Bluetooth: simplify force_no_mitm_write() with kstrtobool_from_user() Simplify 'force_no_mitm_write()' by using the convenient 'kstrtobool_from_user()'. Signed-off-by: Dmitry Antipov Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_debugfs.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 0635e4641db4..aadffaaff20e 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -1161,16 +1161,12 @@ static ssize_t force_no_mitm_write(struct file *file, size_t count, loff_t *ppos) { struct hci_dev *hdev = file->private_data; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf) - 1)); bool enable; + int err; - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - if (kstrtobool(buf, &enable)) - return -EINVAL; + err = kstrtobool_from_user(user_buf, count, &enable); + if (err) + return err; if (enable == hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM)) return -EALREADY; From dc7b9893a3a0dd7a72537d55816fd4eb7e5457f6 Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Mon, 15 Jun 2026 11:33:06 -0400 Subject: [PATCH 08/84] Bluetooth: hci_sync: Remove unused hci_cmd_sync_dequeue_once() hci_cmd_sync_dequeue_once() had a single in-tree caller, hci_cancel_connect_sync(), which now holds cmd_sync_work_lock across the in-flight create flag test and the dequeue and so open-codes the lookup and cancel under that lock. That leaves the exported hci_cmd_sync_dequeue_once() with no in-tree user, so remove it along with its declaration. Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_sync.h | 3 --- net/bluetooth/hci_sync.c | 26 -------------------------- 2 files changed, 29 deletions(-) diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h index 73e494b2591d..818e62d9fe9e 100644 --- a/include/net/bluetooth/hci_sync.h +++ b/include/net/bluetooth/hci_sync.h @@ -84,9 +84,6 @@ void hci_cmd_sync_cancel_entry(struct hci_dev *hdev, struct hci_cmd_sync_work_entry *entry); bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, void *data, hci_cmd_sync_work_destroy_t destroy); -bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev, - hci_cmd_sync_work_func_t func, void *data, - hci_cmd_sync_work_destroy_t destroy); int hci_update_eir_sync(struct hci_dev *hdev); int hci_update_class_sync(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index c8d14128c363..3660120b26a6 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -860,32 +860,6 @@ void hci_cmd_sync_cancel_entry(struct hci_dev *hdev, } EXPORT_SYMBOL(hci_cmd_sync_cancel_entry); -/* Dequeue one HCI command entry: - * - * - Lookup and cancel first entry that matches. - */ -bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev, - hci_cmd_sync_work_func_t func, - void *data, hci_cmd_sync_work_destroy_t destroy) -{ - struct hci_cmd_sync_work_entry *entry; - - mutex_lock(&hdev->cmd_sync_work_lock); - - entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy); - if (!entry) { - mutex_unlock(&hdev->cmd_sync_work_lock); - return false; - } - - _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); - - mutex_unlock(&hdev->cmd_sync_work_lock); - - return true; -} -EXPORT_SYMBOL(hci_cmd_sync_dequeue_once); - /* Dequeue HCI command entry: * * - Lookup and cancel any entry that matches by function callback or data or From 849a3bf1489093996859ba0c3c1c0cd314d6ff96 Mon Sep 17 00:00:00 2001 From: Gustavo Evgucci Date: Thu, 25 Jun 2026 11:32:30 +0300 Subject: [PATCH 09/84] Bluetooth: btusb: Add USB ID 13d3:3625 for MediaTek MT7922 The IMC Networks MT7922 Bluetooth adapter with USB ID 13d3:3625 is not recognized as a MediaTek device because it is missing from the btusb device ID table. As a result, btmtk firmware loading is never triggered and the HCI reset command times out with -ETIMEDOUT. Add the device with BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH flags, consistent with the neighboring 13d3:3627, 13d3:3628 and 13d3:3630 entries which use the same chip. Tested on: MediaTek MT7922 (Wi-Fi 6E combo card, IMC Networks BT USB interface), kernel 7.0.11-arch1-1. /sys/kernel/debug/usb/devices: T: Bus=01 Lev=01 Prnt=01 Port=12 Cnt=03 Dev#= 4 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=13d3 ProdID=3625 Rev= 1.00 S: Manufacturer=MediaTek Inc. S: Product=Wireless_Device S: SerialNumber=000000000 C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01 I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms I: If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us I:* If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=8a(I) Atr=03(Int.) MxPS= 512 Ivl=125us E: Ad=0a(O) Atr=03(Int.) MxPS= 512 Ivl=125us Signed-off-by: Gustavo Evgucci Reviewed-by: Paul Menzel Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 915623b4b4ec..fada268a78f2 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -798,6 +798,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3625), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3627), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3628), .driver_info = BTUSB_MEDIATEK | From cf81f0a3db2a5c34ee6e4ea379c631fab6d13e01 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:46 -0700 Subject: [PATCH 10/84] Bluetooth: btqca: Fix qca_set_bdaddr() waiting for wrong HCI event qca_set_bdaddr() waits for HCI_EV_VENDOR when sending EDL_WRITE_BD_ADDR_OPCODE (0xFC14), but the controller responds with Command Complete event as confirmed by btmon on WCN7850: < HCI Command: Vendor (0x3f|0x0014) plen 6 #3 [hci0] 11 22 33 44 55 66 > HCI Event: Command Complete (0x0e) plen 4 #4 [hci0] Vendor (0x3f|0x0014) ncmd 1 Status: Success (0x00) Fix by passing 0 as the event parameter to __hci_cmd_sync_ev() to wait for the command complete event instead. Fixes: 5c0a1001c8be ("Bluetooth: hci_qca: Add helper to set device address") Reviewed-by: Bartosz Golaszewski Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btqca.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 10c496eaea2c..4b0d83858229 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -1029,8 +1029,7 @@ int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) baswap(&bdaddr_swapped, bdaddr); skb = __hci_cmd_sync_ev(hdev, EDL_WRITE_BD_ADDR_OPCODE, 6, - &bdaddr_swapped, HCI_EV_VENDOR, - HCI_INIT_TIMEOUT); + &bdaddr_swapped, 0, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); bt_dev_err(hdev, "QCA Change address cmd failed (%d)", err); From d0b15d812688d3f0f3fe1c4426e12814d0c294dc Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:47 -0700 Subject: [PATCH 11/84] Bluetooth: btusb: Fix BD_ADDR byte order in btusb_set_bdaddr_wcn6855() btusb_set_bdaddr_wcn6855() sends the address without swapping byte order for VSC 0xFC14, but the command expects the address in reversed byte order compared to other HCI commands like HCI_Create_Connection, resulting in a wrong BD_ADDR being set. btmon log on WCN6855 shows VSC 0xFC14 is sent with swapped bytes 11 22 33 44 55 66, and Read BD ADDR returns the expected address 11:22:33:44:55:66: < HCI Command: Vendor (0x3f|0x0014) plen 6 #3 [hci0] 11 22 33 44 55 66 > HCI Event: Command Complete (0x0e) plen 4 #4 [hci0] Vendor (0x3f|0x0014) ncmd 1 Status: Success (0x00) < HCI Command: Read BD ADDR (0x04|0x0009) plen 0 #11 [hci0] > HCI Event: Command Complete (0x0e) plen 10 #12 [hci0] Read BD ADDR (0x04|0x0009) ncmd 1 Status: Success (0x00) Address: 11:22:33:44:55:66 (OUI 11-22-33) Fix by swapping the input address before issuing the command. Fixes: b40f58b97386 ("Bluetooth: btusb: Add Qualcomm Bluetooth SoC WCN6855 support") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index fada268a78f2..d02d3a9950de 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3076,14 +3076,15 @@ static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev, static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev, const bdaddr_t *bdaddr) { + bdaddr_t bdaddr_swapped; struct sk_buff *skb; - u8 buf[6]; long ret; - memcpy(buf, bdaddr, sizeof(bdaddr_t)); + baswap(&bdaddr_swapped, bdaddr); - skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(buf), buf, - HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT); + skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(bdaddr_swapped), + &bdaddr_swapped, HCI_EV_CMD_COMPLETE, + HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { ret = PTR_ERR(skb); bt_dev_err(hdev, "Change address command failed (%ld)", ret); From ff50db7a522e7bd3bd1c4db6da715e4bdb53af97 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:48 -0700 Subject: [PATCH 12/84] Bluetooth: btusb: Record matched usb_device_id into btusb_data Add @match_id to btusb_data to record the matched usb_device_id which will be used later. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index d02d3a9950de..f3eee864ef19 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1014,6 +1014,7 @@ struct btusb_data { bool usb_alt6_packet_flow; int isoc_altsetting; int suspend_count; + const struct usb_device_id *match_id; int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb); int (*recv_acl)(struct hci_dev *hdev, struct sk_buff *skb); @@ -4112,6 +4113,7 @@ static int btusb_probe(struct usb_interface *intf, if (!data) return -ENOMEM; + data->match_id = id; err = usb_find_common_endpoints(intf->cur_altsetting, &data->bulk_rx_ep, &data->bulk_tx_ep, &data->intr_ep, NULL); if (err) From 33c6a8d01889a84cc773c0c20c8323bce84af27d Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:49 -0700 Subject: [PATCH 13/84] Bluetooth: btusb: QCA: Fix populating devcoredump fields on unenabled devices Devcoredump is not enabled for ATH3012 or QCA_ROME, but they unconditionally populate devcoredump fields in btusb_setup_qca(). Fix by populating devcoredump fields only when BTUSB_QCA_WCN6855 is set, which marks the first generation of QCA BT SoCs for which devcoredump is enabled. Fixes: 20981ce2d5a5 ("Bluetooth: btusb: Add WCN6855 devcoredump support") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index f3eee864ef19..2e4847c362b3 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3701,8 +3701,10 @@ static int btusb_setup_qca(struct hci_dev *hdev) if (err) return err; - btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); - btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version); + if (btdata->match_id->driver_info & BTUSB_QCA_WCN6855) { + btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); + btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version); + } if (!(status & QCA_SYSCFG_UPDATED)) { err = btusb_setup_qca_load_nvm(hdev, &ver, info); From 01f9b34e4a2c82d981dfbc17048b03929b9b0380 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:50 -0700 Subject: [PATCH 14/84] Bluetooth: btusb: QCA: move qca_dump out of struct btusb_data 'struct btusb_data' ideally should not include vendor specific fields, but it currently includes the QCA devcoredump member 'struct qca_dump_info qca_dump'. Fix by moving it into hci_dev private area accessed by hci_get_priv(). Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 56 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 2e4847c362b3..78bc3f3adc77 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -941,6 +941,10 @@ struct qca_dump_info { u16 ram_dump_seqno; }; +struct btqca_data { + struct qca_dump_info qca_dump; +}; + #define BTUSB_MAX_ISOC_FRAMES 10 #define BTUSB_INTR_RUNNING 0 @@ -1027,8 +1031,6 @@ struct btusb_data { int (*disconnect)(struct hci_dev *hdev); int oob_wake_irq; /* irq for out-of-band wake-on-bt */ - - struct qca_dump_info qca_dump; }; static void btusb_reset(struct hci_dev *hdev) @@ -3121,14 +3123,15 @@ struct qca_dump_hdr { static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb) { char buf[128]; - struct btusb_data *btdata = hci_get_drvdata(hdev); + struct btqca_data *btqca_data = hci_get_priv(hdev); + struct qca_dump_info *qca_dump_ptr = &btqca_data->qca_dump; snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n", - btdata->qca_dump.controller_id); + qca_dump_ptr->controller_id); skb_put_data(skb, buf, strlen(buf)); snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n", - btdata->qca_dump.fw_version); + qca_dump_ptr->fw_version); skb_put_data(skb, buf, strlen(buf)); snprintf(buf, sizeof(buf), "Driver: %s\nVendor: qca\n", @@ -3136,7 +3139,7 @@ static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb) skb_put_data(skb, buf, strlen(buf)); snprintf(buf, sizeof(buf), "VID: 0x%x\nPID:0x%x\n", - btdata->qca_dump.id_vendor, btdata->qca_dump.id_product); + qca_dump_ptr->id_vendor, qca_dump_ptr->id_product); skb_put_data(skb, buf, strlen(buf)); snprintf(buf, sizeof(buf), "Lmp Subversion: 0x%x\n", @@ -3165,6 +3168,8 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb) struct qca_dump_hdr *dump_hdr; struct btusb_data *btdata = hci_get_drvdata(hdev); + struct btqca_data *btqca_data = hci_get_priv(hdev); + struct qca_dump_info *qca_dump_ptr = &btqca_data->qca_dump; struct usb_device *udev = btdata->udev; pkt_type = hci_skb_pkt_type(skb); @@ -3192,8 +3197,8 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb) goto out; } - btdata->qca_dump.ram_dump_size = dump_size; - btdata->qca_dump.ram_dump_seqno = 0; + qca_dump_ptr->ram_dump_size = dump_size; + qca_dump_ptr->ram_dump_seqno = 0; skb_pull(skb, offsetof(struct qca_dump_hdr, data0)); @@ -3205,29 +3210,29 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb) skb_pull(skb, offsetof(struct qca_dump_hdr, data)); } - if (!btdata->qca_dump.ram_dump_size) { + if (!qca_dump_ptr->ram_dump_size) { ret = -EINVAL; bt_dev_err(hdev, "memdump is not active"); goto out; } - if ((seqno > btdata->qca_dump.ram_dump_seqno + 1) && (seqno != QCA_LAST_SEQUENCE_NUM)) { - dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - btdata->qca_dump.ram_dump_seqno - 1); + if ((seqno > qca_dump_ptr->ram_dump_seqno + 1) && seqno != QCA_LAST_SEQUENCE_NUM) { + dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - qca_dump_ptr->ram_dump_seqno - 1); hci_devcd_append_pattern(hdev, 0x0, dump_size); bt_dev_err(hdev, "expected memdump seqno(%u) is not received(%u)\n", - btdata->qca_dump.ram_dump_seqno, seqno); - btdata->qca_dump.ram_dump_seqno = seqno; + qca_dump_ptr->ram_dump_seqno, seqno); + qca_dump_ptr->ram_dump_seqno = seqno; kfree_skb(skb); return ret; } hci_devcd_append(hdev, skb); - btdata->qca_dump.ram_dump_seqno++; + qca_dump_ptr->ram_dump_seqno++; if (seqno == QCA_LAST_SEQUENCE_NUM) { bt_dev_info(hdev, "memdump done: pkts(%u), total(%u)\n", - btdata->qca_dump.ram_dump_seqno, btdata->qca_dump.ram_dump_size); + qca_dump_ptr->ram_dump_seqno, qca_dump_ptr->ram_dump_size); hci_devcd_complete(hdev); goto out; @@ -3235,10 +3240,10 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb) return ret; out: - if (btdata->qca_dump.ram_dump_size) + if (qca_dump_ptr->ram_dump_size) usb_enable_autosuspend(udev); - btdata->qca_dump.ram_dump_size = 0; - btdata->qca_dump.ram_dump_seqno = 0; + qca_dump_ptr->ram_dump_size = 0; + qca_dump_ptr->ram_dump_seqno = 0; clear_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags); if (ret < 0) @@ -3702,8 +3707,10 @@ static int btusb_setup_qca(struct hci_dev *hdev) return err; if (btdata->match_id->driver_info & BTUSB_QCA_WCN6855) { - btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); - btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version); + struct btqca_data *btqca_data = hci_get_priv(hdev); + + btqca_data->qca_dump.fw_version = le32_to_cpu(ver.patch_version); + btqca_data->qca_dump.controller_id = le32_to_cpu(ver.rom_version); } if (!(status & QCA_SYSCFG_UPDATED)) { @@ -4169,6 +4176,9 @@ static int btusb_probe(struct usb_interface *intf, } else if (id->driver_info & BTUSB_MEDIATEK) { /* Allocate extra space for Mediatek device */ priv_size += sizeof(struct btmtk_data); + } else if (id->driver_info & BTUSB_QCA_WCN6855) { + /* Allocate extra space for QCA WCN6855 device */ + priv_size += sizeof(struct btqca_data); } data->recv_acl = hci_recv_frame; @@ -4311,8 +4321,10 @@ static int btusb_probe(struct usb_interface *intf, } if (id->driver_info & BTUSB_QCA_WCN6855) { - data->qca_dump.id_vendor = id->idVendor; - data->qca_dump.id_product = id->idProduct; + struct btqca_data *btqca_data = hci_get_priv(hdev); + + btqca_data->qca_dump.id_vendor = id->idVendor; + btqca_data->qca_dump.id_product = id->idProduct; data->recv_event = btusb_recv_evt_qca; data->recv_acl = btusb_recv_acl_qca; hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL); From faeaddd353fe8e2299dff417e2018cba0edbf03b Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:51 -0700 Subject: [PATCH 15/84] Bluetooth: hci_sync: Introduce __hci_reset_sync() for device drivers Several vendor drivers have a requirement to send a synchronous raw HCI reset with HCI_INIT_TIMEOUT. Add a dedicated __hci_reset_sync() for them to use. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_sync.h | 1 + net/bluetooth/hci_sync.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h index 818e62d9fe9e..0756d6fe77d4 100644 --- a/include/net/bluetooth/hci_sync.h +++ b/include/net/bluetooth/hci_sync.h @@ -59,6 +59,7 @@ int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen, int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u8 event, u32 timeout, struct sock *sk); +int __hci_reset_sync(struct hci_dev *hdev); int hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 3660120b26a6..00857fc3235b 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -3766,6 +3766,14 @@ int hci_reset_sync(struct hci_dev *hdev) return 0; } +/* Send a raw HCI reset for use by vendor drivers */ +int __hci_reset_sync(struct hci_dev *hdev) +{ + return __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL, + HCI_INIT_TIMEOUT); +} +EXPORT_SYMBOL(__hci_reset_sync); + static int hci_init0_sync(struct hci_dev *hdev) { int err; From 2c0a1aaede9e82b54195756d64ff0eea15387809 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:52 -0700 Subject: [PATCH 16/84] Bluetooth: btqca: Simplify qca_send_reset() by using __hci_reset_sync() qca_send_reset() is functionally equivalent to the newly added __hci_reset_sync(). Drop qca_send_reset() and call __hci_reset_sync() directly. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btqca.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 4b0d83858229..22b08ab05b82 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -190,25 +190,6 @@ static int qca_send_patch_config_cmd(struct hci_dev *hdev) return err; } -static int qca_send_reset(struct hci_dev *hdev) -{ - struct sk_buff *skb; - int err; - - bt_dev_dbg(hdev, "QCA HCI_RESET"); - - skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - bt_dev_err(hdev, "QCA Reset failed (%d)", err); - return err; - } - - kfree_skb(skb); - - return 0; -} - static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid) { u8 cmd; @@ -990,11 +971,12 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, } /* Perform HCI reset */ - err = qca_send_reset(hdev); + err = __hci_reset_sync(hdev); if (err < 0) { bt_dev_err(hdev, "QCA Failed to run HCI_RESET (%d)", err); return err; } + bt_dev_dbg(hdev, "QCA HCI_RESET succeed"); switch (soc_type) { case QCA_WCN3991: From 5b31fab9387520d9192661d67371f2881d3119a4 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:53 -0700 Subject: [PATCH 17/84] Bluetooth: btusb: Simplify btusb_shutdown_qca() by using __hci_reset_sync() btusb_shutdown_qca() open-codes a synchronous raw HCI reset that is functionally equivalent to the newly added __hci_reset_sync(). Replace it with __hci_reset_sync() and return its result directly. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 78bc3f3adc77..2a58a82a3ece 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3892,16 +3892,13 @@ static bool btusb_wakeup(struct hci_dev *hdev) static int btusb_shutdown_qca(struct hci_dev *hdev) { - struct sk_buff *skb; + int err; - skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { + err = __hci_reset_sync(hdev); + if (err) bt_dev_err(hdev, "HCI reset during shutdown failed"); - return PTR_ERR(skb); - } - kfree_skb(skb); - return 0; + return err; } static ssize_t force_poll_sync_read(struct file *file, char __user *user_buf, From 9c23bb412cfa8625c5e86518d89a2337c742226b Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:54 -0700 Subject: [PATCH 18/84] Bluetooth: hci_sync: Simplify hci_reset_sync() Return the reset command status directly instead of storing it in a local variable and using an if/return pattern. Reviewed-by: Bartosz Golaszewski Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 00857fc3235b..7779d9d1663a 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -3754,16 +3754,10 @@ static const struct hci_init_stage hci_init0[] = { int hci_reset_sync(struct hci_dev *hdev) { - int err; - set_bit(HCI_RESET, &hdev->flags); - err = __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL, - HCI_CMD_TIMEOUT); - if (err) - return err; - - return 0; + return __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL, + HCI_CMD_TIMEOUT); } /* Send a raw HCI reset for use by vendor drivers */ From c3bd57b9be300913a45a9446e34ad4438a220bea Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:55 -0700 Subject: [PATCH 19/84] Bluetooth: hci_event: Log error for HCI reset status error in hci_cc_reset() HCI_Reset is a critical command, but hci_cc_reset() uses bt_dev_dbg() to log it, so a non-zero error status response may not be noticed. Fix by using bt_dev_err() when a status error occurs. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 741d658e9630..ea858391c789 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -269,7 +269,10 @@ static u8 hci_cc_reset(struct hci_dev *hdev, void *data, struct sk_buff *skb) { struct hci_ev_status *rp = data; - bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); + if (rp->status) + bt_dev_err(hdev, "status 0x%2.2x", rp->status); + else + bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); clear_bit(HCI_RESET, &hdev->flags); From 92db4555c73f2aab9954dc7d2bc84c0563e6db7d Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:56 -0700 Subject: [PATCH 20/84] Bluetooth: btusb: Reduce a redundant assignment in btusb_probe() Initialize @priv_size at declaration rather than separately: - Simpler: one statement completes both declaration and assignment. - More flexible: the variable is immediately usable from that point, so any new priv_size += can be freely inserted without caring about where the separate priv_size = 0 sits. Reviewed-by: Bartosz Golaszewski Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 2a58a82a3ece..bb5e1c2a6bc4 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4082,7 +4082,7 @@ static int btusb_probe(struct usb_interface *intf, struct btusb_data *data; struct hci_dev *hdev; unsigned ifnum_base; - int err, priv_size; + int err, priv_size = 0; BT_DBG("intf %p id %p", intf, id); @@ -4153,8 +4153,6 @@ static int btusb_probe(struct usb_interface *intf, init_usb_anchor(&data->ctrl_anchor); spin_lock_init(&data->rxlock); - priv_size = 0; - data->recv_event = hci_recv_frame; data->recv_bulk = btusb_recv_bulk; From e22eb379a92ae8093e7aa4296b1cc93409a362f8 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jun 2026 22:19:57 -0700 Subject: [PATCH 21/84] Bluetooth: btusb: Use & instead of == to test bitflag BTUSB_IGNORE The driver_info field is a bitmask, so use & instead of == to test the BTUSB_IGNORE bitflag against it, which is consistent with how the other flags are tested. Reviewed-by: Bartosz Golaszewski Reviewed-by: Dmitry Baryshkov Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index bb5e1c2a6bc4..88b87e50fac2 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4101,7 +4101,7 @@ static int btusb_probe(struct usb_interface *intf, id = match; } - if (id->driver_info == BTUSB_IGNORE) + if (id->driver_info & BTUSB_IGNORE) return -ENODEV; if (id->driver_info & BTUSB_ATH3012) { From dc16388d45ecbd3be0d8c9424dbbaa2c81806578 Mon Sep 17 00:00:00 2001 From: Tibor Harcsa Date: Mon, 29 Jun 2026 22:34:20 +0200 Subject: [PATCH 22/84] Bluetooth: btusb: Add IMC Networks QCA9377 to quirks table Add the USB ID (13d3:3503) for the IMC Networks Qualcomm Atheros QCA9377 Bluetooth controller to the btusb quirks table. This device requires Qualcomm Rome firmware and wideband speech support to function properly; otherwise, BLE scanning fails with HCI unexpected event opcode 0x2005 errors. The device reports the following in /sys/kernel/debug/usb/devices: P: Vendor=13d3 ProdID=3503 Rev= 0.01 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Tibor Harcsa Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 88b87e50fac2..fc5424d89dac 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -297,6 +297,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3503), .driver_info = BTUSB_QCA_ROME | + BTUSB_WIDEBAND_SPEECH }, /* QCA WCN6855 chipset */ { USB_DEVICE(0x0489, 0xe0c7), .driver_info = BTUSB_QCA_WCN6855 | From 45640627e3ddc5f4d3a0d08618a596a8c65d48a5 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 17:17:01 +0800 Subject: [PATCH 23/84] Bluetooth: hci_nokia: validate firmware packet bounds nokia_setup_fw() walks a length-prefixed firmware stream and decodes HCI command packets from each record. Check that each record fits in the remaining firmware image, that command records contain the HCI command header, and that the payload length is covered before submitting the command. Signed-off-by: Pengpeng Hou Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_nokia.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c index 1e65b541f8ad..be2923231e71 100644 --- a/drivers/bluetooth/hci_nokia.c +++ b/drivers/bluetooth/hci_nokia.c @@ -354,9 +354,29 @@ static int nokia_setup_fw(struct hci_uart *hu) u16 opcode; struct sk_buff *skb; + if (pkt_size > fw_size - 2) { + err = -EINVAL; + dev_err(dev, "%s: Malformed firmware packet\n", + hu->hdev->name); + goto done; + } + switch (pkt_type) { case HCI_COMMAND_PKT: + if (pkt_size < 1 + HCI_COMMAND_HDR_SIZE) { + err = -EINVAL; + dev_err(dev, "%s: Malformed firmware command\n", + hu->hdev->name); + goto done; + } + cmd = (struct hci_command_hdr *)(fw_ptr + 3); + if (cmd->plen > pkt_size - 1 - HCI_COMMAND_HDR_SIZE) { + err = -EINVAL; + dev_err(dev, "%s: Truncated firmware command\n", + hu->hdev->name); + goto done; + } opcode = le16_to_cpu(cmd->opcode); skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen, From 980084de4d9b25193398d89a1c0430ba3501b683 Mon Sep 17 00:00:00 2001 From: Christoph Zwerschke Date: Sun, 5 Jul 2026 11:28:56 +0200 Subject: [PATCH 24/84] Bluetooth: btusb: Add ASUS USB-BT540 for Realtek 8761CU Add the vendor/product ID (0x0b05, 0x1bef) to the usb_device_id table for the Realtek RTL8761CU-based ASUS USB-BT540 adapter. It binds via the generic Bluetooth class today, so BTUSB_REALTEK is never set and the rtl8761cu firmware is not loaded, leaving the controller non-functional. With the entry the driver loads rtl_bt/rtl8761cu_fw.bin (already shipped by linux-firmware) and the adapter works (tested: A2DP and ASHA). Similar to commit bc597f0cc44f ("Bluetooth: btusb: Add TP-Link UB600 for Realtek 8761BUV"). Device info from /sys/kernel/debug/usb/devices: T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 22 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0b05 ProdID=1bef Rev= 2.00 S: Manufacturer=Realtek S: Product=Bluetooth Controller C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms Cc: stable@vger.kernel.org Signed-off-by: Christoph Zwerschke Reviewed-by: Paul Menzel Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index fc5424d89dac..5ef79cc0469e 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -856,6 +856,10 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + /* Additional Realtek 8761CU Bluetooth devices */ + { USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK }, From 6f0624b4427e38c3bb63a951c536cf8adaee1238 Mon Sep 17 00:00:00 2001 From: Christoph Zwerschke Date: Sun, 5 Jul 2026 11:28:57 +0200 Subject: [PATCH 25/84] Bluetooth: btusb: Add ASUS USB-BT600 for Realtek 8761CU Add the vendor/product ID (0x0b05, 0x1d70) to the usb_device_id table for the Realtek RTL8761CU-based ASUS USB-BT600 adapter. It binds via the generic Bluetooth class today, so BTUSB_REALTEK is never set and the rtl8761cu firmware is not loaded, leaving the controller non-functional. With the entry the driver loads rtl_bt/rtl8761cu_fw.bin (already shipped by linux-firmware) and the adapter works (tested: A2DP and ASHA). Similar to commit bc597f0cc44f ("Bluetooth: btusb: Add TP-Link UB600 for Realtek 8761BUV"). Device info from /sys/kernel/debug/usb/devices: T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 23 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0b05 ProdID=1d70 Rev= 2.00 S: Manufacturer=Realtek S: Product=Bluetooth Controller C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms Cc: stable@vger.kernel.org Signed-off-by: Christoph Zwerschke Reviewed-by: Paul Menzel Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 5ef79cc0469e..e4063131822f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -859,6 +859,8 @@ static const struct usb_device_id quirks_table[] = { /* Additional Realtek 8761CU Bluetooth devices */ { USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0b05, 0x1d70), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, From a2b3b4f00403a3a40c5627d1bf537c8fb166e221 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Fri, 15 May 2026 08:46:07 +0800 Subject: [PATCH 26/84] Bluetooth: btnxpuart: Fix use-after-free in probe error path In nxp_serdev_probe(), if hci_register_dev() succeeds but ps_setup() fails, the error path jumps to 'probe_fail' which only calls hci_free_dev() and asserts the reset GPIO, but does NOT call hci_unregister_dev() first. This leaves the HCI device registered in the system with its backing memory freed, leading to a use-after-free when userspace subsequently accesses the device (e.g. via hciconfig or bluetoothd). Fix by adding a 'probe_fail_unregister' label that calls hci_unregister_dev() before falling through to the existing 'probe_fail' label. The original 'probe_fail' label is preserved for the case where hci_register_dev() itself fails (device was never registered, so no unregister is needed). Signed-off-by: Zhao Dongdong Reviewed-by: Neeraj Sanjay Kale Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 6a1cffe08d5f..0bb300eef157 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1913,13 +1913,15 @@ static int nxp_serdev_probe(struct serdev_device *serdev) } if (ps_setup(hdev)) - goto probe_fail; + goto probe_fail_unregister; hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr, nxp_coredump_notify); return 0; +probe_fail_unregister: + hci_unregister_dev(hdev); probe_fail: reset_control_assert(nxpdev->pdn); hci_free_dev(hdev); From 86f8661893613c68b6e9945bc093b0021381314b Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 2 Jul 2026 22:33:59 +0530 Subject: [PATCH 27/84] Bluetooth: btintel_pcie: split coredump worker into per-trigger works btintel_pcie_coredump_worker() handled three unrelated jobs in one work item: collect a DRAM trace coredump, read the hardware exception event, and read the firmware-trigger event. The worker walked three flag bits at runtime and each interrupt path mutated multiple bits to communicate which sub-jobs the worker should run, which made the ownership rules for those bits hard to reason about and entangled the trigger reason with the in-progress accounting. Replace the single combined worker with three single-purpose ones, each owning exactly one flag: coredump_work -> btintel_pcie_dump_traces() guarded by COREDUMP_INPROGRESS hwexp_work -> btintel_pcie_read_hwexp() guarded by CORE_HALTED (already permanent until re-probe; HWEXP_INPROGRESS is now redundant and removed) fwtrigger_work -> btintel_pcie_dump_fwtrigger_event() guarded by FWTRIGGER_DUMP_INPROGRESS All three workers are queued on a shared ordered workqueue (renamed coredump_workqueue -> dump_workqueue) so a companion event reader (hwexp/fwtrigger) and the coredump always run FIFO. Companion work is queued before coredump_work so dmp_hdr.event_type/event_id are populated by the time dump_traces() consumes them, preserving the original ordering. Introduce btintel_pcie_queue_coredump() to centralize the coredump trigger contract: it is the single writer of COREDUMP_INPROGRESS and of dmp_hdr.trigger_reason, sets both atomically against concurrent triggers, and rolls back the bit if the workqueue is disabled (reset/remove in progress) so a later trigger after re-probe can succeed. All four trigger sites (HWEXP IRQ, FW-trigger IRQ, devcoredump user trigger, resume() D0 error path) go through the helper. Per-work guard bits are now cleared at the tail of each worker rather than in the middle of the combined worker, which closes a subtle race where a duplicate IRQ could observe a cleared bit and requeue while the previous pass was still finalizing dev_coredumpv(). reset_work() and remove() now disable_work_sync() all three workers and, on the FLR-failure path, enable_work() all three to keep their disable counters balanced. The PLDR/FLR-success contract (re-probe re-INIT_WORKs everything with counter 0) is preserved. No functional change to the dump payloads; this is a pure restructuring of the worker dispatch and its synchronization. Signed-off-by: Kiran K Assisted-by: GitHub-Copilot:claude-4.7-opus Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel_pcie.c | 185 ++++++++++++++++++++----------- drivers/bluetooth/btintel_pcie.h | 12 +- 2 files changed, 130 insertions(+), 67 deletions(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 2b7231be5973..013568197a39 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -1446,72 +1446,134 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data) return err; } +/* Queue a coredump dump_traces() pass. + * + * Returns true if a new coredump was queued, false if one was already + * in-flight (the BTINTEL_PCIE_COREDUMP_INPROGRESS bit serves as the + * single-writer guard for the @coredump_work item) or the workqueue is + * disabled (reset / remove in progress). + * + * Always queue this AFTER any companion event-reader work (hwexp / + * fwtrigger) so that, on the ordered @dump_workqueue, the event reader + * runs first and populates dmp_hdr.event_type / event_id before + * dump_traces consumes them. + */ +static bool btintel_pcie_queue_coredump(struct btintel_pcie_data *data, + u16 trigger_reason) +{ + if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) + return false; + + data->dmp_hdr.trigger_reason = trigger_reason; + + if (queue_work(data->dump_workqueue, &data->coredump_work)) + return true; + + /* Workqueue is disabled (reset/remove drained it). Release the + * guard so a later trigger, after re-probe, can succeed. + */ + clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); + return false; +} + static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data) { bt_dev_dbg(data->hdev, "Received firmware smart trigger cause"); - if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) + /* Per-work guard: deduplicate concurrent FW-trigger interrupts. + * Cleared at the tail of btintel_pcie_fwtrigger_worker(). + */ + if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, + &data->flags)) return; - /* Trigger device core dump when there is FW assert */ - if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) - data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; + if (!queue_work(data->dump_workqueue, &data->fwtrigger_work)) { + clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags); + return; + } - queue_work(data->coredump_workqueue, &data->coredump_work); + /* Queue coredump after the fwtrigger event reader so dmp_hdr.event_* + * is populated before dump_traces consumes it. + */ + btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT); } static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data) { bt_dev_err(data->hdev, "Received hw exception interrupt"); + /* CORE_HALTED is the single-writer guard for this handler. It is + * set once on first HW exception and cleared only by re-probe + * (data is reallocated), so it also serializes hwexp_work + * scheduling without needing a separate bit. + */ if (test_and_set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags)) return; - if (test_and_set_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) - return; + /* Queue companion coredump first so it is appended after hwexp_work + * on the ordered @dump_workqueue (preserves the original + * coredump-then-hwexp ordering). + */ + btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT); - /* Trigger device core dump when there is HW exception */ - if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) - data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; - - queue_work(data->coredump_workqueue, &data->coredump_work); + queue_work(data->dump_workqueue, &data->hwexp_work); } static void btintel_pcie_coredump_worker(struct work_struct *work) { struct btintel_pcie_data *data = container_of(work, struct btintel_pcie_data, coredump_work); - int err; /* hdev is NULL until setup_hdev() succeeds, and is cleared on * teardown after disable_work_sync() drains us; bail in that case. */ + if (!data->hdev) + goto out; + + btintel_pcie_dump_traces(data->hdev); +out: + /* Release guard last so a new trigger can run only after this + * pass has fully completed (including dev_coredumpv()). + */ + clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); +} + +static void btintel_pcie_hwexp_worker(struct work_struct *work) +{ + struct btintel_pcie_data *data = container_of(work, + struct btintel_pcie_data, hwexp_work); + if (!data->hdev) return; - if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) { - err = btintel_pcie_dump_fwtrigger_event(data); - if (err) - bt_dev_warn(data->hdev, "failed to log fwtrigger event"); - clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags); - } + /* Unlike usb products, controller will not send hardware exception + * event on exception. Instead controller writes the hardware event + * to device memory along with optional debug events, raises MSIX + * and halts. Driver shall read the exception event from device + * memory and passes it to the stack for further processing. + * + * Re-entry is gated by BTINTEL_PCIE_CORE_HALTED in the IRQ + * handler, which is only cleared by re-probe; no per-work bit + * is needed here. + */ + btintel_pcie_read_hwexp(data); +} - if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) { - btintel_pcie_dump_traces(data->hdev); - clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); - } +static void btintel_pcie_fwtrigger_worker(struct work_struct *work) +{ + struct btintel_pcie_data *data = container_of(work, + struct btintel_pcie_data, fwtrigger_work); + int err; - if (test_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) { - /* Unlike usb products, controller will not send hardware - * exception event on exception. Instead controller writes the - * hardware event to device memory along with optional debug - * events, raises MSIX and halts. Driver shall read the - * exception event from device memory and passes it stack for - * further processing. - */ - btintel_pcie_read_hwexp(data); - clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags); - } + if (!data->hdev) + goto out; + + err = btintel_pcie_dump_fwtrigger_event(data); + if (err) + bt_dev_warn(data->hdev, "failed to log fwtrigger event"); +out: + /* Release guard last; matches set in fw_trigger handler. */ + clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags); } static void btintel_pcie_rx_work(struct work_struct *work) @@ -2650,20 +2712,22 @@ static void btintel_pcie_reset_work(struct work_struct *wk) btintel_pcie_synchronize_irqs(data); flush_work(&data->rx_work); - /* Drain any in-flight coredump and block new ones across reset. - * Safe from self-deadlock: coredump_work runs on a separate wq. + /* Drain any in-flight dump workers and block new ones across reset. + * Safe from self-deadlock: they all run on a separate wq. */ disable_work_sync(&data->coredump_work); + disable_work_sync(&data->hwexp_work); + disable_work_sync(&data->fwtrigger_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); /* Both reset paths follow the same contract: on success they * destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs - * the coredump_work with disable count 0), so enable_work() must + * the dump workers with disable count 0), so enable_work() must * NOT be called on the success path. Only the FLR path can fail * with 'data' still alive, in which case we balance the - * disable_work_sync() above so a later successful reset is not - * permanently blocked. + * disable_work_sync() calls above so a later successful reset is + * not permanently blocked. * * pci_lock_rescan_remove() (held above) serializes against PCI * device addition/removal (hotplug), so no device can be added to @@ -2674,8 +2738,11 @@ static void btintel_pcie_reset_work(struct work_struct *wk) goto out; } - if (btintel_pcie_perform_flr(data)) + if (btintel_pcie_perform_flr(data)) { enable_work(&data->coredump_work); + enable_work(&data->hwexp_work); + enable_work(&data->fwtrigger_work); + } out: pci_dev_put(pdev); @@ -2869,8 +2936,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev, if (!data->workqueue) return -ENOMEM; - data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0); - if (!data->coredump_workqueue) { + data->dump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0); + if (!data->dump_workqueue) { destroy_workqueue(data->workqueue); return -ENOMEM; } @@ -2879,6 +2946,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev, INIT_WORK(&data->rx_work, btintel_pcie_rx_work); INIT_WORK(&data->reset_work, btintel_pcie_reset_work); INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker); + INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker); + INIT_WORK(&data->fwtrigger_work, btintel_pcie_fwtrigger_worker); data->boot_stage_cache = 0x00; data->img_resp_cache = 0x00; @@ -2921,7 +2990,7 @@ static int btintel_pcie_probe(struct pci_dev *pdev, /* reset device before exit */ btintel_pcie_reset_bt(data); - destroy_workqueue(data->coredump_workqueue); + destroy_workqueue(data->dump_workqueue); pci_clear_master(pdev); @@ -2940,12 +3009,14 @@ static void btintel_pcie_remove(struct pci_dev *pdev) return; } - /* Permanently block coredump triggers and drain the worker before - * tearing down. Must run before cancel_work_sync(&reset_work) so - * the disable counter stays >= 1 even after reset_work()'s + /* Permanently block all dump triggers and drain the workers before + * tearing down. Must run before disable_work_sync(&reset_work) so + * the disable counters stay >= 1 even after reset_work()'s * balanced enable_work() (counter 2 -> 1, never reaching 0). */ disable_work_sync(&data->coredump_work); + disable_work_sync(&data->hwexp_work); + disable_work_sync(&data->fwtrigger_work); /* Cancel pending reset work. Skip only when remove() is called from * within the reset work itself (PLDR device_reprobe path) to avoid @@ -2973,7 +3044,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev) btintel_pcie_release_hdev(data); - destroy_workqueue(data->coredump_workqueue); + destroy_workqueue(data->dump_workqueue); destroy_workqueue(data->workqueue); btintel_pcie_free(data); @@ -2992,16 +3063,8 @@ static void btintel_pcie_coredump(struct device *dev) if (!data) return; - if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) - return; - - data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER; - /* queue_work() returns false if the work is disabled (reset or - * remove in progress); clear the in-progress bit so a later - * trigger can succeed once the work is re-enabled. - */ - if (!queue_work(data->coredump_workqueue, &data->coredump_work)) - clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); + btintel_pcie_queue_coredump(data, + BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER); } #endif @@ -3138,12 +3201,8 @@ static int btintel_pcie_resume(struct device *dev) if (btintel_pcie_in_error(data) || btintel_pcie_in_device_halt(data)) { bt_dev_err(data->hdev, "Controller in error state for D0 entry"); - if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, - &data->flags)) { - data->dmp_hdr.trigger_reason = - BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; - queue_work(data->coredump_workqueue, &data->coredump_work); - } + btintel_pcie_queue_coredump(data, + BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT); set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); btintel_pcie_reset(data->hdev); } diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 7caee093e316..749369b24031 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -118,7 +118,6 @@ enum { enum { BTINTEL_PCIE_CORE_HALTED, - BTINTEL_PCIE_HWEXP_INPROGRESS, BTINTEL_PCIE_COREDUMP_INPROGRESS, BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, BTINTEL_PCIE_RECOVERY_IN_PROGRESS, @@ -466,8 +465,11 @@ struct btintel_pcie_dump_header { * @workqueue: workqueue for RX work * @rx_skb_q: SKB queue for RX packet * @rx_work: RX work struct to process the RX packet in @rx_skb_q - * @coredump_workqueue: dedicated workqueue for coredump collection - * @coredump_work: work struct for coredump trace collection + * @dump_workqueue: dedicated ordered workqueue serializing the coredump, + * hardware exception, and firmware-trigger dump workers + * @coredump_work: work struct for DRAM trace coredump collection + * @hwexp_work: work struct for hardware exception event read + * @fwtrigger_work: work struct for firmware-triggered diagnostic event read * @dma_pool: DMA pool for descriptors, index array and ci * @dma_p_addr: DMA address for pool * @dma_v_addr: address of pool @@ -516,8 +518,10 @@ struct btintel_pcie_data { struct work_struct rx_work; struct work_struct reset_work; - struct workqueue_struct *coredump_workqueue; + struct workqueue_struct *dump_workqueue; struct work_struct coredump_work; + struct work_struct hwexp_work; + struct work_struct fwtrigger_work; struct dma_pool *dma_pool; dma_addr_t dma_p_addr; From 335d7bd554a20f33acf762655cafe1089f7f5822 Mon Sep 17 00:00:00 2001 From: Pavel Zverev Date: Wed, 8 Jul 2026 01:15:49 +0300 Subject: [PATCH 28/84] Bluetooth: btusb: Add support for 1357:c123 Realtek 8852BE device Wiko Hi MateBook 14 Ryzen 200 laptops (DMI system-product-name "MNCA-XX", board "M1060") are equipped with an RTL8852BE Wi-Fi/BT combo chip (rtw89_8852be), whose Bluetooth radio enumerates as 1357:c123 instead of one of the already-supported 1358:c123 / 0bda:c123 identifiers, presumably due to OEM rebranding. Without a matching entry it only matches the generic USB Bluetooth class fallback, so the Realtek firmware/config (rtl8852btu_fw.bin / rtl8852btu_config.bin) is never loaded and the adapter cannot discover or connect to any device, even though hciconfig reports it as powered and scanning. Device descriptor: idVendor 0x1357 idProduct 0xc123 bcdDevice 0.00 iManufacturer 1 Realtek iProduct 2 Bluetooth Radio bDeviceClass 224 Wireless bDeviceSubClass 1 Radio Frequency bDeviceProtocol 1 Bluetooth Adding the same BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH quirk already used for 1358:c123 and 0bda:c123 fixes firmware loading and normal operation. Signed-off-by: Pavel Zverev Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index e4063131822f..518e44dcd304 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -894,6 +894,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0bda, 0xc123), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x1357, 0xc123), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0cb5, 0xc547), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, From e6b4232bde0219adb11334e65fe7577a21e86458 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Wed, 8 Jul 2026 21:14:53 -0700 Subject: [PATCH 29/84] Bluetooth: coredump: Do not export hci_devcd_rx() and hci_devcd_timeout() Do not export both functions since they are only used internally within the bluetooth module. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/coredump.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/bluetooth/coredump.c b/net/bluetooth/coredump.c index 720cb79adf96..c0f027fab583 100644 --- a/net/bluetooth/coredump.c +++ b/net/bluetooth/coredump.c @@ -390,7 +390,6 @@ void hci_devcd_rx(struct work_struct *work) hci_dev_unlock(hdev); } } -EXPORT_SYMBOL(hci_devcd_rx); void hci_devcd_timeout(struct work_struct *work) { @@ -416,7 +415,6 @@ void hci_devcd_timeout(struct work_struct *work) hci_dev_unlock(hdev); } -EXPORT_SYMBOL(hci_devcd_timeout); int hci_devcd_register(struct hci_dev *hdev, coredump_t coredump, dmp_hdr_t dmp_hdr, notify_change_t notify_change) From e0650618ee8395cdefde1686e31d3ff5c253955e Mon Sep 17 00:00:00 2001 From: Kiran K Date: Wed, 15 Jul 2026 21:47:53 +0530 Subject: [PATCH 30/84] Bluetooth: btintel_pcie: serialize reset_type with RECOVERY_IN_PROGRESS The reset path had two concurrency holes. Both are reachable in practice when btintel_pcie_hw_error() is invoked from the HCI rx path while another reset is being requested or is already in flight. 1. data->reset_type was a plain shared field. The hw_error path wrote it BEFORE the test_and_set_bit(RECOVERY_IN_PROGRESS) guard inside btintel_pcie_reset(), so a second hw_error could clobber the type chosen by an earlier in-flight request: CPU0 (reset_work) CPU1 (hw_error #2) dev_data->reset_type = PLDR T2: read reset_type dev_data->reset_type = FLR reset() test_and_set sees 1 -> drops, but type already clobbered The hdev->reset callback (.reset = btintel_pcie_reset, invoked via the sysfs reset attribute /sys/class/bluetooth/hciX/reset and from hci_cmd_timeout()) compounded this by not writing reset_type at all -- it inherited whatever value a previous hw_error / resume() had left, which could be PLDR. 2. btintel_pcie_dump_debug_registers() was called unconditionally at the top of hw_error(). When reset_work was already running pci_try_reset_function(), the BT MMIO window can read all-1s or trigger AER for the duration of the FLR, polluting the debug dump with no useful information. Refactor the reset path to make RECOVERY_IN_PROGRESS the sole serializer for both the type write and the work scheduling: - Replace btintel_pcie_reset(hdev) with btintel_pcie_request_reset(data, type). The helper takes the desired reset variant as a parameter and writes data->reset_type only after winning test_and_set_bit(); losers return without touching the field, so concurrent triggers can no longer clobber an in-flight reset's type. reset_work()'s read of reset_type is now ordered after the bit transition via schedule_work()'s memory barrier. - Add a thin btintel_pcie_hci_reset() wrapper for the hdev->reset callback (invoked via the sysfs reset attribute /sys/class/bluetooth/hciX/reset and from hci_cmd_timeout()) that always requests FLR explicitly, so these paths no longer inherit stale state from prior error events. - Add an early test_bit(RECOVERY_IN_PROGRESS) gate at the top of hw_error() so dump_debug_registers() and the recovery-counter bookkeeping are skipped when a reset is already in flight; the authoritative test_and_set lives in request_reset() and races cleanly against any caller that passes the optimistic check. - Convert the two resume() reset sites (FREEZE/HIBERNATE and the D0-error path) to request_reset(data, FLR), removing the redundant manual reset_type writes. Assisted-by: GitHub-Copilot:claude-4.7-opus Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel_pcie.c | 75 ++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 013568197a39..2e28847263ab 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -2550,8 +2550,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, } } -static void btintel_pcie_reset(struct hci_dev *hdev); - static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) { union acpi_object *obj, argv4; @@ -2749,56 +2747,86 @@ static void btintel_pcie_reset_work(struct work_struct *wk) pci_unlock_rescan_remove(); } -static void btintel_pcie_reset(struct hci_dev *hdev) +/* Schedule a device reset of the requested type. + * + * BTINTEL_PCIE_RECOVERY_IN_PROGRESS serializes all reset requesters + * (sysfs reset attribute, hci_cmd_timeout(), hw_error, resume error + * path, etc.) so that: + * + * - dev_data->reset_type is written by exactly one caller (the + * thread that wins test_and_set_bit), eliminating the race where + * a second hw_error could clobber an already-scheduled reset's + * type; + * - the write happens AFTER the bit is set, so reset_work observes + * it through schedule_work()'s memory ordering; + * - losers return without touching reset_type or scheduling the + * work, so concurrent triggers are silently coalesced into the + * in-flight one (whose recovery will reinitialize the device + * regardless of the dropped trigger's variant). + * + * The bit is cleared only by .remove() / re-probe via fresh devm + * allocation, which is the intended one-shot semantics: a reset + * tears down and re-probes 'data', so there is no "in-flight" + * reset to follow up after device_reprobe() succeeds. + */ +static void btintel_pcie_request_reset(struct btintel_pcie_data *data, + enum btintel_pcie_reset_type type) { - struct btintel_pcie_data *data; - - data = hci_get_drvdata(hdev); - if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags)) return; if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) return; + data->reset_type = type; + pci_dev_get(data->pdev); schedule_work(&data->reset_work); } +static void btintel_pcie_hci_reset(struct hci_dev *hdev) +{ + struct btintel_pcie_data *data = hci_get_drvdata(hdev); + + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); +} + static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) { - struct btintel_pcie_dev_recovery *data; + struct btintel_pcie_dev_recovery *rec; struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev); struct pci_dev *pdev = dev_data->pdev; + enum btintel_pcie_reset_type type; time64_t retry_window; + if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &dev_data->flags)) + return; + btintel_pcie_dump_debug_registers(hdev); - data = btintel_pcie_get_recovery(pdev, &hdev->dev); - if (!data) + rec = btintel_pcie_get_recovery(pdev, &hdev->dev); + if (!rec) return; - if (code == 0x13) - dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR; - else - dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; + type = (code == 0x13) ? BTINTEL_PCIE_IOSF_PRR_PLDR + : BTINTEL_PCIE_IOSF_PRR_FLR; bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code, - dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); - retry_window = ktime_get_boottime_seconds() - data->last_error; + type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); + retry_window = ktime_get_boottime_seconds() - rec->last_error; if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS && - data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) { + rec->count >= BTINTEL_PCIE_FLR_MAX_RETRY) { bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d", - BTINTEL_PCIE_FLR_MAX_RETRY, data->count); + BTINTEL_PCIE_FLR_MAX_RETRY, rec->count); bt_dev_dbg(hdev, "Boot time: %lld seconds", ktime_get_boottime_seconds()); bt_dev_dbg(hdev, "last error at: %lld seconds", - data->last_error); + rec->last_error); return; } btintel_pcie_inc_recovery_count(pdev, &hdev->dev); - btintel_pcie_reset(hdev); + btintel_pcie_request_reset(dev_data, type); } static bool btintel_pcie_wakeup(struct hci_dev *hdev) @@ -2888,7 +2916,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) hdev->hw_error = btintel_pcie_hw_error; hdev->set_diag = btintel_set_diag; hdev->set_bdaddr = btintel_set_bdaddr; - hdev->reset = btintel_pcie_reset; + hdev->reset = btintel_pcie_hci_reset; hdev->wakeup = btintel_pcie_wakeup; hdev->hci_drv = &btintel_pcie_hci_drv; @@ -3176,8 +3204,7 @@ static int btintel_pcie_resume(struct device *dev) if (data->pm_sx_event == PM_EVENT_FREEZE || data->pm_sx_event == PM_EVENT_HIBERNATE) { set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); - data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; - btintel_pcie_reset(data->hdev); + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); return 0; } @@ -3204,7 +3231,7 @@ static int btintel_pcie_resume(struct device *dev) btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT); set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); - btintel_pcie_reset(data->hdev); + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); } return err; } From 6afadcff79b157a4770c4e987a6005bff40a670b Mon Sep 17 00:00:00 2001 From: Li Qiang Date: Thu, 16 Jul 2026 16:47:28 +0800 Subject: [PATCH 31/84] Bluetooth: bfusb: validate received block boundaries The USB receive path trusts the block header to contain the required number of bytes and passes it to the reassembly routine. The routine also trusts a malformed HCI packet type and can append more data than the skb allocated from the advertised packet length. A malformed USB transfer can therefore cause out-of-bounds reads or an skb tail overwrite. Validate block header availability, declared block size, packet type, and reassembly tailroom. Drop the partial frame on an invalid block. Signed-off-by: Li Qiang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/bfusb.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 8df310983bf6..d31d797639b5 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -301,6 +301,11 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch return -EILSEQ; } break; + + default: + bt_dev_err(data->hdev, "unknown packet type 0x%02x", + pkt_type); + return -EILSEQ; } skb = bt_skb_alloc(pkt_len, GFP_ATOMIC); @@ -319,6 +324,13 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch } } + if (len > skb_tailroom(data->reassembly)) { + bt_dev_err(data->hdev, "block exceeds packet length"); + kfree_skb(data->reassembly); + data->reassembly = NULL; + return -EILSEQ; + } + if (len > 0) skb_put_data(data->reassembly, buf, len); @@ -353,6 +365,13 @@ static void bfusb_rx_complete(struct urb *urb) skb_put(skb, count); while (count) { + if (count < 2) { + bt_dev_err(data->hdev, "short block header"); + kfree_skb(data->reassembly); + data->reassembly = NULL; + break; + } + hdr = buf[0] | (buf[1] << 8); if (hdr & 0x4000) { @@ -360,16 +379,28 @@ static void bfusb_rx_complete(struct urb *urb) count -= 2; buf += 2; } else { + if (count < 3) { + bt_dev_err(data->hdev, "short block header"); + kfree_skb(data->reassembly); + data->reassembly = NULL; + break; + } + len = (buf[2] == 0) ? 256 : buf[2]; count -= 3; buf += 3; } - if (count < len) + if (count < len) { bt_dev_err(data->hdev, "block extends over URB buffer ranges"); + kfree_skb(data->reassembly); + data->reassembly = NULL; + break; + } - if ((hdr & 0xe1) == 0xc1) - bfusb_recv_block(data, hdr, buf, len); + if ((hdr & 0xe1) == 0xc1 && + bfusb_recv_block(data, hdr, buf, len) < 0) + data->hdev->stat.err_rx++; count -= len; buf += len; From 65be90af275675a65deed133ef67c81168e12bc9 Mon Sep 17 00:00:00 2001 From: Li Qiang Date: Thu, 16 Jul 2026 16:47:29 +0800 Subject: [PATCH 32/84] Bluetooth: btmrvl: validate event packet lengths The Marvell event handlers access the HCI event header, command complete payload, and driver-specific event header before validating that the received skb contains them. A truncated event can consequently cause an out-of-bounds read. Validate each header and the command-complete payload length before dereferencing the corresponding fields. Signed-off-by: Li Qiang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmrvl_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index d6f0ad0b4b6e..aaf1614ccfd7 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -43,10 +43,17 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; + if (skb->len < sizeof(*hdr)) + return true; + if (hdr->evt == HCI_EV_CMD_COMPLETE) { struct hci_ev_cmd_complete *ec; u16 opcode; + if (hdr->plen < sizeof(*ec) || + skb->len < HCI_EVENT_HDR_SIZE + sizeof(*ec)) + return true; + ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE); opcode = __le16_to_cpu(ec->opcode); @@ -74,6 +81,9 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) struct btmrvl_event *event; int ret = 0; + if (skb->len < sizeof(*event)) + return -EINVAL; + event = (struct btmrvl_event *) skb->data; if (event->ec != 0xff) { BT_DBG("Not Marvell Event=%x", event->ec); From ceea75ad8925425ee6520ead964b55888fe871ec Mon Sep 17 00:00:00 2001 From: Li Qiang Date: Thu, 16 Jul 2026 16:47:30 +0800 Subject: [PATCH 33/84] Bluetooth: hci_bcsp: validate received packet lengths The BCSP transmit path reads an HCI command header when an extension packet has only been tested for a nonzero length. Its LE configuration packet handler also indexes bytes through offset seven without a length check. Validate the complete command and LE configuration packet headers before accessing their fields. Signed-off-by: Li Qiang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcsp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index db56eead27ce..0323db21c428 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -194,7 +194,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, return NULL; } - if (hciextn && chan == 5) { + if (hciextn && chan == 5 && len > HCI_COMMAND_HDR_SIZE) { __le16 opcode = ((struct hci_command_hdr *)data)->opcode; /* Vendor specific commands */ @@ -402,6 +402,9 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu) u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed }; /* spot "conf" pkts and reply with a "conf rsp" pkt */ + if (bcsp->rx_skb->len < 8) + return; + if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) { struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC); From 0fdb6ca821170c0c80a42b7dcb34cc75b55c2f1b Mon Sep 17 00:00:00 2001 From: Li Qiang Date: Thu, 16 Jul 2026 16:47:31 +0800 Subject: [PATCH 34/84] Bluetooth: hci_ldisc: reject invalid tty write lengths The HCI UART write worker assumes that a tty write callback returns a value in the range from zero through the skb length. A negative value or a value larger than the skb length is passed to accounting and skb_pull, which can corrupt skb state. Treat either return value as a transmit error and discard the skb. Signed-off-by: Li Qiang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_ldisc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 2ad42c3bbaac..46dfbe6f1c2e 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -163,6 +163,12 @@ static void hci_uart_write_work(struct work_struct *work) set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); len = tty->ops->write(tty, skb->data, skb->len); + if (len < 0 || len > skb->len) { + hdev->stat.err_tx++; + kfree_skb(skb); + continue; + } + hdev->stat.byte_tx += len; skb_pull(skb, len); From 47386974699370c015feb3fd5c1e7b33f78fa6ae Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 21 Jul 2026 03:14:40 -0700 Subject: [PATCH 35/84] Bluetooth: hci_qca: Replace HCI_VENDOR_PKT usage with HCI_EV_VENDOR The macros below have different meanings even though they share the same value 0xff: HCI_VENDOR_PKT: HCI packet indicator or type HCI_EV_VENDOR: event code of a VSE This usage of HCI_VENDOR_PKT is wrongly checking an event code. Fix by using HCI_EV_VENDOR for event code. Also fix warning "CHECK: Unnecessary parentheses around comparison" given by checkpatch.pl. Acked-by: Bartosz Golaszewski Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_qca.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 1222f97800f4..e6d107f67759 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1239,8 +1239,8 @@ static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb) * received we store dump into a file before closing hci. This * dump will help in triaging the issues. */ - if ((skb->data[0] == HCI_VENDOR_PKT) && - (get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE)) + if (skb->data[0] == HCI_EV_VENDOR && + get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE) return qca_controller_memdump_event(hdev, skb); return hci_recv_frame(hdev, skb); From 1341d23d053323ac8c6936b69c1d085056cae69e Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 21 Jul 2026 03:14:41 -0700 Subject: [PATCH 36/84] Bluetooth: btusb: QCA: Replace HCI_VENDOR_PKT usages with HCI_EV_VENDOR The macros below have different meanings even though they share the same value 0xff: HCI_VENDOR_PKT: HCI packet indicator or type HCI_EV_VENDOR: event code of a VSE These usages of HCI_VENDOR_PKT are wrongly checking an event code. Fix by using HCI_EV_VENDOR for event code. Also fix warning "CHECK: Unnecessary parentheses around comparison" given by checkpatch.pl. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 518e44dcd304..e28c5daeba3f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3278,7 +3278,7 @@ static bool acl_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb) goto out; event_hdr = skb_pull_data(clone, sizeof(*event_hdr)); - if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT)) + if (!event_hdr || event_hdr->evt != HCI_EV_VENDOR) goto out; dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr)); @@ -3304,7 +3304,7 @@ static bool evt_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb) return false; event_hdr = skb_pull_data(clone, sizeof(*event_hdr)); - if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT)) + if (!event_hdr || event_hdr->evt != HCI_EV_VENDOR) goto out; dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr)); From a68f8bdc00e67fbb1763538d0101e2a5467237c0 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 21 Jul 2026 03:14:42 -0700 Subject: [PATCH 37/84] Bluetooth: btusb: Realtek: Replace HCI_VENDOR_PKT usage with HCI_EV_VENDOR The macros below have different meanings even though they share the same value 0xff: HCI_VENDOR_PKT: HCI packet indicator or type HCI_EV_VENDOR: event code of a VSE This usage of HCI_VENDOR_PKT is wrongly checking an event code. Fix by using HCI_EV_VENDOR for event code. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index e28c5daeba3f..e4c451e8f268 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2800,7 +2800,7 @@ static int btusb_setup_realtek(struct hci_dev *hdev) static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb) { if (skb->len >= HCI_EVENT_HDR_SIZE + 1 && - skb->data[0] == HCI_VENDOR_PKT && + skb->data[0] == HCI_EV_VENDOR && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) { struct rtk_dev_coredump_hdr hdr = { .code = RTK_DEVCOREDUMP_CODE_MEMDUMP, From 874ca6bdde050fde1fcfae9e8d860c95ac959f2a Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Wed, 22 Jul 2026 15:43:15 +0800 Subject: [PATCH 38/84] Bluetooth: btintel_pcie: Remove unreachable break after goto In the switch-case block for hardware variant detection, the default case has an unreachable 'break' statement following 'goto exit_error'. Remove the dead code. Signed-off-by: Chen Changcheng Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel_pcie.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 2e28847263ab..ef42b8d11d4d 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -2425,7 +2425,6 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev) INTEL_HW_VARIANT(ver_tlv.cnvi_bt)); err = -EINVAL; goto exit_error; - break; } data->dmp_hdr.cnvi_top = ver_tlv.cnvi_top; From 290a3644609526f7ac12529cd1618042fa5a32db Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Wed, 22 Jul 2026 16:31:36 +0800 Subject: [PATCH 39/84] Bluetooth: btrsi: Move set_bt_context after successful HCI registration In rsi_hci_attach(), ops->set_bt_context() stores the newly allocated h_adapter into common->bt_adapter before hci_alloc_dev() and hci_register_dev() are called. If either of these fails, h_adapter is freed but common->bt_adapter remains a non-NULL dangling pointer. This causes a deterministically reachable use-after-free when the device operates in a BT+WiFi coexistence mode and CONFIG_RSI_COEX is enabled. The following software-only trigger paths exist: 1. SDIO driver .remove (rsi_disconnect) 2. USB driver .disconnect (rsi_disconnect) 3. SDIO driver .shutdown (rsi_shutdown) 4. Hibernation .freeze (rsi_freeze) All four paths check: if (IS_ENABLED(CONFIG_RSI_COEX) && coex_mode > 1 && bt_adapter) rsi_bt_ops.detach(bt_adapter); // use-after-free coex_mode is set during rsi_91x_init(), before rsi_hci_attach() is called, and is not cleared on attach failure. Since set_bt_context() already wrote bt_adapter before the failure, the deinit paths see a non-NULL dangling pointer and proceed to detach it. Fix this by moving set_bt_context() after hci_register_dev() succeeds. On failure paths bt_adapter stays NULL, and the deinit callers correctly skip the detach call. Signed-off-by: Chen Changcheng Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btrsi.c b/drivers/bluetooth/btrsi.c index 59ad0b9b14c3..3f802b7c83f7 100644 --- a/drivers/bluetooth/btrsi.c +++ b/drivers/bluetooth/btrsi.c @@ -107,7 +107,6 @@ static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops) return -ENOMEM; h_adapter->priv = priv; - ops->set_bt_context(priv, h_adapter); h_adapter->proto_ops = ops; hdev = hci_alloc_dev(); @@ -136,6 +135,8 @@ static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops) goto err; } + ops->set_bt_context(priv, h_adapter); + return 0; err: h_adapter->hdev = NULL; From 6ec1896b5331563fedf3f01014aba9c9e35d7f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iva=20Kasprzakov=C3=A1?= Date: Wed, 22 Jul 2026 15:08:49 +0200 Subject: [PATCH 40/84] Bluetooth: fix BT dependency for submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The modules rfcomm (BT_RFCOMM), bnep (BT_BNEP), hidp (BT_HIDP), and bluetooth_6lowpan (BT_6LOWPAN) are dependent on the bluetooth module (BT, tristate) only transitively through the boolean BT_BREDR for the first three and through the boolean BT_LE for the bluetooth_6lowpan. Therefore, the modules can be selected as built-in even if the BT=m. The combination of BT=m and =y for the said modules leads to the kernel build system silently ignoring those modules, without ever compiling them as built-in or as loadable modules. Add BT as a direct dependency to the Kconfig of rfcomm, bnep, hidp, and bluetooth_6lowpan. The modules set to =y when BT=m will default to =m, rather then getting silently ignored by the build system. Signed-off-by: Iva Kasprzaková Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/Kconfig | 2 +- net/bluetooth/bnep/Kconfig | 2 +- net/bluetooth/hidp/Kconfig | 2 +- net/bluetooth/rfcomm/Kconfig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index d250e94e90eb..1cda01614efe 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -76,7 +76,7 @@ config BT_LE_L2CAP_ECRED config BT_6LOWPAN tristate "Bluetooth 6LoWPAN support" - depends on BT_LE && 6LOWPAN + depends on BT && BT_LE && 6LOWPAN help IPv6 compression over Bluetooth Low Energy. diff --git a/net/bluetooth/bnep/Kconfig b/net/bluetooth/bnep/Kconfig index aac02b5b0d17..f8087e2d2c00 100644 --- a/net/bluetooth/bnep/Kconfig +++ b/net/bluetooth/bnep/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config BT_BNEP tristate "BNEP protocol support" - depends on BT_BREDR + depends on BT && BT_BREDR select CRC32 help BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet diff --git a/net/bluetooth/hidp/Kconfig b/net/bluetooth/hidp/Kconfig index e08aae35351a..ba52c7296f18 100644 --- a/net/bluetooth/hidp/Kconfig +++ b/net/bluetooth/hidp/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config BT_HIDP tristate "HIDP protocol support" - depends on BT_BREDR && HID + depends on BT && BT_BREDR && HID help HIDP (Human Interface Device Protocol) is a transport layer for HID reports. HIDP is required for the Bluetooth Human diff --git a/net/bluetooth/rfcomm/Kconfig b/net/bluetooth/rfcomm/Kconfig index 9b9953ebf4c0..e7af2d565cea 100644 --- a/net/bluetooth/rfcomm/Kconfig +++ b/net/bluetooth/rfcomm/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config BT_RFCOMM tristate "RFCOMM protocol support" - depends on BT_BREDR + depends on BT && BT_BREDR help RFCOMM provides connection oriented stream transport. RFCOMM support is required for Dialup Networking, OBEX and other Bluetooth From 3914dc880317a7fb77f0b18907f3b29913884340 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:41 -0700 Subject: [PATCH 41/84] Bluetooth: coredump: Introduce and apply hci_devcd_state_name() Introduce hci_devcd_state_name() to describe the devcoredump state by a string name instead of a plain number, for several reasons: 1) Applying it in coredump.c makes the devcoredump state in log messages more readable than a plain number. 2) Transport drivers may need to show the devcoredump state name too. 3) In future, the universal state name could be notified to userspace via uevent, allowing a universal application (e.g. a daemon) to be developed to save the coredump, which is otherwise discarded by the device coredump core after 5 minutes (DEVCD_TIMEOUT); see nxp_coredump_notify(). Also drop a trailing space from two bt_dev_dbg() format strings while applying it in coredump.c. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/coredump.h | 7 +++++ net/bluetooth/coredump.c | 45 +++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/coredump.h b/include/net/bluetooth/coredump.h index 72f51b587a04..ab85a6adfffd 100644 --- a/include/net/bluetooth/coredump.h +++ b/include/net/bluetooth/coredump.h @@ -60,6 +60,8 @@ struct hci_devcoredump { #ifdef CONFIG_DEV_COREDUMP +const char *hci_devcd_state_name(enum devcoredump_state state); + void hci_devcd_reset(struct hci_dev *hdev); void hci_devcd_rx(struct work_struct *work); void hci_devcd_timeout(struct work_struct *work); @@ -74,6 +76,11 @@ int hci_devcd_abort(struct hci_dev *hdev); #else +static inline const char *hci_devcd_state_name(enum devcoredump_state state) +{ + return ""; +} + static inline void hci_devcd_reset(struct hci_dev *hdev) {} static inline void hci_devcd_rx(struct work_struct *work) {} static inline void hci_devcd_timeout(struct work_struct *work) {} diff --git a/net/bluetooth/coredump.c b/net/bluetooth/coredump.c index c0f027fab583..913bbba559f8 100644 --- a/net/bluetooth/coredump.c +++ b/net/bluetooth/coredump.c @@ -30,8 +30,9 @@ struct hci_devcoredump_skb_pattern { #define DBG_UNEXPECTED_STATE() \ bt_dev_dbg(hdev, \ - "Unexpected packet (%d) for state (%d). ", \ - hci_dmp_cb(skb)->pkt_type, hdev->dump.state) + "Unexpected packet (%d) for state %s.", \ + hci_dmp_cb(skb)->pkt_type, \ + hci_devcd_state_name(hdev->dump.state)) #define MAX_DEVCOREDUMP_HDR_SIZE 512 /* bytes */ @@ -50,8 +51,9 @@ static int hci_devcd_update_hdr_state(char *buf, size_t size, int state) /* Call with hci_dev_lock only. */ static int hci_devcd_update_state(struct hci_dev *hdev, int state) { - bt_dev_dbg(hdev, "Updating devcoredump state from %d to %d.", - hdev->dump.state, state); + bt_dev_dbg(hdev, "Updating devcoredump state from %s to %s.", + hci_devcd_state_name(hdev->dump.state), + hci_devcd_state_name(state)); hdev->dump.state = state; @@ -245,7 +247,7 @@ static void hci_devcd_dump(struct hci_dev *hdev) struct sk_buff *skb; u32 size; - bt_dev_dbg(hdev, "state %d", hdev->dump.state); + bt_dev_dbg(hdev, "state %s", hci_devcd_state_name(hdev->dump.state)); size = hdev->dump.tail - hdev->dump.head; @@ -368,8 +370,9 @@ void hci_devcd_rx(struct work_struct *work) break; default: - bt_dev_dbg(hdev, "Unknown packet (%d) for state (%d). ", - hci_dmp_cb(skb)->pkt_type, hdev->dump.state); + bt_dev_dbg(hdev, "Unknown packet (%d) for state %s.", + hci_dmp_cb(skb)->pkt_type, + hci_devcd_state_name(hdev->dump.state)); break; } @@ -549,3 +552,31 @@ int hci_devcd_abort(struct hci_dev *hdev) return 0; } EXPORT_SYMBOL(hci_devcd_abort); + +const char *hci_devcd_state_name(enum devcoredump_state state) +{ + const char *state_name = "Unknown"; + + switch (state) { + case HCI_DEVCOREDUMP_IDLE: + state_name = "IDLE"; + break; + case HCI_DEVCOREDUMP_ACTIVE: + state_name = "ACTIVE"; + break; + case HCI_DEVCOREDUMP_DONE: + state_name = "DONE"; + break; + case HCI_DEVCOREDUMP_ABORT: + state_name = "ABORT"; + break; + case HCI_DEVCOREDUMP_TIMEOUT: + state_name = "TIMEOUT"; + break; + default: + break; + } + + return state_name; +} +EXPORT_SYMBOL(hci_devcd_state_name); From d6d15018c8e1527841e39a6a80c60997e594ca70 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:42 -0700 Subject: [PATCH 42/84] Bluetooth: btusb: Make btusb_recv_{event,acl}() take struct hci_dev * Both helpers currently take struct btusb_data *, which is private to btusb.c, as parameter type as below: int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb) int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb) To allow vendor USB-transport-specific source files to share them as well, change the type to struct hci_dev *. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index e4c451e8f268..03048e7eef21 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1252,14 +1252,16 @@ static inline void btusb_free_frags(struct btusb_data *data) spin_unlock_irqrestore(&data->rxlock, flags); } -static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb) +static int btusb_recv_event(struct hci_dev *hdev, struct sk_buff *skb) { + struct btusb_data *data = hci_get_drvdata(hdev); + if (data->intr_interval) { /* Trigger dequeue immediately if an event is received */ schedule_delayed_work(&data->rx_work, 0); } - return data->recv_event(data->hdev, skb); + return data->recv_event(hdev, skb); } static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count) @@ -1319,7 +1321,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count) } /* Complete frame */ - btusb_recv_event(data, skb); + btusb_recv_event(data->hdev, skb); skb = NULL; } } @@ -1330,13 +1332,15 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count) return err; } -static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb) +static int btusb_recv_acl(struct hci_dev *hdev, struct sk_buff *skb) { + struct btusb_data *data = hci_get_drvdata(hdev); + /* Only queue ACL packet if intr_interval is set as it means * force_poll_sync has been enabled. */ if (!data->intr_interval) - return data->recv_acl(data->hdev, skb); + return data->recv_acl(hdev, skb); skb_queue_tail(&data->acl_q, skb); schedule_delayed_work(&data->rx_work, data->intr_interval); @@ -1391,7 +1395,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) if (!hci_skb_expect(skb)) { /* Complete frame */ - btusb_recv_acl(data, skb); + btusb_recv_acl(data->hdev, skb); skb = NULL; } } From 33971338ef4c5e596bf402b63fb61e3aae1ec6a2 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:43 -0700 Subject: [PATCH 43/84] Bluetooth: btusb: Add a simple static btusb_prepare_reset() Add btusb_prepare_reset() to do cleanup before a reset, and apply it to btusb_mtk_reset() as well. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 03048e7eef21..b06784ad955c 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2074,6 +2074,14 @@ static void btusb_stop_traffic(struct btusb_data *data) usb_kill_anchored_urbs(&data->ctrl_anchor); } +static void btusb_prepare_reset(struct hci_dev *hdev) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + + btusb_stop_traffic(data); + usb_kill_anchored_urbs(&data->tx_anchor); +} + static int btusb_close(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); @@ -2918,8 +2926,7 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) /* Release MediaTek ISO data interface */ btusb_mtk_release_iso_intf(hdev); - btusb_stop_traffic(data); - usb_kill_anchored_urbs(&data->tx_anchor); + btusb_prepare_reset(hdev); /* Toggle the hard reset line. The MediaTek device is going to * yank itself off the USB and then replug. The cleanup is handled From 22fd0fbcf720a99acdd2bef66d57f4f3c95ed9cb Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:44 -0700 Subject: [PATCH 44/84] Bluetooth: hci: Introduce hci_acl_handle() and hci_acl_dlen() helpers Introduce both helpers for ACL packet since: both core and transport drivers extract the handle and data length from its header in several places. Both will be used later. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 50f0eef71fb1..d557bdf9ae57 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -3407,6 +3407,16 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb) #define hci_handle(h) (h & 0x0fff) #define hci_flags(h) (h >> 12) +static inline __u16 hci_acl_handle(const struct sk_buff *skb) +{ + return hci_handle(__le16_to_cpu(hci_acl_hdr(skb)->handle)); +} + +static inline __u16 hci_acl_dlen(const struct sk_buff *skb) +{ + return __le16_to_cpu(hci_acl_hdr(skb)->dlen); +} + /* ISO handle and flags pack/unpack */ #define hci_iso_flags_pb(f) (f & 0x0003) #define hci_iso_flags_ts(f) ((f >> 2) & 0x0001) From b1ffea37f7349ca12166812669a554d1191d26cc Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:45 -0700 Subject: [PATCH 45/84] Bluetooth: hci_core: Simplify hci_recv_frame() by hci_acl_handle() Simplify hci_recv_frame() by using hci_acl_handle() instead of: __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle); ... hci_handle(handle) ... Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d1e78ae7728e..9d5adf882509 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2904,10 +2904,9 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) if (hci_conn_num(hdev, CIS_LINK) || hci_conn_num(hdev, BIS_LINK) || hci_conn_num(hdev, PA_LINK)) { - __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle); __u8 type; - type = hci_conn_lookup_type(hdev, hci_handle(handle)); + type = hci_conn_lookup_type(hdev, hci_acl_handle(skb)); if (type == CIS_LINK || type == BIS_LINK || type == PA_LINK) hci_skb_pkt_type(skb) = HCI_ISODATA_PKT; From 16ca59d36bce02de9ad57b939cd6741ca3cd91e9 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:46 -0700 Subject: [PATCH 46/84] Bluetooth: btusb: Simplify btusb_recv_bulk() by hci_acl_dlen() Simplify btusb_recv_bulk() by using hci_acl_dlen() instead of: __le16 dlen = hci_acl_hdr(skb)->dlen; ... __le16_to_cpu(dlen) ... Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b06784ad955c..be82bbbc1b5c 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1379,10 +1379,8 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) hci_skb_expect(skb) -= len; if (skb->len == HCI_ACL_HDR_SIZE) { - __le16 dlen = hci_acl_hdr(skb)->dlen; - /* Complete ACL header */ - hci_skb_expect(skb) = __le16_to_cpu(dlen); + hci_skb_expect(skb) = hci_acl_dlen(skb); if (skb_tailroom(skb) < hci_skb_expect(skb)) { kfree_skb(skb); From 6e53a37acd22469a785bfc4a050c4ff7f73c43c8 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:47 -0700 Subject: [PATCH 47/84] Bluetooth: btintel: Simplify btintel_classify_pkt_type() by hci_acl_handle() Simplify btintel_classify_pkt_type() by using hci_acl_handle() instead of: __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle); ... hci_handle(handle) ... Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index bf567b7c5f00..680f96c188d4 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -2745,9 +2745,7 @@ static u8 btintel_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb) * based on their connection handle value range. */ if (iso_capable(hdev) && hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) { - __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle); - - if (hci_handle(handle) >= BTINTEL_ISODATA_HANDLE_BASE) + if (hci_acl_handle(skb) >= BTINTEL_ISODATA_HANDLE_BASE) return HCI_ISODATA_PKT; } From 760163572bb0ab79857c11aafb6eb33e697a22a0 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:48 -0700 Subject: [PATCH 48/84] Bluetooth: btmrvl_sdio: Do not free HCI_VENDOR_PKT frame by hci_recv_frame() For a HCI_VENDOR_PKT frame, hci_recv_frame() does not accept it and will kfree_skb() it directly. But btmrvl_sdio_card_to_host() is still calling hci_recv_frame() for the frame. Fix by freeing it with kfree_skb() directly. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmrvl_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 93932a0d8625..b91fc63bc9fe 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -799,7 +799,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) skb_pull(skb, SDIO_HEADER_LEN); if (btmrvl_process_event(priv, skb)) - hci_recv_frame(hdev, skb); + kfree_skb(skb); hdev->stat.byte_rx += buf_len; break; From 683fa31f4e1f2ac46e1aa5ad3482a382c7d39391 Mon Sep 17 00:00:00 2001 From: "Pawel Zalewski (The Capable Hub)" Date: Mon, 27 Jul 2026 16:51:51 +0100 Subject: [PATCH 49/84] Bluetooth: use a named initializer for acpi_device_id Use a named initializer for the acpi_device_id fields which makes the code more readable and consistent with how lists are initialized in the rest of the kernel code base. While we are at it - unify the list terminator to have a single space between the brackets without a trailing coma. Signed-off-by: Pawel Zalewski (The Capable Hub) Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcm.c | 336 ++++++++++++++++++------------------ drivers/bluetooth/hci_h5.c | 6 +- drivers/bluetooth/hci_qca.c | 12 +- 3 files changed, 177 insertions(+), 177 deletions(-) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 1a4fc3882fd2..01da3fecb536 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -1314,174 +1314,174 @@ static struct bcm_device_data bcm43430_device_data = { }; static const struct acpi_device_id bcm_acpi_match[] = { - { "BCM2E00" }, - { "BCM2E01" }, - { "BCM2E02" }, - { "BCM2E03" }, - { "BCM2E04" }, - { "BCM2E05" }, - { "BCM2E06" }, - { "BCM2E07" }, - { "BCM2E08" }, - { "BCM2E09" }, - { "BCM2E0A" }, - { "BCM2E0B" }, - { "BCM2E0C" }, - { "BCM2E0D" }, - { "BCM2E0E" }, - { "BCM2E0F" }, - { "BCM2E10" }, - { "BCM2E11" }, - { "BCM2E12" }, - { "BCM2E13" }, - { "BCM2E14" }, - { "BCM2E15" }, - { "BCM2E16" }, - { "BCM2E17" }, - { "BCM2E18" }, - { "BCM2E19" }, - { "BCM2E1A" }, - { "BCM2E1B" }, - { "BCM2E1C" }, - { "BCM2E1D" }, - { "BCM2E1F" }, - { "BCM2E20" }, - { "BCM2E21" }, - { "BCM2E22" }, - { "BCM2E23" }, - { "BCM2E24" }, - { "BCM2E25" }, - { "BCM2E26" }, - { "BCM2E27" }, - { "BCM2E28" }, - { "BCM2E29" }, - { "BCM2E2A" }, - { "BCM2E2B" }, - { "BCM2E2C" }, - { "BCM2E2D" }, - { "BCM2E2E" }, - { "BCM2E2F" }, - { "BCM2E30" }, - { "BCM2E31" }, - { "BCM2E32" }, - { "BCM2E33" }, - { "BCM2E34" }, - { "BCM2E35" }, - { "BCM2E36" }, - { "BCM2E37" }, - { "BCM2E38" }, - { "BCM2E39" }, - { "BCM2E3A" }, - { "BCM2E3B" }, - { "BCM2E3C" }, - { "BCM2E3D" }, - { "BCM2E3E" }, - { "BCM2E3F" }, - { "BCM2E40" }, - { "BCM2E41" }, - { "BCM2E42" }, - { "BCM2E43" }, - { "BCM2E44" }, - { "BCM2E45" }, - { "BCM2E46" }, - { "BCM2E47" }, - { "BCM2E48" }, - { "BCM2E49" }, - { "BCM2E4A" }, - { "BCM2E4B" }, - { "BCM2E4C" }, - { "BCM2E4D" }, - { "BCM2E4E" }, - { "BCM2E4F" }, - { "BCM2E50" }, - { "BCM2E51" }, - { "BCM2E52" }, - { "BCM2E53" }, - { "BCM2E54" }, - { "BCM2E55" }, - { "BCM2E56" }, - { "BCM2E57" }, - { "BCM2E58" }, - { "BCM2E59" }, - { "BCM2E5A" }, - { "BCM2E5B" }, - { "BCM2E5C" }, - { "BCM2E5D" }, - { "BCM2E5E" }, - { "BCM2E5F" }, - { "BCM2E60" }, - { "BCM2E61" }, - { "BCM2E62" }, - { "BCM2E63" }, - { "BCM2E64" }, - { "BCM2E65" }, - { "BCM2E66" }, - { "BCM2E67" }, - { "BCM2E68" }, - { "BCM2E69" }, - { "BCM2E6B" }, - { "BCM2E6D" }, - { "BCM2E6E" }, - { "BCM2E6F" }, - { "BCM2E70" }, - { "BCM2E71" }, - { "BCM2E72" }, - { "BCM2E73" }, - { "BCM2E74", (long)&bcm43430_device_data }, - { "BCM2E75", (long)&bcm43430_device_data }, - { "BCM2E76" }, - { "BCM2E77" }, - { "BCM2E78" }, - { "BCM2E79" }, - { "BCM2E7A" }, - { "BCM2E7B", (long)&bcm43430_device_data }, - { "BCM2E7C" }, - { "BCM2E7D" }, - { "BCM2E7E" }, - { "BCM2E7F" }, - { "BCM2E80", (long)&bcm43430_device_data }, - { "BCM2E81" }, - { "BCM2E82" }, - { "BCM2E83" }, - { "BCM2E84" }, - { "BCM2E85" }, - { "BCM2E86" }, - { "BCM2E87" }, - { "BCM2E88" }, - { "BCM2E89", (long)&bcm43430_device_data }, - { "BCM2E8A" }, - { "BCM2E8B" }, - { "BCM2E8C" }, - { "BCM2E8D" }, - { "BCM2E8E" }, - { "BCM2E90" }, - { "BCM2E92" }, - { "BCM2E93" }, - { "BCM2E94", (long)&bcm43430_device_data }, - { "BCM2E95" }, - { "BCM2E96" }, - { "BCM2E97" }, - { "BCM2E98" }, - { "BCM2E99", (long)&bcm43430_device_data }, - { "BCM2E9A" }, - { "BCM2E9B", (long)&bcm43430_device_data }, - { "BCM2E9C" }, - { "BCM2E9D" }, - { "BCM2E9F", (long)&bcm43430_device_data }, - { "BCM2EA0" }, - { "BCM2EA1" }, - { "BCM2EA2", (long)&bcm43430_device_data }, - { "BCM2EA3", (long)&bcm43430_device_data }, - { "BCM2EA4", (long)&bcm43430_device_data }, /* bcm43455 */ - { "BCM2EA5" }, - { "BCM2EA6" }, - { "BCM2EA7" }, - { "BCM2EA8" }, - { "BCM2EA9" }, - { "BCM2EAA", (long)&bcm43430_device_data }, - { "BCM2EAB", (long)&bcm43430_device_data }, - { "BCM2EAC", (long)&bcm43430_device_data }, - { }, + { .id = "BCM2E00" }, + { .id = "BCM2E01" }, + { .id = "BCM2E02" }, + { .id = "BCM2E03" }, + { .id = "BCM2E04" }, + { .id = "BCM2E05" }, + { .id = "BCM2E06" }, + { .id = "BCM2E07" }, + { .id = "BCM2E08" }, + { .id = "BCM2E09" }, + { .id = "BCM2E0A" }, + { .id = "BCM2E0B" }, + { .id = "BCM2E0C" }, + { .id = "BCM2E0D" }, + { .id = "BCM2E0E" }, + { .id = "BCM2E0F" }, + { .id = "BCM2E10" }, + { .id = "BCM2E11" }, + { .id = "BCM2E12" }, + { .id = "BCM2E13" }, + { .id = "BCM2E14" }, + { .id = "BCM2E15" }, + { .id = "BCM2E16" }, + { .id = "BCM2E17" }, + { .id = "BCM2E18" }, + { .id = "BCM2E19" }, + { .id = "BCM2E1A" }, + { .id = "BCM2E1B" }, + { .id = "BCM2E1C" }, + { .id = "BCM2E1D" }, + { .id = "BCM2E1F" }, + { .id = "BCM2E20" }, + { .id = "BCM2E21" }, + { .id = "BCM2E22" }, + { .id = "BCM2E23" }, + { .id = "BCM2E24" }, + { .id = "BCM2E25" }, + { .id = "BCM2E26" }, + { .id = "BCM2E27" }, + { .id = "BCM2E28" }, + { .id = "BCM2E29" }, + { .id = "BCM2E2A" }, + { .id = "BCM2E2B" }, + { .id = "BCM2E2C" }, + { .id = "BCM2E2D" }, + { .id = "BCM2E2E" }, + { .id = "BCM2E2F" }, + { .id = "BCM2E30" }, + { .id = "BCM2E31" }, + { .id = "BCM2E32" }, + { .id = "BCM2E33" }, + { .id = "BCM2E34" }, + { .id = "BCM2E35" }, + { .id = "BCM2E36" }, + { .id = "BCM2E37" }, + { .id = "BCM2E38" }, + { .id = "BCM2E39" }, + { .id = "BCM2E3A" }, + { .id = "BCM2E3B" }, + { .id = "BCM2E3C" }, + { .id = "BCM2E3D" }, + { .id = "BCM2E3E" }, + { .id = "BCM2E3F" }, + { .id = "BCM2E40" }, + { .id = "BCM2E41" }, + { .id = "BCM2E42" }, + { .id = "BCM2E43" }, + { .id = "BCM2E44" }, + { .id = "BCM2E45" }, + { .id = "BCM2E46" }, + { .id = "BCM2E47" }, + { .id = "BCM2E48" }, + { .id = "BCM2E49" }, + { .id = "BCM2E4A" }, + { .id = "BCM2E4B" }, + { .id = "BCM2E4C" }, + { .id = "BCM2E4D" }, + { .id = "BCM2E4E" }, + { .id = "BCM2E4F" }, + { .id = "BCM2E50" }, + { .id = "BCM2E51" }, + { .id = "BCM2E52" }, + { .id = "BCM2E53" }, + { .id = "BCM2E54" }, + { .id = "BCM2E55" }, + { .id = "BCM2E56" }, + { .id = "BCM2E57" }, + { .id = "BCM2E58" }, + { .id = "BCM2E59" }, + { .id = "BCM2E5A" }, + { .id = "BCM2E5B" }, + { .id = "BCM2E5C" }, + { .id = "BCM2E5D" }, + { .id = "BCM2E5E" }, + { .id = "BCM2E5F" }, + { .id = "BCM2E60" }, + { .id = "BCM2E61" }, + { .id = "BCM2E62" }, + { .id = "BCM2E63" }, + { .id = "BCM2E64" }, + { .id = "BCM2E65" }, + { .id = "BCM2E66" }, + { .id = "BCM2E67" }, + { .id = "BCM2E68" }, + { .id = "BCM2E69" }, + { .id = "BCM2E6B" }, + { .id = "BCM2E6D" }, + { .id = "BCM2E6E" }, + { .id = "BCM2E6F" }, + { .id = "BCM2E70" }, + { .id = "BCM2E71" }, + { .id = "BCM2E72" }, + { .id = "BCM2E73" }, + { .id = "BCM2E74", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2E75", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2E76" }, + { .id = "BCM2E77" }, + { .id = "BCM2E78" }, + { .id = "BCM2E79" }, + { .id = "BCM2E7A" }, + { .id = "BCM2E7B", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2E7C" }, + { .id = "BCM2E7D" }, + { .id = "BCM2E7E" }, + { .id = "BCM2E7F" }, + { .id = "BCM2E80", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2E81" }, + { .id = "BCM2E82" }, + { .id = "BCM2E83" }, + { .id = "BCM2E84" }, + { .id = "BCM2E85" }, + { .id = "BCM2E86" }, + { .id = "BCM2E87" }, + { .id = "BCM2E88" }, + { .id = "BCM2E89", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2E8A" }, + { .id = "BCM2E8B" }, + { .id = "BCM2E8C" }, + { .id = "BCM2E8D" }, + { .id = "BCM2E8E" }, + { .id = "BCM2E90" }, + { .id = "BCM2E92" }, + { .id = "BCM2E93" }, + { .id = "BCM2E94", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2E95" }, + { .id = "BCM2E96" }, + { .id = "BCM2E97" }, + { .id = "BCM2E98" }, + { .id = "BCM2E99", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2E9A" }, + { .id = "BCM2E9B", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2E9C" }, + { .id = "BCM2E9D" }, + { .id = "BCM2E9F", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2EA0" }, + { .id = "BCM2EA1" }, + { .id = "BCM2EA2", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2EA3", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2EA4", .driver_data = (long)&bcm43430_device_data }, /* bcm43455 */ + { .id = "BCM2EA5" }, + { .id = "BCM2EA6" }, + { .id = "BCM2EA7" }, + { .id = "BCM2EA8" }, + { .id = "BCM2EA9" }, + { .id = "BCM2EAA", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2EAB", .driver_data = (long)&bcm43430_device_data }, + { .id = "BCM2EAC", .driver_data = (long)&bcm43430_device_data }, + { } }; MODULE_DEVICE_TABLE(acpi, bcm_acpi_match); #endif diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 93cdde981840..60b90f1e11fc 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -1124,10 +1124,10 @@ static const struct h5_device_data h5_data_rtl8723bs = { #ifdef CONFIG_ACPI static const struct acpi_device_id h5_acpi_match[] = { #ifdef CONFIG_BT_HCIUART_RTL - { "OBDA0623", (kernel_ulong_t)&h5_data_rtl8723bs }, - { "OBDA8723", (kernel_ulong_t)&h5_data_rtl8723bs }, + { .id = "OBDA0623", .driver_data = (kernel_ulong_t)&h5_data_rtl8723bs }, + { .id = "OBDA8723", .driver_data = (kernel_ulong_t)&h5_data_rtl8723bs }, #endif - { }, + { } }; MODULE_DEVICE_TABLE(acpi, h5_acpi_match); #endif diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index e6d107f67759..345f602e9ce2 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -2792,12 +2792,12 @@ MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id qca_bluetooth_acpi_match[] = { - { "QCOM2066", (kernel_ulong_t)&qca_soc_data_qca2066 }, - { "QCOM6390", (kernel_ulong_t)&qca_soc_data_qca6390 }, - { "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 }, - { "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 }, - { "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 }, - { }, + { .id = "QCOM2066", .driver_data = (kernel_ulong_t)&qca_soc_data_qca2066 }, + { .id = "QCOM6390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 }, + { .id = "DLA16390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 }, + { .id = "DLB16390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 }, + { .id = "DLB26390", .driver_data = (kernel_ulong_t)&qca_soc_data_qca6390 }, + { } }; MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match); #endif From 80718e2e0fbce54d57146564455f123daebfe11a Mon Sep 17 00:00:00 2001 From: "Pawel Zalewski (The Capable Hub)" Date: Mon, 27 Jul 2026 16:51:52 +0100 Subject: [PATCH 50/84] Bluetooth: hci_intel: drop unused assignment of acpi_device_id::driver_data This module sets the acpi_device_id::driver_data to 0 but the field is not actually used within the module, we can just drop it from the table. While we are at it - use a named initializer for the acpi_device_id::id field and drop setting the list terminator fields explicitly as well. Signed-off-by: Pawel Zalewski (The Capable Hub) Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_intel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index c31105b91e47..ecf597f3e201 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -1057,8 +1057,8 @@ static const struct hci_uart_proto intel_proto = { #ifdef CONFIG_ACPI static const struct acpi_device_id intel_acpi_match[] = { - { "INT33E1", 0 }, - { "INT33E3", 0 }, + { .id = "INT33E1" }, + { .id = "INT33E3" }, { } }; MODULE_DEVICE_TABLE(acpi, intel_acpi_match); From 39bbe7b7386fb3cdbb1c02a240e40d45f1435778 Mon Sep 17 00:00:00 2001 From: Chandrashekar Devegowda Date: Mon, 27 Jul 2026 10:51:02 +0530 Subject: [PATCH 51/84] Bluetooth: btintel_pcie: Add vendor_reset PCI sysfs for PLDR Add a read-write sysfs entry at /sys/bus/pci/devices//vendor_reset to allow userspace to trigger PLDR (Product Level Device Reset). Reading the attribute displays supported reset types. Writing integer 0 triggers PLDR. Any other input is rejected with -EINVAL and a warning log. Signed-off-by: Chandrashekar Devegowda Signed-off-by: Luiz Augusto von Dentz --- .../sysfs-bus-pci-drivers-btintel_pcie | 15 ++++++++ MAINTAINERS | 1 + drivers/bluetooth/btintel_pcie.c | 38 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie b/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie new file mode 100644 index 000000000000..cceec6ac96bc --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie @@ -0,0 +1,15 @@ +What: /sys/bus/pci/devices//vendor_reset +Date: 22-Jul-2026 +KernelVersion: 6.17 +Contact: linux-bluetooth@vger.kernel.org +Description: This read-write attribute allows userspace to trigger a + Product Level Device Reset (PLDR) on Intel PCIe Bluetooth + controllers. Reading the attribute displays the supported + reset type. Writing integer 0 triggers PLDR. Any other + input is rejected with -EINVAL. + + PLDR resets the entire on-chip platform shared between + Bluetooth and WiFi. This means any driver attached to + the WiFi device that shares hardware with this Bluetooth + device will be released, the platform will be reset, and + both the Bluetooth and WiFi devices will be re-probed. diff --git a/MAINTAINERS b/MAINTAINERS index 08e43bc09735..891c064a881b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4719,6 +4719,7 @@ S: Supported W: http://www.bluez.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git +F: Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie F: Documentation/devicetree/bindings/net/bluetooth/ F: drivers/bluetooth/ diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index ef42b8d11d4d..005c77a4f5eb 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -2790,6 +2790,43 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev) btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); } +static ssize_t vendor_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val; + struct pci_dev *pdev = to_pci_dev(dev); + struct btintel_pcie_data *data = pci_get_drvdata(pdev); + + if (!data || !data->hdev) + return -ENODEV; + + if (kstrtouint(buf, 10, &val) || val != 0) { + bt_dev_warn(data->hdev, "PLDR rejected: invalid input"); + return -EINVAL; + } + + bt_dev_info(data->hdev, "PLDR triggered via sysfs"); + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR); + + return count; +} + +static ssize_t vendor_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "0 - PLDR\n"); +} + +static DEVICE_ATTR_RW(vendor_reset); + +static struct attribute *btintel_pcie_attrs[] = { + &dev_attr_vendor_reset.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(btintel_pcie); + static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) { struct btintel_pcie_dev_recovery *rec; @@ -3250,6 +3287,7 @@ static struct pci_driver btintel_pcie_driver = { .probe = btintel_pcie_probe, .remove = btintel_pcie_remove, .driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops), + .dev_groups = btintel_pcie_groups, #ifdef CONFIG_DEV_COREDUMP .driver.coredump = btintel_pcie_coredump #endif From 2f8784cfe8a961e5f0d85210c5175ebf5d438d93 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 24 Jun 2026 14:47:34 -0400 Subject: [PATCH 52/84] Bluetooth: Add support for Shorter Connection Interval (SCI) feature Add HCI command, event and feature bit definitions for the Bluetooth 6.2 Shorter Connection Interval feature: Commands: - HCI_OP_LE_CONN_RATE (0x20a1) - Connection Rate Request - HCI_OP_LE_SET_DEF_RATE (0x20a2) - Set Default Rate Parameters - HCI_OP_LE_READ_CONN_INTERVAL (0x20a3) - Read Min Supported Connection Interval Events: - HCI_EVT_LE_CONN_RATE_CHANGE (0x37) - Connection Rate Change Feature bits: - HCI_LE_SCI - Shorter Connection Intervals - HCI_LE_SCI_HOST - Shorter Connection Intervals (Host Support) During controller init, when SCI is supported: - Set Shorter Connection Intervals (Host Support) feature via LE Set Host Feature - Read Minimum Supported Connection Interval - Set Default Rate Parameters The Connection Rate Change event handler updates the connection interval, latency and supervision timeout on the hci_conn. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 53 +++++++++++++++++++++++++ include/net/bluetooth/hci_core.h | 9 +++++ net/bluetooth/hci_event.c | 64 +++++++++++++++++++++++++++++++ net/bluetooth/hci_sync.c | 66 +++++++++++++++++++++++++++++++- 4 files changed, 190 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d557bdf9ae57..cd3520a29131 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -653,6 +653,8 @@ enum { #define HCI_LE_LL_EXT_FEATURE 0x80 #define HCI_LE_CS 0x40 #define HCI_LE_CS_HOST 0x80 +#define HCI_LE_SCI 0x01 /* byte 9 - Shorter Connection Intervals */ +#define HCI_LE_SCI_HOST 0x02 /* byte 9 - Shorter Connection Intervals (Host) */ /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 @@ -2489,6 +2491,46 @@ struct hci_cp_le_set_host_feature_v2 { __u8 bit_value; } __packed; +#define HCI_OP_LE_CONN_RATE 0x20a1 +struct hci_cp_le_conn_rate { + __le16 handle; + __le16 interval_min; + __le16 interval_max; + __le16 subrate_min; + __le16 subrate_max; + __le16 max_latency; + __le16 cont_num; + __le16 supv_timeout; + __le16 min_ce_len; + __le16 max_ce_len; +} __packed; + +#define HCI_OP_LE_SET_DEF_RATE 0x20a2 +struct hci_cp_le_set_def_rate { + __le16 interval_min; + __le16 interval_max; + __le16 subrate_min; + __le16 subrate_max; + __le16 max_latency; + __le16 cont_num; + __le16 supv_timeout; + __le16 min_ce_len; + __le16 max_ce_len; +} __packed; + +#define HCI_OP_LE_READ_CONN_INTERVAL 0x20a3 +struct hci_le_conn_interval_group { + __le16 min; + __le16 max; + __le16 stride; +} __packed; + +struct hci_rp_le_read_conn_interval { + __u8 status; + __u8 num_grps; + struct hci_le_conn_interval_group grps[]; +} __packed; + /* ---- HCI Events ---- */ struct hci_ev_status { __u8 status; @@ -3303,6 +3345,17 @@ struct hci_evt_le_cs_test_end_complete { __u8 status; } __packed; +#define HCI_EVT_LE_CONN_RATE_CHANGE 0x37 +struct hci_evt_le_conn_rate_change { + __u8 status; + __le16 handle; + __le16 interval; + __le16 subrate; + __le16 latency; + __le16 cont_number; + __le16 supv_timeout; +} __packed; + #define HCI_EV_VENDOR 0xff /* Internal events generated by Bluetooth stack */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3df59849dcbe..f48f875022d4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -416,6 +416,7 @@ struct hci_dev { __u16 le_conn_max_interval; __u16 le_conn_latency; __u16 le_supv_timeout; + __u16 le_min_rate_interval; __u16 le_def_tx_len; __u16 le_def_tx_time; __u16 le_max_tx_len; @@ -720,6 +721,11 @@ struct hci_conn { __u16 le_conn_interval; __u16 le_conn_latency; __u16 le_supv_timeout; + __u16 le_rate_interval; + __u16 le_subrate; + __u16 le_rate_latency; + __u16 le_cont_num; + __u16 le_rate_supv_timeout; __u8 le_adv_data[HCI_MAX_EXT_AD_LENGTH]; __u8 le_adv_data_len; __u8 le_per_adv_data[HCI_MAX_PER_AD_TOT_LEN]; @@ -2079,6 +2085,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define le_cs_host_capable(dev) \ ((dev)->le_features[5] & HCI_LE_CS_HOST) +#define le_sci_capable(dev) \ + ((dev)->le_features[9] & HCI_LE_SCI) + #define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \ (!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG))) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ea858391c789..7b539c7602b0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1241,6 +1241,39 @@ static u8 hci_cc_le_read_local_features(struct hci_dev *hdev, void *data, return rp->status; } +static u8 hci_cc_le_read_conn_interval(struct hci_dev *hdev, void *data, + struct sk_buff *skb) +{ + struct hci_rp_le_read_conn_interval *rp = data; + u16 min_interval = 0; + int i; + + bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); + + if (rp->status) + return rp->status; + + if (skb->len < flex_array_size(rp, grps, rp->num_grps)) { + bt_dev_err(hdev, "Invalid response length for 0x%4.4x", + HCI_OP_LE_READ_CONN_INTERVAL); + return HCI_ERROR_UNSPECIFIED; + } + + /* Store the smallest minimum supported connection interval reported by + * the controller so the default rate parameters can be clamped to it. + */ + for (i = 0; i < rp->num_grps; i++) { + u16 min = le16_to_cpu(rp->grps[i].min); + + if (!min_interval || min < min_interval) + min_interval = min; + } + + hdev->le_min_rate_interval = min_interval; + + return rp->status; +} + static u8 hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, void *data, struct sk_buff *skb) { @@ -4153,6 +4186,9 @@ static const struct hci_cc { sizeof(struct hci_rp_le_read_buffer_size)), HCI_CC(HCI_OP_LE_READ_LOCAL_FEATURES, hci_cc_le_read_local_features, sizeof(struct hci_rp_le_read_local_features)), + HCI_CC_VL(HCI_OP_LE_READ_CONN_INTERVAL, hci_cc_le_read_conn_interval, + sizeof(struct hci_rp_le_read_conn_interval), + HCI_MAX_EVENT_SIZE), HCI_CC(HCI_OP_LE_READ_ADV_TX_POWER, hci_cc_le_read_adv_tx_power, sizeof(struct hci_rp_le_read_adv_tx_power)), HCI_CC(HCI_OP_USER_CONFIRM_REPLY, hci_cc_user_confirm_reply, @@ -7363,6 +7399,31 @@ static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev, hci_dev_unlock(hdev); } +static void hci_le_conn_rate_change_evt(struct hci_dev *hdev, void *data, + struct sk_buff *skb) +{ + struct hci_evt_le_conn_rate_change *ev = data; + struct hci_conn *conn; + + bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); + + if (ev->status) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + conn->le_rate_interval = le16_to_cpu(ev->interval); + conn->le_subrate = le16_to_cpu(ev->subrate); + conn->le_rate_latency = le16_to_cpu(ev->latency); + conn->le_cont_num = le16_to_cpu(ev->cont_number); + conn->le_rate_supv_timeout = le16_to_cpu(ev->supv_timeout); + } + + hci_dev_unlock(hdev); +} + #define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \ [_op] = { \ .func = _func, \ @@ -7474,6 +7535,9 @@ static const struct hci_le_ev { sizeof(struct hci_evt_le_read_all_remote_features_complete), HCI_MAX_EVENT_SIZE), + /* [0x37 = HCI_EVT_LE_CONN_RATE_CHANGE] */ + HCI_LE_EV(HCI_EVT_LE_CONN_RATE_CHANGE, hci_le_conn_rate_change_evt, + sizeof(struct hci_evt_le_conn_rate_change)), }; static void hci_le_meta_evt(struct hci_dev *hdev, void *data, diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 7779d9d1663a..576a93d6ed2b 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4541,6 +4541,10 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) events[6] |= 0x02; /* LE CS Subevent Result Continue event */ events[6] |= 0x04; /* LE CS Test End Complete event */ } + + if (le_sci_capable(hdev)) + events[6] |= 0x40; /* LE Connection Rate Change event */ + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), events, HCI_CMD_TIMEOUT); } @@ -4709,12 +4713,59 @@ static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u16 bit, u8 value) sizeof(cp), &cp, HCI_CMD_TIMEOUT); } +static int hci_le_read_conn_interval_sync(struct hci_dev *hdev) +{ + if (!le_sci_capable(hdev)) + return 0; + + return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_CONN_INTERVAL, + 0, NULL, HCI_CMD_TIMEOUT); +} + +static int hci_le_set_def_rate_sync(struct hci_dev *hdev) +{ + struct hci_cp_le_set_def_rate cp; + u16 interval_min = 0x000a; /* 1.25 ms */ + u16 interval_max = 0x0078; /* 15 ms */ + + if (!le_sci_capable(hdev)) + return 0; + + /* Clamp the interval range to the controller's minimum supported + * connection interval (read via HCI_OP_LE_READ_CONN_INTERVAL) so the + * default rate parameters are not rejected. The maximum is raised as + * well if needed to keep interval_min <= interval_max. + */ + if (hdev->le_min_rate_interval > interval_min) { + interval_min = hdev->le_min_rate_interval; + if (interval_min > interval_max) + interval_max = interval_min; + } + + memset(&cp, 0, sizeof(cp)); + + /* Use the HIDS 1.2 recommended Full Range mode values as the default + * rate parameters (see HOGP v1.2 spec). Connection intervals are in + * units of 0.125 ms and the supervision timeout is in units of 10 ms. + */ + cp.interval_min = cpu_to_le16(interval_min); + cp.interval_max = cpu_to_le16(interval_max); + cp.subrate_min = cpu_to_le16(0x0001); + cp.subrate_max = cpu_to_le16(0x0004); + cp.max_latency = cpu_to_le16(0x0000); + cp.cont_num = cpu_to_le16(0x0001); + cp.supv_timeout = cpu_to_le16(0x000c); /* 120 ms */ + + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_DEF_RATE, + sizeof(cp), &cp, HCI_CMD_TIMEOUT); +} + /* Set Host Features, each feature needs to be sent separately since * HCI_OP_LE_SET_HOST_FEATURE doesn't support setting all of them at once. */ static int hci_le_set_host_features_sync(struct hci_dev *hdev) { - int err; + int err = 0; if (cis_capable(hdev)) { /* Connected Isochronous Channels (Host Support) */ @@ -4725,9 +4776,16 @@ static int hci_le_set_host_features_sync(struct hci_dev *hdev) return err; } - if (le_cs_capable(hdev)) + if (le_cs_capable(hdev)) { /* Channel Sounding (Host Support) */ err = hci_le_set_host_feature_sync(hdev, 47, 0x01); + if (err) + return err; + } + + if (le_sci_capable(hdev)) + /* Shorter Connection Intervals (Host Support) */ + err = hci_le_set_host_feature_sync(hdev, 73, 0x01); return err; } @@ -4760,6 +4818,10 @@ static const struct hci_init_stage le_init3[] = { HCI_INIT(hci_set_le_support_sync), /* HCI_OP_LE_SET_HOST_FEATURE */ HCI_INIT(hci_le_set_host_features_sync), + /* HCI_OP_LE_READ_CONN_INTERVAL */ + HCI_INIT(hci_le_read_conn_interval_sync), + /* HCI_OP_LE_SET_DEF_RATE */ + HCI_INIT(hci_le_set_def_rate_sync), {} }; From 5ec6f300e26ee1c7d34e0d7e9acd9083af124b55 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 24 Jul 2026 10:57:25 -0400 Subject: [PATCH 53/84] Bluetooth: Add MGMT Shorter Connection Interval setting Add MGMT_SETTING_SCI (bit 25) to advertise support for the Shorter Connection Interval (SCI) feature. It is reported in the supported settings whenever the controller is SCI capable, and in the current settings whenever LE is enabled and the controller is SCI capable (SCI has no separate enable command, so it is a passive capability). Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 2 ++ include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f48f875022d4..b1f531b3b6ab 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2087,6 +2087,8 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define le_sci_capable(dev) \ ((dev)->le_features[9] & HCI_LE_SCI) +#define le_sci_enabled(dev) \ + (le_enabled(dev) && le_sci_capable(dev)) #define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \ (!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG))) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 08daed7a96d5..b285441db55b 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -118,6 +118,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_LL_PRIVACY BIT(22) #define MGMT_SETTING_PAST_SENDER BIT(23) #define MGMT_SETTING_PAST_RECEIVER BIT(24) +#define MGMT_SETTING_SCI BIT(25) #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 167d75e34526..fec41af69d62 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -861,6 +861,9 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (past_receiver_capable(hdev)) settings |= MGMT_SETTING_PAST_RECEIVER; + if (le_sci_capable(hdev)) + settings |= MGMT_SETTING_SCI; + settings |= MGMT_SETTING_PHY_CONFIGURATION; return settings; @@ -952,6 +955,9 @@ static u32 get_current_settings(struct hci_dev *hdev) if (past_receiver_enabled(hdev)) settings |= MGMT_SETTING_PAST_RECEIVER; + if (le_sci_enabled(hdev)) + settings |= MGMT_SETTING_SCI; + return settings; } From 19129d7037beafe16507d6616d8a54eaa49b2eda Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 24 Jul 2026 10:58:06 -0400 Subject: [PATCH 54/84] Bluetooth: Add MGMT Load Connection Subrate command Add MGMT_OP_LOAD_CONN_SUBRATE (0x005C) command to load per-device connection subrate parameters when the SCI feature is supported. Add MGMT_EV_CONN_SUBRATE (0x0033) event to notify userspace when connection rate changes occur via the LE Connection Rate Change HCI event. Add subrate fields (subrate_min, subrate_max, max_latency, cont_num) to struct hci_conn_params to store the loaded subrate parameters, and the corresponding le_rate_* fields to struct hci_conn to track the parameters currently in use. When a single entry is loaded for an already-connected central, or on connection completion, the LE Connection Rate Request procedure is initiated to apply the parameters. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 10 +++ include/net/bluetooth/hci_sync.h | 1 + include/net/bluetooth/mgmt.h | 28 +++++++ net/bluetooth/hci_event.c | 32 ++++++-- net/bluetooth/hci_sync.c | 69 ++++++++++++++++ net/bluetooth/mgmt.c | 131 +++++++++++++++++++++++++++++++ 6 files changed, 263 insertions(+), 8 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b1f531b3b6ab..01b938c4b24a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -818,6 +818,14 @@ struct hci_conn_params { u16 conn_latency; u16 supervision_timeout; + u16 rate_min_interval; + u16 rate_max_interval; + u16 subrate_min; + u16 subrate_max; + u16 max_latency; + u16 cont_num; + u16 rate_supv_timeout; + enum { HCI_AUTO_CONN_DISABLED, HCI_AUTO_CONN_REPORT, @@ -2505,6 +2513,8 @@ void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev, int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip); void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle, bdaddr_t *bdaddr, u8 addr_type); +void mgmt_conn_subrate_notify(struct hci_dev *hdev, struct hci_conn *conn, + u8 status); int hci_abort_conn(struct hci_conn *conn, u8 reason); void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h index 0756d6fe77d4..a6579a868678 100644 --- a/include/net/bluetooth/hci_sync.h +++ b/include/net/bluetooth/hci_sync.h @@ -183,6 +183,7 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn); int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn); int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn, struct hci_conn_params *params); +int hci_le_conn_rate_request(struct hci_dev *hdev, struct hci_conn *conn); int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn); int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index b285441db55b..1e22eab1081c 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -894,6 +894,23 @@ struct mgmt_cp_hci_cmd_sync { } __packed; #define MGMT_HCI_CMD_SYNC_SIZE 6 +#define MGMT_OP_LOAD_CONN_SUBRATE 0x005C +struct mgmt_conn_subrate { + struct mgmt_addr_info addr; + __le16 min_interval; + __le16 max_interval; + __le16 subrate_min; + __le16 subrate_max; + __le16 max_latency; + __le16 cont_num; + __le16 supv_timeout; +} __packed; +struct mgmt_cp_load_conn_subrate { + __le16 param_count; + struct mgmt_conn_subrate params[] __counted_by_le(param_count); +} __packed; +#define MGMT_LOAD_CONN_SUBRATE_SIZE 2 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -1193,3 +1210,14 @@ struct mgmt_ev_mesh_device_found { struct mgmt_ev_mesh_pkt_cmplt { __u8 handle; } __packed; + +#define MGMT_EV_CONN_SUBRATE 0x0033 +struct mgmt_ev_conn_subrate { + struct mgmt_addr_info addr; + __u8 status; + __le16 interval; + __le16 subrate; + __le16 latency; + __le16 cont_num; + __le16 supv_timeout; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7b539c7602b0..9764efb9e293 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -5903,6 +5903,17 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, } } + /* If we are central and have subrate parameters stored, queue a + * connection rate request to apply them. + */ + if (conn->role == HCI_ROLE_MASTER && le_sci_capable(hdev)) { + struct hci_conn_params *p; + + p = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (p && p->subrate_max) + hci_le_conn_rate_request(hdev, conn); + } + unlock: hci_update_passive_scan(hdev); hci_dev_unlock(hdev); @@ -7407,18 +7418,23 @@ static void hci_le_conn_rate_change_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); - if (ev->status) - return; - hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { - conn->le_rate_interval = le16_to_cpu(ev->interval); - conn->le_subrate = le16_to_cpu(ev->subrate); - conn->le_rate_latency = le16_to_cpu(ev->latency); - conn->le_cont_num = le16_to_cpu(ev->cont_number); - conn->le_rate_supv_timeout = le16_to_cpu(ev->supv_timeout); + /* Only update the stored rate parameters on success; on + * failure the values in the event are not valid. Userspace is + * notified either way. + */ + if (!ev->status) { + conn->le_rate_interval = le16_to_cpu(ev->interval); + conn->le_subrate = le16_to_cpu(ev->subrate); + conn->le_rate_latency = le16_to_cpu(ev->latency); + conn->le_cont_num = le16_to_cpu(ev->cont_number); + conn->le_rate_supv_timeout = + le16_to_cpu(ev->supv_timeout); + } + mgmt_conn_subrate_notify(hdev, conn, ev->status); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 576a93d6ed2b..307fd47f8459 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7355,6 +7355,75 @@ int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn, sizeof(cp), &cp, HCI_CMD_TIMEOUT); } +static int hci_le_conn_rate_request_sync(struct hci_dev *hdev, void *data) +{ + struct hci_conn *conn = data; + struct hci_conn_params *params; + struct hci_cp_le_conn_rate cp; + + hci_dev_lock(hdev); + + /* The request was queued asynchronously so re-validate the connection + * and its parameters under hdev->lock. The connection may have been + * torn down, or may not have a valid handle yet (still connecting), + * and the parameters may have been removed in the meantime (e.g. by + * Load Connection Parameters). Snapshot the rate values so the + * blocking command below can run without holding hdev->lock. + */ + if (!hci_conn_valid(hdev, conn) || + HCI_CONN_HANDLE_UNSET(conn->handle)) { + hci_dev_unlock(hdev); + return -ECANCELED; + } + + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (!params) { + hci_dev_unlock(hdev); + return -ECANCELED; + } + + memset(&cp, 0, sizeof(cp)); + cp.handle = cpu_to_le16(conn->handle); + cp.interval_min = cpu_to_le16(params->rate_min_interval); + cp.interval_max = cpu_to_le16(params->rate_max_interval); + cp.subrate_min = cpu_to_le16(params->subrate_min); + cp.subrate_max = cpu_to_le16(params->subrate_max); + cp.max_latency = cpu_to_le16(params->max_latency); + cp.cont_num = cpu_to_le16(params->cont_num); + cp.supv_timeout = cpu_to_le16(params->rate_supv_timeout); + cp.min_ce_len = cpu_to_le16(0x0000); + cp.max_ce_len = cpu_to_le16(0x0000); + + hci_dev_unlock(hdev); + + return __hci_cmd_sync_status(hdev, HCI_OP_LE_CONN_RATE, + sizeof(cp), &cp, HCI_CMD_TIMEOUT); +} + +static void hci_le_conn_rate_request_destroy(struct hci_dev *hdev, void *data, + int err) +{ + struct hci_conn *conn = data; + + hci_conn_put(conn); +} + +int hci_le_conn_rate_request(struct hci_dev *hdev, struct hci_conn *conn) +{ + int err; + + /* Hold a reference to the connection so it cannot be freed while the + * request is pending or running on the cmd_sync worker. + */ + err = hci_cmd_sync_queue(hdev, hci_le_conn_rate_request_sync, + hci_conn_get(conn), + hci_le_conn_rate_request_destroy); + if (err < 0) + hci_conn_put(conn); + + return err; +} + static void create_pa_complete(struct hci_dev *hdev, void *data, int err) { struct hci_conn *conn = data; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fec41af69d62..09edd72acc22 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -8176,6 +8176,118 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, NULL, 0); } +static int load_conn_subrate(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_load_conn_subrate *cp = data; + const u16 max_param_count = ((U16_MAX - sizeof(*cp)) / + sizeof(struct mgmt_conn_subrate)); + u16 param_count, expected_len; + int i; + + if (!lmp_le_capable(hdev) || !le_sci_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_SUBRATE, + MGMT_STATUS_NOT_SUPPORTED); + + param_count = __le16_to_cpu(cp->param_count); + if (param_count > max_param_count) { + bt_dev_err(hdev, "too big param_count value %u", param_count); + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_SUBRATE, + MGMT_STATUS_INVALID_PARAMS); + } + + expected_len = struct_size(cp, params, param_count); + if (expected_len != len) { + bt_dev_err(hdev, "expected %u bytes, got %u bytes", + expected_len, len); + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_SUBRATE, + MGMT_STATUS_INVALID_PARAMS); + } + + bt_dev_dbg(hdev, "param_count %u", param_count); + + hci_dev_lock(hdev); + + for (i = 0; i < param_count; i++) { + struct mgmt_conn_subrate *param = &cp->params[i]; + struct hci_conn_params *hci_param; + u16 min, max, subrate_min, subrate_max; + u16 max_latency, cont_num, supv_timeout; + u8 addr_type; + + bt_dev_dbg(hdev, "Adding subrate %pMR (type %u)", + ¶m->addr.bdaddr, param->addr.type); + + if (param->addr.type == BDADDR_LE_PUBLIC) { + addr_type = ADDR_LE_DEV_PUBLIC; + } else if (param->addr.type == BDADDR_LE_RANDOM) { + addr_type = ADDR_LE_DEV_RANDOM; + } else { + bt_dev_err(hdev, "ignoring invalid connection subrate parameters"); + continue; + } + + min = le16_to_cpu(param->min_interval); + max = le16_to_cpu(param->max_interval); + subrate_min = le16_to_cpu(param->subrate_min); + subrate_max = le16_to_cpu(param->subrate_max); + max_latency = le16_to_cpu(param->max_latency); + cont_num = le16_to_cpu(param->cont_num); + supv_timeout = le16_to_cpu(param->supv_timeout); + + /* Validate the parameters before storing them. Reject + * logically inconsistent values instead of forwarding them to + * the controller. + */ + if (min > max || subrate_min > subrate_max || + subrate_min < 1 || supv_timeout < 1) { + bt_dev_err(hdev, "ignoring invalid connection subrate parameters"); + continue; + } + + hci_param = hci_conn_params_add(hdev, ¶m->addr.bdaddr, + addr_type); + if (!hci_param) { + bt_dev_err(hdev, "failed to add connection parameters"); + continue; + } + + hci_param->rate_min_interval = min; + hci_param->rate_max_interval = max; + hci_param->subrate_min = subrate_min; + hci_param->subrate_max = subrate_max; + hci_param->max_latency = max_latency; + hci_param->cont_num = cont_num; + hci_param->rate_supv_timeout = supv_timeout; + + /* If the device is connected as central check if the + * connection rate parameters need to be updated. + */ + if (!i && param_count == 1) { + struct hci_conn *conn; + + conn = hci_conn_hash_lookup_le(hdev, + &hci_param->addr, + addr_type); + if (conn && conn->state == BT_CONNECTED && + conn->role == HCI_ROLE_MASTER && + (conn->le_rate_interval < min || + conn->le_rate_interval > max || + conn->le_subrate < subrate_min || + conn->le_subrate > subrate_max || + conn->le_rate_latency != max_latency || + conn->le_cont_num != cont_num || + conn->le_rate_supv_timeout != supv_timeout)) + hci_le_conn_rate_request(hdev, conn); + } + } + + hci_dev_unlock(hdev); + + return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_SUBRATE, 0, + NULL, 0); +} + static int set_external_config(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -9593,6 +9705,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_VAR_LEN }, { mesh_send_cancel, MGMT_MESH_SEND_CANCEL_SIZE }, { mgmt_hci_cmd_sync, MGMT_HCI_CMD_SYNC_SIZE, HCI_MGMT_VAR_LEN }, + { load_conn_subrate, MGMT_LOAD_CONN_SUBRATE_SIZE, + HCI_MGMT_VAR_LEN }, }; void mgmt_index_added(struct hci_dev *hdev) @@ -10741,6 +10855,23 @@ int mgmt_init(void) return hci_mgmt_chan_register(&chan); } +void mgmt_conn_subrate_notify(struct hci_dev *hdev, struct hci_conn *conn, + u8 status) +{ + struct mgmt_ev_conn_subrate ev; + + bacpy(&ev.addr.bdaddr, &conn->dst); + ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type); + ev.status = mgmt_status(status); + ev.interval = cpu_to_le16(conn->le_rate_interval); + ev.subrate = cpu_to_le16(conn->le_subrate); + ev.latency = cpu_to_le16(conn->le_rate_latency); + ev.cont_num = cpu_to_le16(conn->le_cont_num); + ev.supv_timeout = cpu_to_le16(conn->le_rate_supv_timeout); + + mgmt_event(MGMT_EV_CONN_SUBRATE, hdev, &ev, sizeof(ev), NULL); +} + void mgmt_exit(void) { hci_mgmt_chan_unregister(&chan); From ad28b52441bc0b89ebf7ecb79a2765aa23db4d42 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 23 Jul 2026 06:43:32 +0530 Subject: [PATCH 55/84] Bluetooth: btintel: Add Bluetooth SAR revision 2 support BRDS revision 2 introduces per-chain (Chain A and Chain B) TX power limits across five sub-bands (2.4G, 5.2G, 5.8/5.9G, 6G-low, 6G-high), replacing the single-chain per-modulation model of revisions 0 and 1. - Add btintel_set_sar_rev2() which sends the full Rev2 DDC sequence: 0x019e inc-power-mode enable flag (1 byte) 0x0311 2.4 GHz sub-band limits (2 bytes) 0x0312 5.2 GHz sub-band limits (2 bytes) 0x0313 5.8/5.9 GHz sub-band limits (2 bytes) 0x0314 5.8/5.9 GHz sub-band limits again (2 bytes, duplicate FW reg) 0x0315 6 GHz low sub-band limits (2 bytes) 0x0316 6 GHz high sub-band limits (2 bytes) followed by the SAR-init-complete command (0xfe25). logs from dmesg when BTSAR2 is enabled in Coreboot/BIOS: Bluetooth: hci0: BT SAR Rev2: revision=2 bt_sar_bios=1 inc_power_mode=1 Bluetooth: hci0: BT SAR Rev2 Chain A: 2g4=76 5g2=0 5g8_5g9=0 6g1=0 6g3=0 Bluetooth: hci0: BT SAR Rev2 Chain B: 2g4=102 5g2=0 5g8_5g9=0 6g1=0 6g3=0 Signed-off-by: Ravindra Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 191 +++++++++++++++++++++++++++++++++++- drivers/bluetooth/btintel.h | 18 ++++ 2 files changed, 208 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 680f96c188d4..770b1fb371c7 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -51,6 +51,7 @@ enum { #define BTINTEL_BT_DOMAIN 0x12 #define BTINTEL_SAR_LEGACY 0 #define BTINTEL_SAR_INC_PWR 1 +#define BTINTEL_SAR_REV2 2 #define BTINTEL_SAR_INC_PWR_SUPPORTED 0 #define CMD_WRITE_BOOT_PARAMS 0xfc0e @@ -3102,6 +3103,111 @@ static int btintel_set_mutual_sar(struct hci_dev *hdev, struct btintel_sar_inc_p return 0; } +/* btintel_send_sar_rev2_band - send DDC command for one Rev2 sub-band + * + * Each DDC 0x0311-0x0316 carries 2 bytes: [ChainA_value, ChainB_value]. + * cmd->len = 4 (2 id + 2 data) + * HCI total = 5 bytes (1 len + 4) + */ +static int btintel_send_sar_rev2_band(struct hci_dev *hdev, + struct btintel_cp_ddc_write *cmd, + u16 id, u8 chain_a, u8 chain_b) +{ + cmd->len = 4; + cmd->id = cpu_to_le16(id); + cmd->data[0] = chain_a; + cmd->data[1] = chain_b; + return btintel_send_sar_ddc(hdev, cmd, 5); +} + +static int btintel_set_sar_rev2(struct hci_dev *hdev, + struct btintel_sar_rev2 *sar) +{ + struct btintel_cp_ddc_write *cmd; + struct sk_buff *skb; + u8 buffer[64]; + u8 enable; + int ret; + + cmd = (void *)buffer; + + /* DDC 0x019e: enable/disable increased power mode SAR (1 byte) */ + cmd->len = 3; + cmd->id = cpu_to_le16(0x019e); + cmd->data[0] = (sar->inc_power_mode == BTINTEL_SAR_INC_PWR_SUPPORTED) ? + 0x01 : 0x00; + ret = btintel_send_sar_ddc(hdev, cmd, 4); + if (ret) + return ret; + + /* DDC 0x0311-0x0316: per sub-band ChainA + ChainB limits */ + ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0311, + sar->chain_a.subband_2g4, + sar->chain_b.subband_2g4); + if (ret) + return ret; + + ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0312, + sar->chain_a.subband_5g2, + sar->chain_b.subband_5g2); + if (ret) + return ret; + + /* 0x0313 and 0x0314 both carry the 5G8/5G9 value */ + ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0313, + sar->chain_a.subband_5g8_5g9, + sar->chain_b.subband_5g8_5g9); + if (ret) + return ret; + + ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0314, + sar->chain_a.subband_5g8_5g9, + sar->chain_b.subband_5g8_5g9); + if (ret) + return ret; + + ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0315, + sar->chain_a.subband_6g1, + sar->chain_b.subband_6g1); + if (ret) + return ret; + + ret = btintel_send_sar_rev2_band(hdev, cmd, 0x0316, + sar->chain_a.subband_6g3, + sar->chain_b.subband_6g3); + if (ret) + return ret; + + /* Notify firmware that SAR initialisation is complete */ + enable = 0x01; + skb = __hci_cmd_sync(hdev, 0xfe25, sizeof(enable), &enable, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_warn(hdev, "Failed to send Intel SAR Rev2 Enable (%ld)", + PTR_ERR(skb)); + return PTR_ERR(skb); + } + + kfree_skb(skb); + return 0; +} + +static int btintel_sar_rev2_send_to_device(struct hci_dev *hdev, + struct btintel_sar_rev2 *sar, + struct intel_version_tlv *ver) +{ + u16 cnvi = ver->cnvi_top & 0xfff; + u16 cnvr = ver->cnvr_top & 0xfff; + + if (cnvi < BTINTEL_CNVI_BLAZARI || cnvr != BTINTEL_CNVR_WHP2) { + bt_dev_dbg(hdev, "BT SAR Rev2 not supported on this platform (cnvi=0x%x cnvr=0x%x)", + cnvi, cnvr); + return -EOPNOTSUPP; + } + + bt_dev_info(hdev, "Applying Bluetooth SAR Rev2"); + return btintel_set_sar_rev2(hdev, sar); +} + static int btintel_sar_send_to_device(struct hci_dev *hdev, struct btintel_sar_inc_pwr *sar, struct intel_version_tlv *ver) { @@ -3128,6 +3234,7 @@ static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv * { union acpi_object *bt_pkg, *buffer = NULL; struct btintel_sar_inc_pwr sar; + struct btintel_sar_rev2 sar_rev2; acpi_status status; u8 revision; int ret; @@ -3148,14 +3255,96 @@ static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv * goto error; } + if (buffer->package.elements[0].type != ACPI_TYPE_INTEGER) { + bt_dev_warn(hdev, "BT_SAR: unexpected ACPI type for revision field"); + ret = -EINVAL; + goto error; + } + revision = buffer->package.elements[0].integer.value; - if (revision > BTINTEL_SAR_INC_PWR) { + if (revision > BTINTEL_SAR_REV2) { bt_dev_dbg(hdev, "BT_SAR: revision: 0x%2.2x not supported", revision); ret = -EOPNOTSUPP; goto error; } + if (revision == BTINTEL_SAR_REV2 && bt_pkg->package.count == 13) { + /* Element layout: 0 = domain ID (BTINTEL_BT_DOMAIN, 0x12), + * 1 = bt_sar_bios (u32), 2 = inc_power_mode (u32), + * 3..12 = per-chain sub-band limits (u8 each). + */ + static const u64 rev2_max[13] = { + U8_MAX, /* domain ID */ + U32_MAX, U32_MAX, /* bt_sar_bios, inc_power_mode */ + U8_MAX, U8_MAX, U8_MAX, U8_MAX, U8_MAX, /* chain A */ + U8_MAX, U8_MAX, U8_MAX, U8_MAX, U8_MAX, /* chain B */ + }; + union acpi_object *e; + int i; + + for (i = 0; i < 13; i++) { + e = &bt_pkg->package.elements[i]; + if (e->type != ACPI_TYPE_INTEGER) { + bt_dev_warn(hdev, "BT SAR Rev2: unexpected ACPI type at element %d", + i); + ret = -EINVAL; + goto error; + } + if (e->integer.value > rev2_max[i]) { + bt_dev_warn(hdev, "BT SAR Rev2: element %d value 0x%llx out of range", + i, e->integer.value); + ret = -ERANGE; + goto error; + } + } + + memset(&sar_rev2, 0, sizeof(sar_rev2)); + sar_rev2.revision = revision; + sar_rev2.bt_sar_bios = bt_pkg->package.elements[1].integer.value; + + if (sar_rev2.bt_sar_bios != 1) { + bt_dev_warn(hdev, "Bluetooth SAR Rev2 is not enabled"); + ret = -EOPNOTSUPP; + goto error; + } + + sar_rev2.inc_power_mode = bt_pkg->package.elements[2].integer.value; + + sar_rev2.chain_a.subband_2g4 = bt_pkg->package.elements[3].integer.value; + sar_rev2.chain_a.subband_5g2 = bt_pkg->package.elements[4].integer.value; + sar_rev2.chain_a.subband_5g8_5g9 = bt_pkg->package.elements[5].integer.value; + sar_rev2.chain_a.subband_6g1 = bt_pkg->package.elements[6].integer.value; + sar_rev2.chain_a.subband_6g3 = bt_pkg->package.elements[7].integer.value; + + sar_rev2.chain_b.subband_2g4 = bt_pkg->package.elements[8].integer.value; + sar_rev2.chain_b.subband_5g2 = bt_pkg->package.elements[9].integer.value; + sar_rev2.chain_b.subband_5g8_5g9 = bt_pkg->package.elements[10].integer.value; + sar_rev2.chain_b.subband_6g1 = bt_pkg->package.elements[11].integer.value; + sar_rev2.chain_b.subband_6g3 = bt_pkg->package.elements[12].integer.value; + + bt_dev_dbg(hdev, "BT SAR Rev2: revision=%u bt_sar_bios=%u inc_power_mode=%u", + sar_rev2.revision, sar_rev2.bt_sar_bios, sar_rev2.inc_power_mode); + bt_dev_dbg(hdev, "BT SAR Rev2 Chain A: 2g4=%u 5g2=%u 5g8_5g9=%u 6g1=%u 6g3=%u", + sar_rev2.chain_a.subband_2g4, sar_rev2.chain_a.subband_5g2, + sar_rev2.chain_a.subband_5g8_5g9, sar_rev2.chain_a.subband_6g1, + sar_rev2.chain_a.subband_6g3); + bt_dev_dbg(hdev, "BT SAR Rev2 Chain B: 2g4=%u 5g2=%u 5g8_5g9=%u 6g1=%u 6g3=%u", + sar_rev2.chain_b.subband_2g4, sar_rev2.chain_b.subband_5g2, + sar_rev2.chain_b.subband_5g8_5g9, sar_rev2.chain_b.subband_6g1, + sar_rev2.chain_b.subband_6g3); + + ret = btintel_sar_rev2_send_to_device(hdev, &sar_rev2, ver); + goto error; + } + + if (revision == BTINTEL_SAR_REV2) { + bt_dev_warn(hdev, "BT SAR Rev2: unexpected ACPI package count %d (expected 13)", + bt_pkg->package.count); + ret = -EINVAL; + goto error; + } + memset(&sar, 0, sizeof(sar)); if (revision == BTINTEL_SAR_LEGACY && bt_pkg->package.count == 8) { diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 37d93abdd5a3..966ec1b02be2 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -65,6 +65,7 @@ struct intel_tlv { /* CNVR */ #define BTINTEL_CNVR_FMP2 0x910 +#define BTINTEL_CNVR_WHP2 0xA10 /* Whale Peak2 - Panther Lake */ #define BTINTEL_IMG_BOOTLOADER 0x01 /* Bootloader image */ #define BTINTEL_IMG_IML 0x02 /* Intermediate image */ @@ -204,6 +205,23 @@ struct btintel_sar_inc_pwr { u8 le_lr; }; +/* Bluetooth SAR feature (BRDS), Revision 2 - per-chain sub-band power limits */ +struct btintel_sar_band_limits { + u8 subband_2g4; + u8 subband_5g2; + u8 subband_5g8_5g9; + u8 subband_6g1; + u8 subband_6g3; +}; + +struct btintel_sar_rev2 { + u8 revision; + u32 bt_sar_bios; /* 1: BIOS-managed SAR enabled */ + u32 inc_power_mode; /* 0: supported, 1: disabled */ + struct btintel_sar_band_limits chain_a; + struct btintel_sar_band_limits chain_b; +}; + #define INTEL_HW_PLATFORM(cnvx_bt) ((u8)(((cnvx_bt) & 0x0000ff00) >> 8)) #define INTEL_HW_VARIANT(cnvx_bt) ((u8)(((cnvx_bt) & 0x003f0000) >> 16)) #define INTEL_CNVX_TOP_TYPE(cnvx_top) ((cnvx_top) & 0x00000fff) From 2bf6b9baca9372ea51b6d0f2820dc9bf29a83ef4 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Thu, 30 Jul 2026 18:05:28 +0545 Subject: [PATCH 56/84] Bluetooth: hci_aml: validate firmware segment lengths aml_download_firmware() reads two lengths from the firmware header and uses them to build pointers before checking that the header and segment data are present. A truncated or inconsistent firmware image can make the driver read past firmware->data while constructing TCI commands. Reject images shorter than the header and ensure that the ICCM and DCCM ranges fit within the loaded firmware before downloading either segment. Fixes: 37bac77e4649 ("Bluetooth: hci_uart: Add support for Amlogic HCI UART") Cc: stable@vger.kernel.org Signed-off-by: Laxman Acharya Padhya Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_aml.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_aml.c b/drivers/bluetooth/hci_aml.c index 959d9e67b669..067fbf278b44 100644 --- a/drivers/bluetooth/hci_aml.c +++ b/drivers/bluetooth/hci_aml.c @@ -247,7 +247,7 @@ static int aml_download_firmware(struct hci_dev *hdev, const char *fw_name) struct hci_uart *hu = hci_get_drvdata(hdev); struct aml_serdev *amldev = serdev_device_get_drvdata(hu->serdev); const struct firmware *firmware = NULL; - struct aml_fw_len *fw_len = NULL; + const struct aml_fw_len *fw_len = NULL; u8 *iccm_start = NULL, *dccm_start = NULL; u32 iccm_len, dccm_len; u32 value = 0; @@ -281,7 +281,21 @@ static int aml_download_firmware(struct hci_dev *hdev, const char *fw_name) goto exit; } - fw_len = (struct aml_fw_len *)firmware->data; + if (firmware->size < sizeof(*fw_len)) { + bt_dev_err(hdev, "Firmware is too small for its header"); + ret = -EINVAL; + goto exit; + } + + fw_len = (const struct aml_fw_len *)firmware->data; + if (fw_len->iccm_len < amldev->aml_dev_data->iccm_offset || + fw_len->iccm_len > firmware->size - sizeof(*fw_len) || + fw_len->dccm_len > firmware->size - sizeof(*fw_len) - + fw_len->iccm_len) { + bt_dev_err(hdev, "Invalid firmware segment lengths"); + ret = -EINVAL; + goto exit; + } /* Download ICCM */ iccm_start = (u8 *)(firmware->data) + sizeof(struct aml_fw_len) From 33af47e847fe4a28b109673affb5874015d54f5a Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Thu, 30 Jul 2026 16:32:02 +0800 Subject: [PATCH 57/84] Bluetooth: hci_event: fix LE list UAF on reset hci_cc_reset() clears the LE accept and resolving lists without taking hdev->lock. Other command-complete handlers serialize updates to these lists with that lock, and the debugfs readers hold it while walking them. This permits the reset completion and a debugfs read to interleave as follows: hci_rx_work debugfs reader ----------- -------------- lock hdev->lock fetch current entry list_del(entry) kfree(entry) read entry fields The reader then dereferences a freed list entry and may follow its stale next pointer. KASAN reported: BUG: KASAN: slab-use-after-free in white_list_show+0x15f/0x180 Read of size 1 at addr ffff8881015dab16 by task poc/95 Call Trace: white_list_show+0x15f/0x180 seq_read_iter+0x3ff/0x1190 seq_read+0x267/0x3d0 vfs_read+0x177/0xa20 ksys_read+0xf7/0x1c0 Allocated by task 91: hci_bdaddr_list_add+0x1a6/0x3a0 hci_cc_le_add_to_accept_list+0xab/0x140 hci_cmd_complete_evt+0x26c/0x9a0 hci_event_packet+0x454/0xb20 hci_rx_work+0x293/0x730 Freed by task 90: kfree+0x131/0x3c0 hci_bdaddr_list_clear+0xd8/0x160 hci_cc_reset+0x28a/0x370 hci_cmd_complete_evt+0x26c/0x9a0 hci_event_packet+0x454/0xb20 hci_rx_work+0x293/0x730 Take hdev->lock around both list clears. This matches the existing mutation and traversal locking convention. Fixes: a4d5504d5c39 ("Bluetooth: Clear LE white list when resetting controller") Fixes: cfdb0c2d095a ("Bluetooth: Store Resolv list size") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9764efb9e293..9c8bf6708356 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -297,8 +297,10 @@ static u8 hci_cc_reset(struct hci_dev *hdev, void *data, struct sk_buff *skb) hdev->ssp_debug_mode = 0; + hci_dev_lock(hdev); hci_bdaddr_list_clear(&hdev->le_accept_list); hci_bdaddr_list_clear(&hdev->le_resolv_list); + hci_dev_unlock(hdev); return rp->status; } From eb7e88e359882def910ccdc061d981a60d43f8ff Mon Sep 17 00:00:00 2001 From: oshada imalka Date: Thu, 30 Jul 2026 16:55:09 +0530 Subject: [PATCH 58/84] Bluetooth: hcli_ldisc: Remove reduntant braces Removed a redundant braces for a single if statement Signed-off-by: Oshada Imalka Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_ldisc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 46dfbe6f1c2e..58f5504a336e 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -762,9 +762,9 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id) hu->proto = p; err = hci_uart_register_dev(hu); - if (err) { + if (err) return err; - } + set_bit(HCI_UART_PROTO_READY, &hu->flags); clear_bit(HCI_UART_PROTO_INIT, &hu->flags); From 502adc06ba76dee19c292ae4a07d74d202fe734d Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Thu, 30 Jul 2026 10:57:28 +0900 Subject: [PATCH 59/84] Bluetooth: virtio_bt: avoid OOB read of build info string The virtbt_setup_zephyr() sends the Zephyr vendor command 0xfc08 (Read Build Information) and hands the response to bt_dev_info() and hci_set_fw_info() as a "%s" string starting at skb->data + 1, without checking the length. A backend that answers with status only leaves that pointer past the end of the received data, so the walk reads adjacent slab memory until it meets a NUL. Those bytes reach the kernel log and the firmware-info debugfs file. To fix this, print the string with a bounded "%.*s" limited to skb->len - 1. A short or unterminated response then prints as much as arrived instead of failing setup. This mirrors commit dd068ef04412 ("Bluetooth: bpa10x: avoid OOB read of revision string in bpa10x_setup()"), which fixed the identical pattern. Fixes: afd2daa26c7a ("Bluetooth: Add support for virtio transport driver") Signed-off-by: HyeongJun An Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/virtio_bt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c index 140ab55c9fc5..c20d54088c8c 100644 --- a/drivers/bluetooth/virtio_bt.c +++ b/drivers/bluetooth/virtio_bt.c @@ -120,9 +120,13 @@ static int virtbt_setup_zephyr(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); + /* Bounded print: the backend controls skb->len. */ + if (skb->len > 1) { + int len = skb->len - 1; - hci_set_fw_info(hdev, "%s", skb->data + 1); + bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1)); + hci_set_fw_info(hdev, "%.*s", len, skb->data + 1); + } kfree_skb(skb); return 0; From 2b66c83ff1751d6bd3201b3017206262ab46dc05 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 1 Aug 2026 21:37:23 +0300 Subject: [PATCH 60/84] Bluetooth: L2CAP: use proto_lock for l2cap_data to fix l2cap_disconn_ind hci_conn::l2cap_data is accessed without locks in l2cap_disconn_ind via hci_conn_timeout (disc_work) -> hci_proto_disconn_ind -> l2cap_disconn_ind. This is UAF if the l2cap_conn is deleted concurrently. disc_work is disabled sync in hci_conn_del(), so we cannot take hci_dev_lock in disc_work. Fix by using proto_lock to guard l2cap_data, in addition to hdev->lock which is held in other access paths. Fixes: ab4eedb790ca ("Bluetooth: L2CAP: Fix corrupted list in hci_chan_del") Reported-by: syzbot+9c40ad7c6ed7165e46e8@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=9c40ad7c6ed7165e46e8 Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1156aba4e83c..30d7120d3a15 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1833,7 +1833,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) hci_chan_del(conn->hchan); conn->hchan = NULL; + spin_lock(&hcon->proto_lock); hcon->l2cap_data = NULL; + spin_unlock(&hcon->proto_lock); + mutex_unlock(&conn->lock); l2cap_conn_put(conn); } @@ -7168,8 +7171,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) } kref_init(&conn->ref); - hcon->l2cap_data = conn; - conn->hcon = hci_conn_get(hcon); conn->hchan = hchan; BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); @@ -7198,6 +7199,11 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; + spin_lock(&hcon->proto_lock); + conn->hcon = hci_conn_get(hcon); + hcon->l2cap_data = conn; + spin_unlock(&hcon->proto_lock); + return conn; } @@ -7582,13 +7588,18 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) int l2cap_disconn_ind(struct hci_conn *hcon) { - struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_conn *conn; + int ret = HCI_ERROR_REMOTE_USER_TERM; BT_DBG("hcon %p", hcon); - if (!conn) - return HCI_ERROR_REMOTE_USER_TERM; - return conn->disc_reason; + spin_lock(&hcon->proto_lock); + conn = hcon->l2cap_data; + if (conn) + ret = conn->disc_reason; + spin_unlock(&hcon->proto_lock); + + return ret; } static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) From b80de2cbb1c8c9352f2bed879dd9427e18f9b34b Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 1 Aug 2026 21:37:24 +0300 Subject: [PATCH 61/84] Bluetooth: add annotations for l2cap_data locking context Add context analysis annotations for hci_conn::l2cap_data locking. Also add necessary lockdep_assert_held() and __must_hold annotations to prove the access is safe. The access in smp_conn_security() is supposed to be guarded by the caller holding lock that blocks concurrent l2cap_conn_del() eg. hdev->lock, conn->lock or chan->lock. Mark unsafe as can't be automatically checked now. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/6lowpan.c | 2 ++ net/bluetooth/l2cap_core.c | 9 +++++++++ net/bluetooth/mgmt.c | 2 ++ net/bluetooth/smp.c | 7 ++++++- net/bluetooth/smp.h | 6 ++++-- 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 01b938c4b24a..c299daac7fbe 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -775,7 +775,7 @@ struct hci_conn { struct hci_dev *hdev; spinlock_t proto_lock; /* lock guarding protocol data */ - void *l2cap_data; + void *l2cap_data __guarded_by(&proto_lock, &hdev->lock); void *sco_data; void *iso_data __guarded_by(&proto_lock); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index d504a363a30f..30f4afa18bc8 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1007,6 +1007,8 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, return -ENOENT; } + lockdep_assert_held(&hcon->hdev->lock); + *conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data); BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 30d7120d3a15..ee459dd411f5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1791,6 +1791,7 @@ static void l2cap_unregister_all_users(struct l2cap_conn *conn) } static void l2cap_conn_del(struct hci_conn *hcon, int err) + __must_hold(&hcon->hdev->lock) { struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_chan *chan, *l; @@ -7153,6 +7154,7 @@ static void process_pending_rx(struct work_struct *work) } static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) + __must_hold(&hcon->hdev->lock) { struct l2cap_conn *conn = hcon->l2cap_data; struct hci_chan *hchan; @@ -7358,6 +7360,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, goto done; } + lockdep_assert_held(&hcon->hdev->lock); + conn = l2cap_conn_add(hcon); if (!conn) { hci_conn_drop(hcon); @@ -7528,6 +7532,7 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c, } static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) + __must_hold(&hcon->hdev->lock) { struct hci_dev *hdev = hcon->hdev; struct l2cap_conn *conn; @@ -7603,6 +7608,7 @@ int l2cap_disconn_ind(struct hci_conn *hcon) } static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) + __must_hold(&hcon->hdev->lock) { if (hcon->type != ACL_LINK && hcon->type != LE_LINK) return; @@ -7630,6 +7636,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) } static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) + __must_hold(&hcon->hdev->lock) { struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_chan *chan; @@ -7814,6 +7821,8 @@ int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle, return -ENOENT; } + lockdep_assert_held(&hcon->hdev->lock); + hci_conn_enter_active_mode(hcon, BT_POWER_FORCE_ACTIVE_OFF); conn = hcon->l2cap_data; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 09edd72acc22..0d6b41fe0b34 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3876,6 +3876,8 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, } if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) { + lockdep_assert_held(&conn->hdev->lock); + err = smp_user_confirm_reply(conn, mgmt_op, passkey); if (!err) err = mgmt_cmd_complete(sk, hdev->id, mgmt_op, diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index c4470958b0d5..f23b695c487b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2327,12 +2327,15 @@ static void smp_send_security_req(struct smp_chan *smp, __u8 auth) int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) { - struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_conn *conn; struct l2cap_chan *chan; struct smp_chan *smp; __u8 authreq; int ret; + /* Caller shall ensure there can be no race with l2cap_conn_del() */ + conn = context_unsafe(hcon->l2cap_data); + bt_dev_dbg(hcon->hdev, "conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); @@ -2421,6 +2424,8 @@ int smp_cancel_and_remove_pairing(struct hci_dev *hdev, bdaddr_t *bdaddr, if (!hcon) goto done; + lockdep_assert_held(&hcon->hdev->lock); + conn = hcon->l2cap_data; if (!conn) goto done; diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index eac27bd541bb..c86c46389007 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -180,11 +180,13 @@ enum smp_key_pref { /* SMP Commands */ int smp_cancel_and_remove_pairing(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type); + u8 addr_type) + __must_hold(&hdev->lock); bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level, enum smp_key_pref key_pref); int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); -int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); +int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey) + __must_hold(&conn->hdev->lock); bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16], const bdaddr_t *bdaddr); From bd76a28a739dd4d15ac119c7f9cfe3f7cf8a85c0 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 4 Aug 2026 11:46:30 +0200 Subject: [PATCH 62/84] Bluetooth: btmrvl: fix event packet length validation The event length validation added by 65be90af2756 commit used a single check against sizeof(*event), which assumed every event type uses the maximum payload size. Unfortunately event packet length depends on the type of the received event, so it must be checked separately for each event type to avoid rejecting some known well-formed events. Fixes: 65be90af2756 ("Bluetooth: btmrvl: validate event packet lengths") Signed-off-by: Marek Szyprowski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmrvl_main.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index aaf1614ccfd7..e25930351f64 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -81,7 +81,7 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) struct btmrvl_event *event; int ret = 0; - if (skb->len < sizeof(*event)) + if (skb->len <= offsetof(typeof(*event), data[0])) return -EINVAL; event = (struct btmrvl_event *) skb->data; @@ -93,6 +93,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) switch (event->data[0]) { case BT_EVENT_AUTO_SLEEP_MODE: + if (skb->len <= offsetof(typeof(*event), data[2])) + return -EINVAL; if (!event->data[2]) { if (event->data[1] == BT_PS_ENABLE) adapter->psmode = 1; @@ -106,6 +108,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) break; case BT_EVENT_HOST_SLEEP_CONFIG: + if (skb->len <= offsetof(typeof(*event), data[3])) + return -EINVAL; if (!event->data[3]) BT_DBG("gpio=%x, gap=%x", event->data[1], event->data[2]); @@ -114,6 +118,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) break; case BT_EVENT_HOST_SLEEP_ENABLE: + if (skb->len <= offsetof(typeof(*event), data[1])) + return -EINVAL; if (!event->data[1]) { adapter->hs_state = HS_ACTIVATED; if (adapter->psmode) @@ -126,6 +132,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) break; case BT_EVENT_MODULE_CFG_REQ: + if (skb->len <= offsetof(typeof(*event), data[2])) + return -EINVAL; if (priv->btmrvl_dev.sendcmdflag && event->data[1] == MODULE_BRINGUP_REQ) { BT_DBG("EVENT:%s", @@ -143,6 +151,8 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) break; case BT_EVENT_POWER_STATE: + if (skb->len <= offsetof(typeof(*event), data[1])) + return -EINVAL; if (event->data[1] == BT_PS_SLEEP) adapter->ps_state = PS_SLEEP; BT_DBG("EVENT:%s", From 0acd4eeb4b225b9bebbf9ef96cc10cdd79b94899 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Sat, 1 Aug 2026 23:54:52 +0545 Subject: [PATCH 63/84] Bluetooth: hci_event: validate LE Set CIG Parameters response The Command Complete dispatch validates only the fixed part of the LE Set CIG Parameters response. After that part is pulled from the skb, hci_cc_le_set_cig_params() trusts num_handles and reads each entry in the trailing handle array. Matching num_handles against the command's num_cis does not guarantee that the response contains the advertised handles. A truncated response from a malfunctioning controller can therefore make the handler read beyond the skb data. Validate that the remaining skb data contains all advertised handles. Include this in the existing response validation so malformed responses also follow the established CIG failure handling. Fixes: 26afbd826ee3 ("Bluetooth: Add initial implementation of CIS connections") Cc: stable@vger.kernel.org Signed-off-by: Laxman Acharya Padhya Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9c8bf6708356..6890d60ade93 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3865,8 +3865,10 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_CIG_PARAMS); - if (!rp->status && (!cp || rp->num_handles != cp->num_cis || - rp->cig_id != cp->cig_id)) { + if (!rp->status && + (!cp || rp->num_handles != cp->num_cis || + rp->cig_id != cp->cig_id || + skb->len < array_size(rp->num_handles, sizeof(*rp->handle)))) { bt_dev_err(hdev, "unexpected Set CIG Parameters response data"); status = HCI_ERROR_UNSPECIFIED; } From ad0e7ac7da9a9a0095570bd6add3e27f259de104 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 1 Aug 2026 23:31:36 -0700 Subject: [PATCH 64/84] Bluetooth: btintel: Fix diagnostics event detection For a diagnostics VSE, diagnostics_hdr[] sits at the start of the event payload, skb->data[2], but btintel_recv_event() wrongly guards its memcmp with @len, which is measured from skb->data[3] for the earlier INTEL_BOOTLOADER check. Fix by using (@len + 1) instead, which == (skb->len - HCI_EVENT_HDR_SIZE) exactly. Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 770b1fb371c7..d06a335ff1db 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -4021,7 +4021,7 @@ int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb) /* Handle all diagnostics events separately. May still call * hci_recv_frame. */ - if (len >= sizeof(diagnostics_hdr) && + if (len + 1 >= sizeof(diagnostics_hdr) && memcmp(&skb->data[2], diagnostics_hdr, sizeof(diagnostics_hdr)) == 0) { return btintel_diagnostics(hdev, skb); From d39667cb0472843d25d414ee1cb8f02cbb263be3 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 1 Aug 2026 23:31:37 -0700 Subject: [PATCH 65/84] Bluetooth: btintel: Remove redundant (hdr->plen > 0) in btintel_recv_event() Drop the check since: - it is already implied by the existing (skb->len > HCI_EVENT_HDR_SIZE) - hdr->plen is then not used by the function at all Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index d06a335ff1db..bcb2514b7bc0 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -3991,8 +3991,7 @@ int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb) struct hci_event_hdr *hdr = (void *)skb->data; const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 }; - if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && - hdr->plen > 0) { + if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff) { const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; From 8824e13fb5dcc40ae78862a92d2f8bf48a934477 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 1 Aug 2026 23:31:38 -0700 Subject: [PATCH 66/84] Bluetooth: coredump: Expose header size and end marker to drivers To separate the coredump header and data far more easily, give a vendor driver the option to pad its header to a fixed size, by moving the header size limit and ending marker to coredump.h: - HCI_DEVCD_HDR_SIZE_MAX: the max header size - HCI_DEVCD_HDR_END_MARKER: the header-ending marker Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/coredump.h | 7 +++++++ net/bluetooth/coredump.c | 7 ++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/net/bluetooth/coredump.h b/include/net/bluetooth/coredump.h index ab85a6adfffd..1f071ab55416 100644 --- a/include/net/bluetooth/coredump.h +++ b/include/net/bluetooth/coredump.h @@ -8,6 +8,13 @@ #define DEVCOREDUMP_TIMEOUT msecs_to_jiffies(10000) /* 10 sec */ +/* + * Max header size, shared by both the devcoredump core and + * the dmp_hdr() registered by driver via hci_devcd_register() + */ +#define HCI_DEVCD_HDR_SIZE_MAX 512 +#define HCI_DEVCD_HDR_END_MARKER "--- Start dump ---\n" + typedef void (*coredump_t)(struct hci_dev *hdev); typedef void (*dmp_hdr_t)(struct hci_dev *hdev, struct sk_buff *skb); typedef void (*notify_change_t)(struct hci_dev *hdev, int state); diff --git a/net/bluetooth/coredump.c b/net/bluetooth/coredump.c index 913bbba559f8..5bee863bd6d2 100644 --- a/net/bluetooth/coredump.c +++ b/net/bluetooth/coredump.c @@ -34,8 +34,6 @@ struct hci_devcoredump_skb_pattern { hci_dmp_cb(skb)->pkt_type, \ hci_devcd_state_name(hdev->dump.state)) -#define MAX_DEVCOREDUMP_HDR_SIZE 512 /* bytes */ - static int hci_devcd_update_hdr_state(char *buf, size_t size, int state) { int len = 0; @@ -63,7 +61,6 @@ static int hci_devcd_update_state(struct hci_dev *hdev, int state) static int hci_devcd_mkheader(struct hci_dev *hdev, struct sk_buff *skb) { - char dump_start[] = "--- Start dump ---\n"; char hdr[80]; int hdr_len; @@ -74,7 +71,7 @@ static int hci_devcd_mkheader(struct hci_dev *hdev, struct sk_buff *skb) if (hdev->dump.dmp_hdr) hdev->dump.dmp_hdr(hdev, skb); - skb_put_data(skb, dump_start, strlen(dump_start)); + skb_put_data(skb, HCI_DEVCD_HDR_END_MARKER, strlen(HCI_DEVCD_HDR_END_MARKER)); return skb->len; } @@ -154,7 +151,7 @@ static int hci_devcd_prepare(struct hci_dev *hdev, u32 dump_size) int dump_hdr_size; int err = 0; - skb = alloc_skb(MAX_DEVCOREDUMP_HDR_SIZE, GFP_ATOMIC); + skb = alloc_skb(HCI_DEVCD_HDR_SIZE_MAX, GFP_ATOMIC); if (!skb) return -ENOMEM; From e6997c120c62381700f8fdf0c3bdc46d2d6fe698 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 1 Aug 2026 23:31:39 -0700 Subject: [PATCH 67/84] Bluetooth: hci_core: Introduce __hci_reset_dev() with a hardware error code hci_reset_dev() injects a constant hardware error code 0x00 to restart the device. But a transport driver may need a different error code. Fix by introducing __hci_reset_dev(hdev, hw_err_code), which will be used by a follow-up patch. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 8 +++++++- net/bluetooth/hci_core.c | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c299daac7fbe..6ff47f9bf758 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1785,7 +1785,13 @@ int hci_register_suspend_notifier(struct hci_dev *hdev); int hci_unregister_suspend_notifier(struct hci_dev *hdev); int hci_suspend_dev(struct hci_dev *hdev); int hci_resume_dev(struct hci_dev *hdev); -int hci_reset_dev(struct hci_dev *hdev); +int __hci_reset_dev(struct hci_dev *hdev, u8 hw_err_code); + +static inline int hci_reset_dev(struct hci_dev *hdev) +{ + return __hci_reset_dev(hdev, 0); +} + int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb); __printf(2, 3) void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9d5adf882509..509c820a693d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2852,9 +2852,9 @@ int hci_resume_dev(struct hci_dev *hdev) EXPORT_SYMBOL(hci_resume_dev); /* Reset HCI device */ -int hci_reset_dev(struct hci_dev *hdev) +int __hci_reset_dev(struct hci_dev *hdev, u8 hw_err_code) { - static const u8 hw_err[] = { HCI_EV_HARDWARE_ERROR, 0x01, 0x00 }; + const u8 hw_err[] = { HCI_EV_HARDWARE_ERROR, 0x01, hw_err_code }; struct sk_buff *skb; skb = bt_skb_alloc(3, GFP_ATOMIC); @@ -2869,7 +2869,7 @@ int hci_reset_dev(struct hci_dev *hdev) /* Send Hardware Error to upper stack */ return hci_recv_frame(hdev, skb); } -EXPORT_SYMBOL(hci_reset_dev); +EXPORT_SYMBOL(__hci_reset_dev); static u8 hci_dev_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb) { From 73c4c035ae25108ad98ff03ccdd0c763a42bb08f Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 1 Aug 2026 23:31:40 -0700 Subject: [PATCH 68/84] Bluetooth: btnxpuart: Simplify nxp_set_ind_reset() by __hci_reset_dev() nxp_set_ind_reset() injects the non-zero hardware error code BTNXPUART_IR_HW_ERR. Simplify it by __hci_reset_dev(hdev, BTNXPUART_IR_HW_ERR). Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 0bb300eef157..993c48b5f593 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1331,19 +1331,7 @@ static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev) static int nxp_set_ind_reset(struct hci_dev *hdev, void *data) { - static const u8 ir_hw_err[] = { HCI_EV_HARDWARE_ERROR, - 0x01, BTNXPUART_IR_HW_ERR }; - struct sk_buff *skb; - - skb = bt_skb_alloc(3, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hci_skb_pkt_type(skb) = HCI_EVENT_PKT; - skb_put_data(skb, ir_hw_err, 3); - - /* Inject Hardware Error to upper stack */ - return hci_recv_frame(hdev, skb); + return __hci_reset_dev(hdev, BTNXPUART_IR_HW_ERR); } /* Firmware dump */ From 0bd606b31d40dceb718bf22e3ce7b4cff7e34bf6 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 1 Aug 2026 23:31:41 -0700 Subject: [PATCH 69/84] Bluetooth: hci_event: Introduce handle_ev_vendor() for HCI_EV_VENDOR Introduce the hook to solve issues below: msft_vendor_evt(), the current handler for all VSEs, is unsuitable since: - many VSEs are not MSFT ones; - it always corrupts the non-MSFT VSEs by calling skb_pull_data() once the MSFT extension is enabled. Several issues are caused by many transport drivers pre-processing VSEs in their RX path, often an IRQ-disabled atomic context. Take the two typical cases below as examples: Case 1: // no btmon log, no way to reach userspace Step 1: handle and free @original_skb directly Case 2: // hurts performance and consumes GFP_ATOMIC memory Step 1: cloned_skb = skb_clone(original_skb, GFP_ATOMIC); // the VSE is handled here Step 2: handle and free @cloned_skb Step 3: hci_recv_frame(hdev, original_skb); // already handled, but re-enters the stack's event-handling path Step 4: hci_event_packet(hdev, original_skb); Fix by introducing the hook with usage: 1) the transport driver registers the hook for VSEs of interest; 2) the stack calls it in process context, handling the VSE like any other event: - if interested, handle the VSE - no need to free it - and return true; - otherwise return false. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_event.c | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6ff47f9bf758..e07418a5adce 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -646,6 +646,8 @@ struct hci_dev { int (*setup)(struct hci_dev *hdev); int (*shutdown)(struct hci_dev *hdev); int (*send)(struct hci_dev *hdev, struct sk_buff *skb); + /* Handle HCI_EV_VENDOR; return true if handled, false otherwise */ + bool (*handle_ev_vendor)(struct hci_dev *hdev, struct sk_buff *skb); void (*notify)(struct hci_dev *hdev, unsigned int evt); void (*hw_error)(struct hci_dev *hdev, u8 code); int (*post_init)(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 6890d60ade93..d8e9125ae74a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -7604,6 +7604,14 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data, subev->func(hdev, data, skb); } +static void hci_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) +{ + if (hdev->handle_ev_vendor && hdev->handle_ev_vendor(hdev, skb)) + return; + + msft_vendor_evt(hdev, data, skb); +} + static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 event, struct sk_buff *skb) { @@ -7831,7 +7839,7 @@ static const struct hci_ev { HCI_EV_REQ_VL(HCI_EV_LE_META, hci_le_meta_evt, sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_SIZE), /* [0xff = HCI_EV_VENDOR] */ - HCI_EV_VL(HCI_EV_VENDOR, msft_vendor_evt, 0, HCI_MAX_EVENT_SIZE), + HCI_EV_VL(HCI_EV_VENDOR, hci_vendor_evt, 0, HCI_MAX_EVENT_SIZE), }; static void hci_event_func(struct hci_dev *hdev, u8 event, struct sk_buff *skb, From 9a4fa3cddc692efb47515c45fe05217369448bde Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 1 Aug 2026 23:31:42 -0700 Subject: [PATCH 70/84] Bluetooth: hci_event: Use 255 as max event payload length in hci_ev_table[] hci_event_func() validates skb->len against ev->max_len from the entry in hci_ev_table[]. By then, the header has already been stripped by skb_pull(). So the max event payload is 255, but hci_ev_table[] still uses HCI_MAX_EVENT_SIZE (260) for it, which is imprecise. Fix by introducing HCI_MAX_EVENT_PLEN (255) and using it instead. Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_event.c | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cd3520a29131..1641d879dbda 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -3382,6 +3382,7 @@ struct hci_ev_si_security { /* ---- HCI Packet structures ---- */ #define HCI_COMMAND_HDR_SIZE 3 #define HCI_EVENT_HDR_SIZE 2 +#define HCI_MAX_EVENT_PLEN 255 #define HCI_ACL_HDR_SIZE 4 #define HCI_SCO_HDR_SIZE 3 #define HCI_ISO_HDR_SIZE 4 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d8e9125ae74a..371ca8236bc5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -7728,7 +7728,7 @@ static const struct hci_ev { HCI_EV_STATUS(HCI_EV_INQUIRY_COMPLETE, hci_inquiry_complete_evt), /* [0x02 = HCI_EV_INQUIRY_RESULT] */ HCI_EV_VL(HCI_EV_INQUIRY_RESULT, hci_inquiry_result_evt, - sizeof(struct hci_ev_inquiry_result), HCI_MAX_EVENT_SIZE), + sizeof(struct hci_ev_inquiry_result), HCI_MAX_EVENT_PLEN), /* [0x03 = HCI_EV_CONN_COMPLETE] */ HCI_EV(HCI_EV_CONN_COMPLETE, hci_conn_complete_evt, sizeof(struct hci_ev_conn_complete)), @@ -7756,7 +7756,7 @@ static const struct hci_ev { sizeof(struct hci_ev_remote_features)), /* [0x0e = HCI_EV_CMD_COMPLETE] */ HCI_EV_REQ_VL(HCI_EV_CMD_COMPLETE, hci_cmd_complete_evt, - sizeof(struct hci_ev_cmd_complete), HCI_MAX_EVENT_SIZE), + sizeof(struct hci_ev_cmd_complete), HCI_MAX_EVENT_PLEN), /* [0x0f = HCI_EV_CMD_STATUS] */ HCI_EV_REQ(HCI_EV_CMD_STATUS, hci_cmd_status_evt, sizeof(struct hci_ev_cmd_status)), @@ -7768,7 +7768,7 @@ static const struct hci_ev { sizeof(struct hci_ev_role_change)), /* [0x13 = HCI_EV_NUM_COMP_PKTS] */ HCI_EV_VL(HCI_EV_NUM_COMP_PKTS, hci_num_comp_pkts_evt, - sizeof(struct hci_ev_num_comp_pkts), HCI_MAX_EVENT_SIZE), + sizeof(struct hci_ev_num_comp_pkts), HCI_MAX_EVENT_PLEN), /* [0x14 = HCI_EV_MODE_CHANGE] */ HCI_EV(HCI_EV_MODE_CHANGE, hci_mode_change_evt, sizeof(struct hci_ev_mode_change)), @@ -7794,7 +7794,7 @@ static const struct hci_ev { HCI_EV_VL(HCI_EV_INQUIRY_RESULT_WITH_RSSI, hci_inquiry_result_with_rssi_evt, sizeof(struct hci_ev_inquiry_result_rssi), - HCI_MAX_EVENT_SIZE), + HCI_MAX_EVENT_PLEN), /* [0x23 = HCI_EV_REMOTE_EXT_FEATURES] */ HCI_EV(HCI_EV_REMOTE_EXT_FEATURES, hci_remote_ext_features_evt, sizeof(struct hci_ev_remote_ext_features)), @@ -7804,7 +7804,7 @@ static const struct hci_ev { /* [0x2f = HCI_EV_EXTENDED_INQUIRY_RESULT] */ HCI_EV_VL(HCI_EV_EXTENDED_INQUIRY_RESULT, hci_extended_inquiry_result_evt, - sizeof(struct hci_ev_ext_inquiry_result), HCI_MAX_EVENT_SIZE), + sizeof(struct hci_ev_ext_inquiry_result), HCI_MAX_EVENT_PLEN), /* [0x30 = HCI_EV_KEY_REFRESH_COMPLETE] */ HCI_EV(HCI_EV_KEY_REFRESH_COMPLETE, hci_key_refresh_complete_evt, sizeof(struct hci_ev_key_refresh_complete)), @@ -7837,9 +7837,9 @@ static const struct hci_ev { sizeof(struct hci_ev_remote_host_features)), /* [0x3e = HCI_EV_LE_META] */ HCI_EV_REQ_VL(HCI_EV_LE_META, hci_le_meta_evt, - sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_SIZE), + sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_PLEN), /* [0xff = HCI_EV_VENDOR] */ - HCI_EV_VL(HCI_EV_VENDOR, hci_vendor_evt, 0, HCI_MAX_EVENT_SIZE), + HCI_EV_VL(HCI_EV_VENDOR, hci_vendor_evt, 0, HCI_MAX_EVENT_PLEN), }; static void hci_event_func(struct hci_dev *hdev, u8 event, struct sk_buff *skb, From f57b399c4fa1501b2d5451f52d861ece86bcf3db Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Sat, 1 Aug 2026 15:05:24 +0800 Subject: [PATCH 71/84] Bluetooth: hci_sync: Fix accept list UAF during suspend hci_update_event_filter_sync() walks hdev->accept_list while sending a synchronous HCI command for each remote-wakeup device. The suspend path holds hdev->req_lock, but accept-list updates are serialized by hdev->lock. Consequently, remove_device() can free the current list entry during the controller wait. The following interleaving causes the use-after-free: hci_update_event_filter_sync() remove_device() fetch accept-list entry hci_set_event_filter_sync() wait for controller response hci_dev_lock() list_del() kfree() hci_dev_unlock() read the freed list.next KASAN reported: BUG: KASAN: slab-use-after-free in hci_suspend_sync+0x835/0x910 Read of size 8 at addr ffff88810bec8440 by task kworker/0:1/10 Workqueue: events vhci_suspend_work Call Trace: hci_suspend_sync+0x835/0x910 hci_suspend_dev+0x182/0x450 process_one_work+0x661/0x1090 worker_thread+0x45b/0xd10 Allocated by task 86: hci_bdaddr_list_add_with_flags+0x1a8/0x400 add_device+0x381/0x820 hci_sock_sendmsg+0x1033/0x1ea0 Freed by task 91: kfree+0x131/0x3c0 remove_device+0x429/0xb70 hci_sock_sendmsg+0x1033/0x1ea0 Snapshot the remote-wakeup addresses under hdev->lock. Release the lock before sending HCI commands. Clear the controller event filter before building the snapshot, and skip allocation and the second list traversal when there are no matching entries. This preserves the original filter and scan-state updates without retaining an accept-list node across a controller wait. Fixes: 182ee45da083 ("Bluetooth: hci_sync: Rework hci_suspend_notifier") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-bluetooth/20260730092331.2069741-1-nicoyip.dev@gmail.com/ Signed-off-by: Chengfeng Ye Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 46 ++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 307fd47f8459..a661634a63aa 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6351,6 +6351,8 @@ static int hci_pause_discovery_sync(struct hci_dev *hdev) static int hci_update_event_filter_sync(struct hci_dev *hdev) { struct bdaddr_list_with_flags *b; + bdaddr_t *accept_list; + size_t i, num_entries = 0; u8 scan = SCAN_DISABLED; bool scanning = test_bit(HCI_PSCAN, &hdev->flags); int err; @@ -6367,23 +6369,49 @@ static int hci_update_event_filter_sync(struct hci_dev *hdev) /* Always clear event filter when starting */ hci_clear_event_filter_sync(hdev); - list_for_each_entry(b, &hdev->accept_list, list) { - if (!(b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP)) - continue; + hci_dev_lock(hdev); - bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr); + list_for_each_entry(b, &hdev->accept_list, list) + if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP) + num_entries++; - err = hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SETUP, - HCI_CONN_SETUP_ALLOW_BDADDR, - &b->bdaddr, - HCI_CONN_SETUP_AUTO_ON); + if (!num_entries) { + hci_dev_unlock(hdev); + goto update_scan; + } + + accept_list = kmalloc_array(num_entries, sizeof(*accept_list), + GFP_KERNEL); + if (!accept_list) { + hci_dev_unlock(hdev); + return -ENOMEM; + } + + i = 0; + list_for_each_entry(b, &hdev->accept_list, list) + if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP) + bacpy(&accept_list[i++], &b->bdaddr); + + hci_dev_unlock(hdev); + + for (i = 0; i < num_entries; i++) { + bt_dev_dbg(hdev, "Adding event filters for %pMR", + &accept_list[i]); + + err = hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SETUP, + HCI_CONN_SETUP_ALLOW_BDADDR, + &accept_list[i], + HCI_CONN_SETUP_AUTO_ON); if (err) bt_dev_err(hdev, "Failed to set event filter for %pMR", - &b->bdaddr); + &accept_list[i]); else scan = SCAN_PAGE; } + kfree(accept_list); + +update_scan: if (scan && !scanning) hci_write_scan_enable_sync(hdev, scan); else if (!scan && scanning) From 42de40abe25db9211107af8896d0fd741f10648d Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 6 Aug 2026 20:59:54 +0800 Subject: [PATCH 72/84] Bluetooth: hci_conn: fix the SCO setup context lifetime hci_setup_sync() queues a conn_handle_t with a NULL destroy callback, so the context is only freed if hci_enhanced_setup_sync() actually runs. An entry that is cancelled instead is leaked, as _hci_cmd_sync_cancel_entry() does not release entry->data when there is no destroy callback, and hci_cmd_sync_clear() cancels every pending entry when the controller is unregistered. The context also stores a bare hci_conn pointer, so the connection can be freed while the work is queued. The dequeue in hci_conn_del() does not cover it either, as it matches on entry->data == conn and entry->data is the wrapper here. Same problem as commit 2f5d635ad590 ("Bluetooth: hci_sync: hold conn in hci_connect_acl/le_sync() callbacks"). Hold the connection and release both from a destroy callback. The submission failure path drops both, since hci_cmd_sync_submit() does not call the destroy callback when it fails to queue. Fixes: e07a06b4eb41 ("Bluetooth: Convert SCO configure_datapath to hci_sync") Signed-off-by: Linmao Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b1f911fd4ad6..19b7629b1cc1 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -283,8 +283,6 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) struct hci_cp_enhanced_setup_sync_conn cp; const struct sco_param *param; - kfree(conn_handle); - if (!hci_conn_valid(hdev, conn)) return -ECANCELED; @@ -453,6 +451,15 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle) return true; } +static void hci_enhanced_setup_sync_destroy(struct hci_dev *hdev, void *data, + int err) +{ + struct conn_handle_t *conn_handle = data; + + hci_conn_put(conn_handle->conn); + kfree(conn_handle); +} + bool hci_setup_sync(struct hci_conn *conn, __u16 handle) { int result; @@ -464,12 +471,15 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle) if (!conn_handle) return false; - conn_handle->conn = conn; + conn_handle->conn = hci_conn_get(conn); conn_handle->handle = handle; result = hci_cmd_sync_queue(conn->hdev, hci_enhanced_setup_sync, - conn_handle, NULL); - if (result < 0) + conn_handle, + hci_enhanced_setup_sync_destroy); + if (result < 0) { + hci_conn_put(conn); kfree(conn_handle); + } return result == 0; } From 120d8dc042e3d45073bb6e50ee7b058a0b182627 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 6 Aug 2026 20:59:55 +0800 Subject: [PATCH 73/84] Bluetooth: hci_sync: free the advertising instance on the failure and cancel paths adv_timeout_expire() hands a kmalloc()ed instance byte to hci_cmd_sync_queue() with a NULL destroy callback, and only adv_timeout_expire_sync() frees it. That leaks on two paths: - the return value is not checked, and hci_cmd_sync_queue() does not take ownership when it fails (-ENETDOWN, -ENODEV, -ENOMEM); - a cancelled entry is not released, as _hci_cmd_sync_cancel_entry() does not free entry->data when there is no destroy callback. hci_cmd_sync_clear() cancels every pending entry when the controller is unregistered. Free the buffer from a destroy callback, and in the caller when the entry could not be queued at all. Fixes: c249ea9b4309 ("Bluetooth: Move Adv Instance timer to hci_sync") Signed-off-by: Linmao Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index a661634a63aa..df2d037970f9 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -540,8 +540,6 @@ static int adv_timeout_expire_sync(struct hci_dev *hdev, void *data) { u8 instance = *(u8 *)data; - kfree(data); - hci_clear_adv_instance_sync(hdev, NULL, instance, false); if (list_empty(&hdev->adv_instances)) @@ -550,6 +548,12 @@ static int adv_timeout_expire_sync(struct hci_dev *hdev, void *data) return 0; } +static void adv_timeout_expire_destroy(struct hci_dev *hdev, void *data, + int err) +{ + kfree(data); +} + static void adv_timeout_expire(struct work_struct *work) { u8 *inst_ptr; @@ -570,7 +574,9 @@ static void adv_timeout_expire(struct work_struct *work) goto unlock; *inst_ptr = hdev->cur_adv_instance; - hci_cmd_sync_queue(hdev, adv_timeout_expire_sync, inst_ptr, NULL); + if (hci_cmd_sync_queue(hdev, adv_timeout_expire_sync, inst_ptr, + adv_timeout_expire_destroy) < 0) + kfree(inst_ptr); unlock: hci_dev_unlock(hdev); From 3c742feda8fcabf741a17bcf668b63c8f606f9c5 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 6 Aug 2026 20:59:56 +0800 Subject: [PATCH 74/84] Bluetooth: MGMT: free the mesh send cancel command when it is cancelled mesh_send_cancel() queues the pending command with a NULL destroy callback, so it is only freed if send_cancel() runs. A cancelled entry is leaked, as _hci_cmd_sync_cancel_entry() does not release entry->data when there is no destroy callback, and hci_cmd_sync_clear() cancels every pending entry when the controller is unregistered. Nothing else reclaims it either: mgmt_pending_new() does not put the command on hdev->mgmt_pending. The leak also pins the socket reference taken by mgmt_pending_new(), so the mgmt socket is never released. Free the command from a destroy callback. Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh") Signed-off-by: Linmao Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0d6b41fe0b34..bd56830f07ea 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2437,11 +2437,15 @@ static int send_cancel(struct hci_dev *hdev, void *data) mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL, 0, NULL, 0); - mgmt_pending_free(cmd); return 0; } +static void send_cancel_destroy(struct hci_dev *hdev, void *data, int err) +{ + mgmt_pending_free(data); +} + static int mesh_send_cancel(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -2462,7 +2466,8 @@ static int mesh_send_cancel(struct sock *sk, struct hci_dev *hdev, if (!cmd) err = -ENOMEM; else - err = hci_cmd_sync_queue(hdev, send_cancel, cmd, NULL); + err = hci_cmd_sync_queue(hdev, send_cancel, cmd, + send_cancel_destroy); if (err < 0) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL, From 414b365ecea6c30357adee6b8a7c5edc03a03575 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 6 Aug 2026 20:59:57 +0800 Subject: [PATCH 75/84] Bluetooth: MGMT: free the HCI command when it is cancelled mgmt_hci_cmd_sync() queues the pending command with a NULL destroy callback, so it is only freed if send_hci_cmd_sync() runs. A cancelled entry is leaked, as _hci_cmd_sync_cancel_entry() does not release entry->data when there is no destroy callback, and hci_cmd_sync_clear() cancels every pending entry when the controller is unregistered. Nothing else reclaims it either: mgmt_pending_new() does not put the command on hdev->mgmt_pending. The leak also pins the socket reference taken by mgmt_pending_new(), so the mgmt socket is never released. Free the command from a destroy callback. The now-empty done label is replaced by a direct return. Fixes: 827af4787e74 ("Bluetooth: MGMT: Add initial implementation of MGMT_OP_HCI_CMD_SYNC") Signed-off-by: Linmao Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bd56830f07ea..c4ba845f7e5d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2653,7 +2653,7 @@ static int send_hci_cmd_sync(struct hci_dev *hdev, void *data) if (IS_ERR(skb)) { mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, mgmt_status(PTR_ERR(skb))); - goto done; + return 0; } mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, 0, @@ -2661,12 +2661,14 @@ static int send_hci_cmd_sync(struct hci_dev *hdev, void *data) kfree_skb(skb); -done: - mgmt_pending_free(cmd); - return 0; } +static void send_hci_cmd_sync_destroy(struct hci_dev *hdev, void *data, int err) +{ + mgmt_pending_free(data); +} + static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -2684,7 +2686,8 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev, if (!cmd) err = -ENOMEM; else - err = hci_cmd_sync_queue(hdev, send_hci_cmd_sync, cmd, NULL); + err = hci_cmd_sync_queue(hdev, send_hci_cmd_sync, cmd, + send_hci_cmd_sync_destroy); if (err < 0) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, From e48e332d84d8df9bc615530beaa3ece9240da2d6 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Tue, 21 Jul 2026 11:04:58 +0800 Subject: [PATCH 76/84] Bluetooth: btnxpuart: Add M.2 Bluetooth device support using pwrseq Power supply to the M.2 Bluetooth device attached to the host using M.2 connector is controlled using the 'uart' pwrseq device. So add support for getting the pwrseq device if the OF graph link is present. Once obtained, pwrseq_power_on() is called to power up the M.2 Bluetooth card. The power sequencer descriptor is obtained via pwrseq_get() with the UART controller device (serdev->ctrl->dev), since the OF graph link is defined on the UART controller node. Also add the explicit pwrseq_put() call in all exit paths, pwrseq_put() already calls pwrseq_power_off() internally, so no separate pwrseq_power_off() call is needed. Signed-off-by: Sherry Sun Reviewed-by: Bartosz Golaszewski Reviewed-by: Frank Li Reviewed-by: Manivannan Sadhasivam Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 993c48b5f593..81cdd8da5636 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -211,6 +213,7 @@ struct btnxpuart_dev { struct ps_data psdata; struct btnxpuart_data *nxp_data; + struct pwrseq_desc *pwrseq; struct reset_control *pdn; struct hci_uart hu; }; @@ -1860,11 +1863,26 @@ static int nxp_serdev_probe(struct serdev_device *serdev) return err; } + if (of_graph_is_present(dev_of_node(&serdev->ctrl->dev))) { + struct pwrseq_desc *pwrseq; + + pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart"); + if (IS_ERR(pwrseq)) + return dev_err_probe(&serdev->dev, PTR_ERR(pwrseq), + "failed to get pwrseq\n"); + + nxpdev->pwrseq = pwrseq; + err = pwrseq_power_on(pwrseq); + if (err) + goto err_pwrseq_put; + } + /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { dev_err(&serdev->dev, "Can't allocate HCI device\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_pwrseq_put; } reset_control_deassert(nxpdev->pdn); @@ -1895,13 +1913,16 @@ static int nxp_serdev_probe(struct serdev_device *serdev) if (bacmp(&ba, BDADDR_ANY)) hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY); - if (hci_register_dev(hdev) < 0) { + err = hci_register_dev(hdev); + if (err < 0) { dev_err(&serdev->dev, "Can't register HCI device\n"); goto probe_fail; } - if (ps_setup(hdev)) + if (ps_setup(hdev)) { + err = -ENODEV; goto probe_fail_unregister; + } hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr, nxp_coredump_notify); @@ -1913,7 +1934,10 @@ static int nxp_serdev_probe(struct serdev_device *serdev) probe_fail: reset_control_assert(nxpdev->pdn); hci_free_dev(hdev); - return -ENODEV; +err_pwrseq_put: + if (nxpdev->pwrseq) + pwrseq_put(nxpdev->pwrseq); + return err; } static void nxp_serdev_remove(struct serdev_device *serdev) @@ -1940,6 +1964,8 @@ static void nxp_serdev_remove(struct serdev_device *serdev) ps_cleanup(nxpdev); hci_unregister_dev(hdev); reset_control_assert(nxpdev->pdn); + if (nxpdev->pwrseq) + pwrseq_put(nxpdev->pwrseq); hci_free_dev(hdev); } From 5d95286b6d6e8f1d304da7522bfa6860fc017e48 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Thu, 6 Aug 2026 17:39:53 +0000 Subject: [PATCH 77/84] Bluetooth: MGMT: reject HCI_CMD_SYNC params_len above 255 mgmt_hci_cmd_sync() checks that the message length agrees with params_len but puts no upper bound on it. params_len is __le16 while the parameter length in the HCI command header is a u8: struct hci_command_hdr { __le16 opcode; __u8 plen; } __packed; hci_cmd_sync_alloc() assigns one to the other: hdr->plen = plen; if (plen) skb_put_data(skb, param, plen); so a params_len of 256 leaves plen at 0 while all 256 bytes are still appended. The frame handed to the driver then declares no parameters and carries 256 of them. On a length framed transport such as H:4 the controller takes the trailing bytes as the start of the next packet. The mgmt socket MTU is HCI_MAX_FRAME_SIZE, so params_len can reach about 1KB this way. Commit 03f1700b9b4d ("Bluetooth: MGMT: reject malformed HCI_CMD_SYNC commands") only made params_len agree with the message length, a value that fits the message but not the header field is still accepted. Reject params_len that does not fit the header field. Fixes: 827af4787e74 ("Bluetooth: MGMT: Add initial implementation of MGMT_OP_HCI_CMD_SYNC") Cc: stable@vger.kernel.org Signed-off-by: Ali Ahmet Memis Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c4ba845f7e5d..860c086011b7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2681,6 +2681,14 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev, return mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, MGMT_STATUS_INVALID_PARAMS); + /* The HCI command header carries the parameter length in a u8, a + * larger value would be truncated there while the parameters are + * still appended to the frame in full. + */ + if (le16_to_cpu(cp->params_len) > U8_MAX) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); cmd = mgmt_pending_new(sk, MGMT_OP_HCI_CMD_SYNC, hdev, data, len); if (!cmd) From b0c0b37940115383e7ea65d4d988f9b9e613ab92 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 7 Aug 2026 23:14:47 +0800 Subject: [PATCH 78/84] Bluetooth: btmtksdio: fix usage_count leak when autosuspend_delay is negative btmtksdio_setup() calls pm_runtime_use_autosuspend() when runtime PM is supported, but btmtksdio_remove() does not call the matching pm_runtime_dont_use_autosuspend() when removing the device. If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during driver teardown, this reference is not dropped and usage_count remains unbalanced. Add the missing pm_runtime_dont_use_autosuspend() call in the remove path before restoring the runtime PM usage reference. This issue was found by manual code inspection. Fixes: 7f3c563c575e ("Bluetooth: btmtksdio: Add runtime PM support to SDIO based Bluetooth") Signed-off-by: Guangshuo Li Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmtksdio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index c6f80c419e90..4e1012e90979 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -1480,6 +1480,9 @@ static void btmtksdio_remove(struct sdio_func *func) if (test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state)) btmtksdio_close(hdev); + if (bdev->data->pm_runtime_supported) + pm_runtime_dont_use_autosuspend(bdev->dev); + /* Be consistent the state in btmtksdio_probe */ pm_runtime_get_noresume(bdev->dev); From e3643fbddb257c928c075cab05bbd929106b56ee Mon Sep 17 00:00:00 2001 From: Laxman Acharya Date: Wed, 5 Aug 2026 23:22:01 +0545 Subject: [PATCH 79/84] Bluetooth: hci_event: fix out-of-bounds read in LE PA report reassembly hci_le_per_adv_report_evt() is dispatched with a minimum length of sizeof(struct hci_ev_le_per_adv_report), which only covers the fixed part of the event and not the trailing data[] array: struct hci_ev_le_per_adv_report { __le16 sync_handle; __u8 tx_power; __u8 rssi; __u8 cte_type; __u8 data_status; __u8 length; __u8 data[]; } __packed; The handler notifies the ISO layer via hci_proto_connect_ind(), which reaches iso_connect_ind(). That function retrieves the stored event with hci_recv_event_data() and, while reassembling the periodic advertising data, does: memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset, ev->data, ev->length); ev->length is taken directly from the event and is never validated against the amount of data the event actually carries. A controller that reports a length larger than the received event therefore causes the memcpy() to read past the end of the event buffer. The leaked bytes are stored in hcon->le_per_adv_data and can subsequently be read back from user space via getsockopt(BT_ISO_BASE). Validate that the event contains ev->length data bytes before it is consumed, mirroring the check already performed by hci_le_ext_adv_report_evt() and hci_le_adv_report_evt(). Signed-off-by: Laxman Acharya Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 371ca8236bc5..3eb1eaf6e6a0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -6658,6 +6658,13 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "sync_handle 0x%4.4x", le16_to_cpu(ev->sync_handle)); + /* The reassembly in iso_connect_ind() copies ev->length bytes from the + * stored event, so make sure the event actually carries that many data + * bytes before it is consumed. + */ + if (!hci_le_ev_skb_pull(hdev, skb, HCI_EV_LE_PER_ADV_REPORT, ev->length)) + return; + hci_dev_lock(hdev); mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, PA_LINK, &flags); From 75722cde87ee24029e93e4e32d85309989f55991 Mon Sep 17 00:00:00 2001 From: Muhammad Saheed Date: Wed, 5 Aug 2026 01:40:51 +0530 Subject: [PATCH 80/84] Bluetooth: hci_sync: Disable legacy instance's ext adv before setup snapshot hci_setup_ext_adv_instance_sync(...) only disabled HCI_OP_LE_SET_EXT_ADV_ENABLE before setup snapshot in case of non-legacy instances (instance > 0) and never disabled the same for legacy instance (instance == 0). This would lead to failure in setting ext adv params with HCI_ERROR_COMMAND_DISALLOWED (0x0c) error like below, when toggling the discoverable/connectable property of a controller with advertising enabled. ``` $ btmgmt advertising off hci0 Set Advertising complete, settings: powered ssp br/edr le secure-conn wide-band-speech cis-central cis-peripheral $ btmgmt connectable on hci0 Set Connectable complete, settings: powered connectable ssp br/edr le secure-conn wide-band-speech cis-central cis-peripheral $ btmgmt connectable off hci0 Set Connectable complete, settings: powered ssp br/edr le secure-conn wide-band-speech cis-central cis-peripheral $ btmgmt advertising on hci0 Set Advertising complete, settings: powered connectable ssp br/edr le advertising secure-conn wide-band-speech cis-central cis-peripheral $ btmgmt connectable on Set Connectable for hci0 failed with status 0x0a (Busy) $ btmgmt connectable off Set Connectable for hci0 failed with status 0x0a (Busy) $ dmesg ... [ 21.970527] hci0: Opcode 0x2036 [ 21.970529] hci0: opcode 0x2036 plen 25 [ 21.970537] hci0: skb len 28 [ 21.970539] hci0: length 1 [ 21.976099] hci0: result 0x0c [ 21.976105] hci0: end: err -16 [ 21.976114] Bluetooth: hci0: Opcode 0x2036 failed: -16 ``` Signed-off-by: Muhammad Saheed Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index df2d037970f9..b5897545d795 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -1150,6 +1150,32 @@ int hci_update_random_address_sync(struct hci_dev *hdev, bool require_privacy, return 0; } +static int hci_disable_ext_adv_legacy_instance_sync(struct hci_dev *hdev) +{ + struct hci_cp_le_set_ext_adv_enable *cp; + struct hci_cp_ext_adv_set *set; + u8 data[sizeof(*cp) + sizeof(*set) * 1]; + u8 size; + + if (!hci_dev_test_flag(hdev, HCI_LE_ADV_0)) + return 0; + + memset(data, 0, sizeof(data)); + + cp = (void *)data; + set = (void *)cp->data; + + cp->num_of_sets = 0x01; + cp->enable = 0x00; + + set->handle = 0x00; + + size = sizeof(*cp) + sizeof(*set) * cp->num_of_sets; + + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_ENABLE, + size, data, HCI_CMD_TIMEOUT); +} + static int hci_disable_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) { struct hci_cp_le_set_ext_adv_enable *cp; @@ -1375,6 +1401,10 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) return -EINVAL; } } else { + err = hci_disable_ext_adv_legacy_instance_sync(hdev); + if (err) + return err; + adv = NULL; } From 9838a80096ba472d5e03057136a112631aabae6e Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Fri, 7 Aug 2026 00:59:55 +0000 Subject: [PATCH 81/84] Bluetooth: ISO: do not force BT_LISTEN after a failed BIG sync iso_sock_recvmsg() handles the deferred setup of a broadcast sink by dropping the socket lock, calling iso_conn_big_sync() and taking the lock again: release_sock(sk); iso_conn_big_sync(sk); lock_sock(sk); sk->sk_state = BT_LISTEN; The state is written unconditionally, but iso_conn_big_sync() returns void and has paths that do nothing at all: hci_get_route() may fail, and after re-acquiring the socket lock the connection may already be gone, in which case it bails out without ever issuing an LE BIG Create Sync. While the lock is dropped the connection can be torn down, for example when the controller reports HCI_EV_LE_PA_SYNC_LOST: hci_le_pa_sync_lost_evt() hci_disconn_cfm() -> iso_disconn_cfm() -> iso_conn_del() iso_chan_del() iso_pi(sk)->conn = NULL sk->sk_state = BT_CLOSED sock_set_flag(sk, SOCK_ZAPPED) iso_conn_big_sync() then finds conn == NULL and returns, but the caller still overwrites the BT_CLOSED that iso_chan_del() has just set. The socket ends up marked BT_LISTEN with no connection, so recvmsg() reports success for a setup that never happened and a later accept() waits for BIS connections that can never arrive instead of failing. A concurrent shutdown() reaches the same write by another route: __iso_sock_close() takes the BT_CONNECT2 PA sync path to iso_sock_disconn(), which sets BT_DISCONN but leaves conn and conn->hcon in place, so iso_conn_big_sync() succeeds and BT_LISTEN is written over BT_DISCONN. Both the BT_CONNECT2 and the BT_CONNECTED case write the state the same way. Let iso_conn_big_sync() report whether the BIG sync was started, and only move the socket to BT_LISTEN when it was and when the state has not changed while the lock was dropped, mirroring what the BT_CONNECT case of the same switch already does with iso_connect_cis(). Both conditions are needed, the error alone does not cover the shutdown() race. This corrupts the socket state machine only, it is not a memory safety issue. KASAN and lockdep stayed quiet in all of the runs below. Reproduced with an emulated controller over /dev/vhci on a KASAN + PROVE_LOCKING kernel. A PA sync broadcast sink socket is driven to BT_CONNECT2 and recvmsg() on it is raced against teardown, with a debug delay inside the lock-dropped section to widen the window: - HCI_EV_LE_PA_SYNC_LOST injected: 64 of 64 rounds left the socket in BT_LISTEN with the connection gone, recvmsg() returned 0 and accept() on that fd returned EAGAIN, which iso_sock_accept() can only do while the socket is BT_LISTEN. With this patch, 0 of 64, recvmsg() returns an error and accept() returns EBADFD. - shutdown() instead of a controller event: 24 of 32 rounds wedged in BT_LISTEN, 0 of 32 with this patch. With only the error check in place and a short window, one round still wedged while recvmsg() returned 0, which is the case the state re-check covers. An unraced control round behaves the same before and after: recvmsg() returns 0, the socket reaches BT_LISTEN and an LE BIG Create Sync is issued. Fixes: 7a17308c1788 ("Bluetooth: iso: Fix circular lock in iso_conn_big_sync") Cc: stable@vger.kernel.org Signed-off-by: Ali Ahmet Memis Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index a461c8a4efed..e46242d4455d 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1658,9 +1658,9 @@ static void iso_conn_defer_accept(struct hci_conn *conn) hci_send_cmd(hdev, HCI_OP_LE_ACCEPT_CIS, sizeof(cp), &cp); } -static void iso_conn_big_sync(struct sock *sk) +static int iso_conn_big_sync(struct sock *sk) { - int err; + int err = 0; struct hci_dev *hdev; struct iso_conn *conn; bdaddr_t src, dst; @@ -1675,7 +1675,7 @@ static void iso_conn_big_sync(struct sock *sk) hdev = hci_get_route(&dst, &src, src_type); if (!hdev) - return; + return -EHOSTUNREACH; /* hci_le_big_create_sync requires hdev lock to be held, since * it enqueues the HCI LE BIG Create Sync command via @@ -1691,8 +1691,10 @@ static void iso_conn_big_sync(struct sock *sk) * both before dereferencing conn->hcon. */ conn = iso_pi(sk)->conn; - if (!conn || !conn->hcon) + if (!conn || !conn->hcon) { + err = -ENOTCONN; goto unlock; + } if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { err = hci_conn_big_create_sync(hdev, conn->hcon, @@ -1708,6 +1710,8 @@ static void iso_conn_big_sync(struct sock *sk) release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); + + return err; } static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg, @@ -1732,10 +1736,19 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg, case BT_CONNECT2: if (test_bit(BT_SK_PA_SYNC, &pi->flags)) { release_sock(sk); - iso_conn_big_sync(sk); + err = iso_conn_big_sync(sk); lock_sock(sk); - sk->sk_state = BT_LISTEN; + /* The socket lock was dropped, so the + * connection may have been torn down + * meanwhile and iso_chan_del() may have + * already moved the socket to BT_CLOSED. + * Only move on to BT_LISTEN if the BIG sync + * was actually started and nothing else has + * changed the state. + */ + if (!err && sk->sk_state == BT_CONNECT2) + sk->sk_state = BT_LISTEN; } else { iso_conn_defer_accept(pi->conn->hcon); sk->sk_state = BT_CONFIG; @@ -1746,10 +1759,11 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg, case BT_CONNECTED: if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) { release_sock(sk); - iso_conn_big_sync(sk); + err = iso_conn_big_sync(sk); lock_sock(sk); - sk->sk_state = BT_LISTEN; + if (!err && sk->sk_state == BT_CONNECTED) + sk->sk_state = BT_LISTEN; early_ret = true; } From 884cf2cc957da7ac178a0e6c6c69ddfec0481cc8 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Thu, 6 Aug 2026 23:06:21 +0000 Subject: [PATCH 82/84] Bluetooth: ISO: zero the sockaddr before returning it in getname iso_sock_getname() fills a struct sockaddr_iso in place and returns its size without clearing it first, so bytes it does not write are copied to user space from the kernel stack. The getsockname(2) and getpeername(2) paths both run through do_getsockname(), which hands getname() an uninitialized sockaddr_storage on the stack and copies back up to the number of bytes getname() returns, so the driver has to initialize every byte it accounts for. Two ranges are left uninitialized: - struct sockaddr_iso is 10 bytes but only 9 are written (family, iso_bdaddr, iso_bdaddr_type), leaking the trailing pad byte on every call. - for a broadcast peer (BIS_LINK or PA_LINK) the returned length grows by sizeof(struct sockaddr_iso_bc), but only bc_sid, bc_num_bis and bc_bis are filled; bc_bdaddr and bc_bdaddr_type, the first 7 bytes of that structure, are never written. An unprivileged process can open a BTPROTO_ISO socket and reach the pad leak with getsockname(); the broadcast leak needs an established BIS/PA connection. l2cap and rfcomm already memset their sockaddr in getname for the same reason; do the same here. Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") Fixes: 0a766a0affb5 ("Bluetooth: ISO: Fix getpeername not returning sockaddr_iso_bc fields") Cc: stable@vger.kernel.org Signed-off-by: Ali Ahmet Memis Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index e46242d4455d..aa2ce78f56a2 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1536,6 +1536,7 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, lock_sock(sk); + memset(sa, 0, sizeof(struct sockaddr_iso)); addr->sa_family = AF_BLUETOOTH; if (peer) { @@ -1546,6 +1547,7 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, sa->iso_bdaddr_type = iso_pi(sk)->dst_type; if (hcon && (hcon->type == BIS_LINK || hcon->type == PA_LINK)) { + memset(sa->iso_bc, 0, sizeof(struct sockaddr_iso_bc)); sa->iso_bc->bc_sid = iso_pi(sk)->bc_sid; sa->iso_bc->bc_num_bis = iso_pi(sk)->bc_num_bis; memcpy(sa->iso_bc->bc_bis, iso_pi(sk)->bc_bis, From 0079e1a944634ab2dc1c7cdec1144486d096407e Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Fri, 7 Aug 2026 01:25:54 +0000 Subject: [PATCH 83/84] Bluetooth: MSFT: validate evt_prefix_len against the response length read_supported_features() only checks that the response covers the fixed part of struct msft_rp_read_supported_features, which is 11 bytes: if (skb->len < sizeof(*rp)) { bt_dev_err(hdev, "MSFT supported features length mismatch"); goto failed; } evt_prefix[] is a flexible array member and rp->evt_prefix_len is an unvalidated u8 taken straight out of that response, so msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len, GFP_KERNEL); copies up to 255 bytes from a reply that may have carried none of them. What is copied is data the controller never sent, and it is then used to match incoming vendor events in msft_vendor_evt(). This is not an out-of-bounds access. An skb data allocation always has at least SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) bytes past the payload, which is more than the 255 byte maximum, so the read stays inside the allocation and KASAN does not report it. It is still a read of bytes the host was never given, with the length fully controlled by the controller. Reject a response that is too short for the prefix it declares. Verified with an emulated controller over /dev/vhci on a KASAN kernel, with vhci made to advertise an MSFT opcode the way btintel, btqca, btmtk and btrtl do unconditionally. A reply of exactly 11 bytes declaring evt_prefix_len = 255 reaches kmemdup and copies 255 bytes ("skb->len=11 evt_prefix_len=255", with the copied buffer dumped); since the reply ends at the fixed part, all 255 come from past the end of the response. No KASAN report is produced, as expected from the allocation slack described above. With this patch the response is rejected with "MSFT event prefix length mismatch" and msft->evt_prefix is left unset. Fixes: 145373cb1b1f ("Bluetooth: Add framework for Microsoft vendor extension") Signed-off-by: Ali Ahmet Memis Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/msft.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index d7badce8746c..ded68568e6c9 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -165,6 +165,11 @@ static bool read_supported_features(struct hci_dev *hdev, if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES) goto failed; + if (skb->len < sizeof(*rp) + rp->evt_prefix_len) { + bt_dev_err(hdev, "MSFT event prefix length mismatch"); + goto failed; + } + if (rp->evt_prefix_len > 0) { msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len, GFP_KERNEL); From 43a556b2fd43f2df6dded59c2e26560a27874c24 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Fri, 7 Aug 2026 02:03:44 +0000 Subject: [PATCH 84/84] Bluetooth: RFCOMM: take rfcomm_mutex for the deferred setup accept rfcomm_sock_recvmsg() completes a deferred setup by calling rfcomm_dlc_accept() without holding any RFCOMM lock: if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { rfcomm_dlc_accept(d); return 0; } and rfcomm_dlc_accept() dereferences the session on its first line: struct sock *sk = d->session->sock->sk; Every other path that touches d->session runs under rfcomm_mutex: rfcomm_dlc_open(), rfcomm_dlc_close(), rfcomm_dlc_exists(), rfcomm_dlc_send_rpn(), and the RFCOMM thread through rfcomm_process_sessions(). rfcomm_connect_ind() is even documented as "called under rfcomm_lock()". This call site is the only one that skips it. The RFCOMM_DEFER_SETUP bit looks like it serialises the accept against teardown, since __rfcomm_dlc_close() returns early when it wins the test_and_clear. But rfcomm_recv_disc() forces the state first: d->state = BT_CLOSED; __rfcomm_dlc_close(d, err); and the early return only covers BT_CONNECT, BT_CONFIG, BT_OPEN and BT_CONNECT2. With the state already BT_CLOSED that switch does not match, the bit is never consulted, and __rfcomm_dlc_close() falls through to rfcomm_dlc_unlink(), which sets d->session = NULL. So a remote DISC on a deferred dlc clears the session while leaving RFCOMM_DEFER_SETUP set. The next recvmsg() then passes the test_and_clear and dereferences a NULL session. No timing window is needed: once the DISC has been processed, the dereference is unconditional. Give rfcomm_dlc_accept() the same shape as rfcomm_dlc_open() and rfcomm_dlc_close(): an exported wrapper that takes rfcomm_mutex and re-checks the session, around a __rfcomm_dlc_accept() that the two in-core callers, which already hold the mutex, keep using. Reproduced on a KASAN + PROVE_LOCKING kernel with a BR/EDR peer emulated over /dev/vhci: the peer brings up an ACL link, opens L2CAP on the RFCOMM PSM, starts a session, opens a dlc on a channel bound with BT_DEFER_SETUP, and sends DISC after the socket is accepted. recv() on the accepted socket then hits: Oops: general protection fault KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] RIP: 0010:rfcomm_dlc_accept+0x54/0x350 Call Trace: rfcomm_sock_recvmsg+0x1cd/0x230 sock_recvmsg+0x166/0x1c0 __sys_recvfrom+0x20d/0x300 0x10 is the offset of sock in struct rfcomm_session. With this patch the same run completes with recv() returning 0 and no report, and lockdep stays quiet, confirming rfcomm_mutex is still taken before lock_sock on this path as it is on the thread side. Fixes: bb23c0ab8246 ("Bluetooth: Add support for deferring RFCOMM connection setup") Cc: stable@vger.kernel.org Signed-off-by: Ali Ahmet Memis Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/rfcomm/core.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 2e8c080b4d9e..9cdfea666a2c 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1331,7 +1331,10 @@ static struct rfcomm_session *rfcomm_recv_disc(struct rfcomm_session *s, return s; } -void rfcomm_dlc_accept(struct rfcomm_dlc *d) +/* Must be called with rfcomm_mutex held, so that the session cannot be + * unlinked from under us. + */ +static void __rfcomm_dlc_accept(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; @@ -1353,6 +1356,21 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d) rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); } +void rfcomm_dlc_accept(struct rfcomm_dlc *d) +{ + rfcomm_lock(); + + /* rfcomm_recv_disc() sets the dlc state to BT_CLOSED before calling + * __rfcomm_dlc_close(), so the RFCOMM_DEFER_SETUP handshake there is + * skipped and the session can already be unlinked by the time the + * deferred accept runs from rfcomm_sock_recvmsg(). + */ + if (d->session) + __rfcomm_dlc_accept(d); + + rfcomm_unlock(); +} + static void rfcomm_check_accept(struct rfcomm_dlc *d) { if (rfcomm_check_security(d)) { @@ -1365,7 +1383,7 @@ static void rfcomm_check_accept(struct rfcomm_dlc *d) d->state_change(d, 0); rfcomm_dlc_unlock(d); } else - rfcomm_dlc_accept(d); + __rfcomm_dlc_accept(d); } else { set_bit(RFCOMM_AUTH_PENDING, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); @@ -1958,7 +1976,7 @@ static void rfcomm_process_dlcs(struct rfcomm_session *s) d->state_change(d, 0); rfcomm_dlc_unlock(d); } else - rfcomm_dlc_accept(d); + __rfcomm_dlc_accept(d); } continue; } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {