From 4746e2cd0e183314cfb362383c2b4555ebd2b49e Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Wed, 3 Dec 2025 11:37:12 +0800 Subject: [PATCH 01/43] Bluetooth: hci_qca: Fix SSR (SubSystem Restart) fail when BT_EN is pulled up by hw On QCS9075 and QCA8275 platforms, the BT_EN pin is always pulled up by hw and cannot be controlled by the host. As a result, in case of a firmware crash, the host cannot trigger a cold reset. Instead, the BT controller performs a warm restart on its own, without reloading the firmware. This leads to the controller remaining in IBS_WAKE state, while the host expects it to be in sleep mode. The mismatch causes HCI reset commands to time out. Additionally, the driver does not clear internal flags QCA_SSR_TRIGGERED and QCA_IBS_DISABLED, which blocks the reset sequence. If the SSR duration exceeds 2 seconds, the host may enter TX sleep mode due to tx_idle_timeout, further preventing recovery. Also, memcoredump_flag is not cleared, so only the first SSR generates a coredump. Tell the driver that the BT controller has undergone a proper restart sequence: - Clear QCA_SSR_TRIGGERED and QCA_IBS_DISABLED flags after SSR. - Add a 50ms delay to allow the controller to complete its warm reset. - Reset tx_idle_timer to prevent the host from entering TX sleep mode. - Clear memcoredump_flag to allow multiple coredump captures. Apply these steps only when HCI_QUIRK_NON_PERSISTENT_SETUP is not set, which indicates that BT_EN is defined in DTS and cannot be toggled. Refer to the comment in include/net/bluetooth/hci.h for details on HCI_QUIRK_NON_PERSISTENT_SETUP. Reviewed-by: Dmitry Baryshkov Signed-off-by: Shuai Zhang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_qca.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 888176b0faa90..a3c217571c3c4 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1653,6 +1653,39 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code) skb_queue_purge(&qca->rx_memdump_q); } + /* + * If the BT chip's bt_en pin is connected to a 3.3V power supply via + * hardware and always stays high, driver cannot control the bt_en pin. + * As a result, during SSR (SubSystem Restart), QCA_SSR_TRIGGERED and + * QCA_IBS_DISABLED flags cannot be cleared, which leads to a reset + * command timeout. + * Add an msleep delay to ensure controller completes the SSR process. + * + * Host will not download the firmware after SSR, controller to remain + * in the IBS_WAKE state, and the host needs to synchronize with it + * + * Since the bluetooth chip has been reset, clear the memdump state. + */ + if (!hci_test_quirk(hu->hdev, HCI_QUIRK_NON_PERSISTENT_SETUP)) { + /* + * When the SSR (SubSystem Restart) duration exceeds 2 seconds, + * it triggers host tx_idle_delay, which sets host TX state + * to sleep. Reset tx_idle_timer after SSR to prevent + * host enter TX IBS_Sleep mode. + */ + mod_timer(&qca->tx_idle_timer, jiffies + + msecs_to_jiffies(qca->tx_idle_delay)); + + /* Controller reset completion time is 50ms */ + msleep(50); + + clear_bit(QCA_SSR_TRIGGERED, &qca->flags); + clear_bit(QCA_IBS_DISABLED, &qca->flags); + + qca->tx_ibs_state = HCI_IBS_TX_AWAKE; + qca->memdump_state = QCA_MEMDUMP_IDLE; + } + clear_bit(QCA_HW_ERROR_EVENT, &qca->flags); } From 3d55304c6e437fe5e7fc02a3b7966d928bbb0b83 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Thu, 4 Dec 2025 22:40:20 +0200 Subject: [PATCH 02/43] Bluetooth: MGMT: report BIS capability flags in supported settings MGMT_SETTING_ISO_BROADCASTER and MGMT_SETTING_ISO_RECEIVER flags are missing from supported_settings although they are in current_settings. Report them also in supported_settings to be consistent. Fixes: ae7533613133 ("Bluetooth: Check for ISO support in controller") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c11cdef42b6f6..5be9b8c919490 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -849,6 +849,12 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (cis_peripheral_capable(hdev)) settings |= MGMT_SETTING_CIS_PERIPHERAL; + if (bis_capable(hdev)) + settings |= MGMT_SETTING_ISO_BROADCASTER; + + if (sync_recv_capable(hdev)) + settings |= MGMT_SETTING_ISO_SYNC_RECEIVER; + if (ll_privacy_capable(hdev)) settings |= MGMT_SETTING_LL_PRIVACY; From 9b10de4acec0db65e2f3139c4b6a3e664a46dd24 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 10 Dec 2025 11:38:08 -0500 Subject: [PATCH 03/43] Bluetooth: hci_conn: Fix using conn->le_{tx,rx}_phy as supported PHYs conn->le_{tx,rx}_phy is not actually a bitfield as it set by HCI_EV_LE_PHY_UPDATE_COMPLETE it is actually correspond to the current PHY in use not what is supported by the controller, so this introduces different fields (conn->le_{tx,rx}_def_phys) to track what PHYs are supported by the connection. Fixes: eab2404ba798 ("Bluetooth: Add BT_PHY socket option") Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 17 +++++++++++------ net/bluetooth/hci_event.c | 30 +++++++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4263e71a23efb..8aadf4cdead2b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -730,6 +730,8 @@ struct hci_conn { __u16 le_per_adv_data_offset; __u8 le_adv_phy; __u8 le_adv_sec_phy; + __u8 le_tx_def_phys; + __u8 le_rx_def_phys; __u8 le_tx_phy; __u8 le_rx_phy; __s8 rssi; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c3f7828bf9d54..5a4374ccf8e84 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1008,6 +1008,11 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, /* conn->src should reflect the local identity address */ hci_copy_identity_address(hdev, &conn->src, &conn->src_type); conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu; + /* Use the controller supported PHYS as default until the + * remote features are resolved. + */ + conn->le_tx_def_phys = hdev->le_tx_def_phys; + conn->le_rx_def_phys = hdev->le_tx_def_phys; break; case CIS_LINK: /* conn->src should reflect the local identity address */ @@ -2928,22 +2933,22 @@ u32 hci_conn_get_phy(struct hci_conn *conn) break; case LE_LINK: - if (conn->le_tx_phy & HCI_LE_SET_PHY_1M) + if (conn->le_tx_def_phys & HCI_LE_SET_PHY_1M) phys |= BT_PHY_LE_1M_TX; - if (conn->le_rx_phy & HCI_LE_SET_PHY_1M) + if (conn->le_rx_def_phys & HCI_LE_SET_PHY_1M) phys |= BT_PHY_LE_1M_RX; - if (conn->le_tx_phy & HCI_LE_SET_PHY_2M) + if (conn->le_tx_def_phys & HCI_LE_SET_PHY_2M) phys |= BT_PHY_LE_2M_TX; - if (conn->le_rx_phy & HCI_LE_SET_PHY_2M) + if (conn->le_rx_def_phys & HCI_LE_SET_PHY_2M) phys |= BT_PHY_LE_2M_RX; - if (conn->le_tx_phy & HCI_LE_SET_PHY_CODED) + if (conn->le_tx_def_phys & HCI_LE_SET_PHY_CODED) phys |= BT_PHY_LE_CODED_TX; - if (conn->le_rx_phy & HCI_LE_SET_PHY_CODED) + if (conn->le_rx_def_phys & HCI_LE_SET_PHY_CODED) phys |= BT_PHY_LE_CODED_RX; break; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a9868f17ef40f..58075bf720554 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -6607,8 +6607,20 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data, conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { - if (!ev->status) - memcpy(conn->features[0], ev->features, 8); + if (!ev->status) { + memcpy(conn->le_features, ev->features, 8); + + /* Update supported PHYs */ + if (!(conn->le_features[1] & HCI_LE_PHY_2M)) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_2M; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_2M; + } + + if (!(conn->le_features[1] & HCI_LE_PHY_CODED)) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED; + } + } if (conn->state == BT_CONFIG) { __u8 status; @@ -7221,9 +7233,21 @@ static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev, if (!conn) goto unlock; - if (!ev->status) + if (!ev->status) { memcpy(conn->le_features, ev->features, 248); + /* Update supported PHYs */ + if (!(conn->le_features[1] & HCI_LE_PHY_2M)) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_2M; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_2M; + } + + if (!(conn->le_features[1] & HCI_LE_PHY_CODED)) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED; + } + } + if (conn->state == BT_CONFIG) { __u8 status; From 3a7cf69ce3cfce597357cb677a4f23e0d26481a3 Mon Sep 17 00:00:00 2001 From: Raphael Pinsonneault-Thibeault Date: Wed, 10 Dec 2025 11:02:28 -0500 Subject: [PATCH 04/43] Bluetooth: btusb: revert use of devm_kzalloc in btusb This reverts commit 98921dbd00c4e ("Bluetooth: Use devm_kzalloc in btusb.c file"). In btusb_probe(), we use devm_kzalloc() to allocate the btusb data. This ties the lifetime of all the btusb data to the binding of a driver to one interface, INTF. In a driver that binds to other interfaces, ISOC and DIAG, this is an accident waiting to happen. The issue is revealed in btusb_disconnect(), where calling usb_driver_release_interface(&btusb_driver, data->intf) will have devm free the data that is also being used by the other interfaces of the driver that may not be released yet. To fix this, revert the use of devm and go back to freeing memory explicitly. Fixes: 98921dbd00c4e ("Bluetooth: Use devm_kzalloc in btusb.c file") Signed-off-by: Raphael Pinsonneault-Thibeault Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 8ed3883ab8eef..ded09e94d296d 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4052,7 +4052,7 @@ static int btusb_probe(struct usb_interface *intf, return -ENODEV; } - data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -4075,8 +4075,10 @@ static int btusb_probe(struct usb_interface *intf, } } - if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) + if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) { + kfree(data); return -ENODEV; + } if (id->driver_info & BTUSB_AMP) { data->cmdreq_type = USB_TYPE_CLASS | 0x01; @@ -4131,8 +4133,10 @@ static int btusb_probe(struct usb_interface *intf, data->recv_acl = hci_recv_frame; hdev = hci_alloc_dev_priv(priv_size); - if (!hdev) + if (!hdev) { + kfree(data); return -ENOMEM; + } hdev->bus = HCI_USB; hci_set_drvdata(hdev, data); @@ -4406,6 +4410,7 @@ static int btusb_probe(struct usb_interface *intf, if (data->reset_gpio) gpiod_put(data->reset_gpio); hci_free_dev(hdev); + kfree(data); return err; } @@ -4454,6 +4459,7 @@ static void btusb_disconnect(struct usb_interface *intf) } hci_free_dev(hdev); + kfree(data); } #ifdef CONFIG_PM From b96a6fd8cb4ec10aff6d3beadf558c53f2bedbc1 Mon Sep 17 00:00:00 2001 From: Bluecross Date: Wed, 10 Dec 2025 23:22:25 +0300 Subject: [PATCH 05/43] Bluetooth: btusb: Add support for MediaTek7920 0489:e158 Add support for MediaTek7920 0489:e158 /sys/kernel/debug/usb/devices reports for that device: T: Bus=03 Lev=01 Prnt=01 Port=02 Cnt=03 Dev#= 5 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e158 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=(none) 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=(none) 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: Andrew Elatsev 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 ded09e94d296d..646de80c7e7be 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -637,6 +637,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3622), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe158), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional MediaTek MT7921 Bluetooth devices */ { USB_DEVICE(0x0489, 0xe0c8), .driver_info = BTUSB_MEDIATEK | From 0ad59c87f41cfd10f9e686f8a076e927c5f2288f Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Wed, 10 Dec 2025 23:21:09 +0000 Subject: [PATCH 06/43] Bluetooth: hci_bcm4377: Use generic power management Switch to the generic PCI power management framework and remove legacy callbacks like .suspend() and .resume(). With the generic framework, the standard PCI related work like: - pci_save/restore_state() - pci_enable/disable_device() - pci_set_power_state() is handled by the PCI core and this driver should implement only hci_bcm4377 specific operations in its respective callback functions. Signed-off-by: Vaibhav Gupta Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcm4377.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c index 45e6d84224ee3..925d0a6359453 100644 --- a/drivers/bluetooth/hci_bcm4377.c +++ b/drivers/bluetooth/hci_bcm4377.c @@ -2416,8 +2416,9 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id) hdev); } -static int bcm4377_suspend(struct pci_dev *pdev, pm_message_t state) +static int bcm4377_suspend(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev); int ret; @@ -2431,8 +2432,9 @@ static int bcm4377_suspend(struct pci_dev *pdev, pm_message_t state) return 0; } -static int bcm4377_resume(struct pci_dev *pdev) +static int bcm4377_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev); iowrite32(BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE, @@ -2441,6 +2443,8 @@ static int bcm4377_resume(struct pci_dev *pdev) return hci_resume_dev(bcm4377->hdev); } +static DEFINE_SIMPLE_DEV_PM_OPS(bcm4377_ops, bcm4377_suspend, bcm4377_resume); + static const struct dmi_system_id bcm4377_dmi_board_table[] = { { .matches = { @@ -2541,8 +2545,7 @@ static struct pci_driver bcm4377_pci_driver = { .name = "hci_bcm4377", .id_table = bcm4377_devid_table, .probe = bcm4377_probe, - .suspend = bcm4377_suspend, - .resume = bcm4377_resume, + .driver.pm = &bcm4377_ops, }; module_pci_driver(bcm4377_pci_driver); From fd52b1d0ad7de69b1266c2efa7065e1d2f8acb49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 17 Dec 2025 12:20:33 +0100 Subject: [PATCH 07/43] Bluetooth: btmtksdio: Use pm_ptr instead of #ifdef CONFIG_PM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This increases build coverage and allows to drop an #ifdef. Signed-off-by: Uwe Kleine-König Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmtksdio.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index fba3ab6d30a5a..e986e5af51ae1 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -1472,7 +1472,6 @@ static void btmtksdio_remove(struct sdio_func *func) hci_free_dev(hdev); } -#ifdef CONFIG_PM static int btmtksdio_runtime_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); @@ -1542,18 +1541,13 @@ static const struct dev_pm_ops btmtksdio_pm_ops = { RUNTIME_PM_OPS(btmtksdio_runtime_suspend, btmtksdio_runtime_resume, NULL) }; -#define BTMTKSDIO_PM_OPS (&btmtksdio_pm_ops) -#else /* CONFIG_PM */ -#define BTMTKSDIO_PM_OPS NULL -#endif /* CONFIG_PM */ - static struct sdio_driver btmtksdio_driver = { .name = "btmtksdio", .probe = btmtksdio_probe, .remove = btmtksdio_remove, .id_table = btmtksdio_table, .drv = { - .pm = BTMTKSDIO_PM_OPS, + .pm = pm_ptr(&btmtksdio_pm_ops), } }; From ad2804ccc724fa12a5497d4ec04aa5fc3183fa6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20S=C3=B8rensen?= Date: Tue, 16 Dec 2025 10:20:09 +0100 Subject: [PATCH 08/43] Bluetooth: hci_conn: use mod_delayed_work for active mode timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hci_conn_enter_active_mode() uses queue_delayed_work() with the intention that the work will run after the given timeout. However, queue_delayed_work() does nothing if the work is already queued, so depending on the link policy we may end up putting the connection into idle mode every hdev->idle_timeout ms. Use mod_delayed_work() instead so the work is queued if not already queued, and the timeout is updated otherwise. Signed-off-by: Stefan Sørensen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5a4374ccf8e84..1b98b64674854 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2619,8 +2619,8 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) timer: if (hdev->idle_timeout > 0) - queue_delayed_work(hdev->workqueue, &conn->idle_work, - msecs_to_jiffies(hdev->idle_timeout)); + mod_delayed_work(hdev->workqueue, &conn->idle_work, + msecs_to_jiffies(hdev->idle_timeout)); } /* Drop all connection on the device */ From f7f2402b780738ff4bdb2f8d40a6c2136f1f92d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20S=C3=B8rensen?= Date: Tue, 16 Dec 2025 10:20:10 +0100 Subject: [PATCH 09/43] Bluetooth: hci_conn: Set link_policy on incoming ACL connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The connection link policy is only set when establishing an outgoing ACL connection causing connection idle modes not to be available on incoming connections. Move the setting of the link policy to the creation of the connection so all ACL connection will use the link policy set on the HCI device. Signed-off-by: Stefan Sørensen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 1 + net/bluetooth/hci_sync.c | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 1b98b64674854..dc085856f5e91 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1002,6 +1002,7 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, switch (type) { case ACL_LINK: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; + conn->link_policy = hdev->link_policy; conn->mtu = hdev->acl_mtu; break; case LE_LINK: diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index a9f5b1a68356e..3b620c6eed70c 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6896,8 +6896,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) conn->attempt++; - conn->link_policy = hdev->link_policy; - memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, &conn->dst); cp.pscan_rep_mode = 0x02; From fa10d5a2b8f9dc31f390c16bf611fb8171f52707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20S=C3=B8rensen?= Date: Tue, 16 Dec 2025 10:20:11 +0100 Subject: [PATCH 10/43] Bluetooth: mgmt: Add idle_timeout to configurable system parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the configurable system parameters allow controlling the SNIFF mode parameters, they do not include the idle_timeout parameter responsible for enabling SNIFF mode. Add the idle_timeout parameter to allow controlling the idle timeout of BR/EDR connections. Signed-off-by: Stefan Sørensen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt_config.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/net/bluetooth/mgmt_config.c b/net/bluetooth/mgmt_config.c index c4063d200c0a6..fdcc752c6f13f 100644 --- a/net/bluetooth/mgmt_config.c +++ b/net/bluetooth/mgmt_config.c @@ -11,6 +11,12 @@ #include "mgmt_util.h" #include "mgmt_config.h" +#define HDEV_PARAM_U32(_param_name_) \ + struct {\ + struct mgmt_tlv_hdr entry; \ + __le32 value; \ + } __packed _param_name_ + #define HDEV_PARAM_U16(_param_name_) \ struct {\ struct mgmt_tlv_hdr entry; \ @@ -29,6 +35,12 @@ cpu_to_le16(hdev->_param_name_) \ } +#define TLV_SET_U32(_param_code_, _param_name_) \ + { \ + { cpu_to_le16(_param_code_), sizeof(__u32) }, \ + cpu_to_le32(hdev->_param_name_) \ + } + #define TLV_SET_U8(_param_code_, _param_name_) \ { \ { cpu_to_le16(_param_code_), sizeof(__u8) }, \ @@ -78,6 +90,7 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, HDEV_PARAM_U16(advmon_allowlist_duration); HDEV_PARAM_U16(advmon_no_filter_duration); HDEV_PARAM_U8(enable_advmon_interleave_scan); + HDEV_PARAM_U32(idle_timeout); } __packed rp = { TLV_SET_U16(0x0000, def_page_scan_type), TLV_SET_U16(0x0001, def_page_scan_int), @@ -111,6 +124,7 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, TLV_SET_U16(0x001d, advmon_allowlist_duration), TLV_SET_U16(0x001e, advmon_no_filter_duration), TLV_SET_U8(0x001f, enable_advmon_interleave_scan), + TLV_SET_U32(0x0020, idle_timeout), }; bt_dev_dbg(hdev, "sock %p", sk); @@ -122,6 +136,7 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, } #define TO_TLV(x) ((struct mgmt_tlv *)(x)) +#define TLV_GET_LE32(tlv) le32_to_cpu(*((__le32 *)(TO_TLV(tlv)->value))) #define TLV_GET_LE16(tlv) le16_to_cpu(*((__le16 *)(TO_TLV(tlv)->value))) #define TLV_GET_U8(tlv) (*((__u8 *)(TO_TLV(tlv)->value))) @@ -191,6 +206,9 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, case 0x001f: exp_type_len = sizeof(u8); break; + case 0x0020: + exp_type_len = sizeof(u32); + break; default: exp_type_len = 0; bt_dev_warn(hdev, "unsupported parameter %u", type); @@ -314,6 +332,9 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, case 0x0001f: hdev->enable_advmon_interleave_scan = TLV_GET_U8(buffer); break; + case 0x00020: + hdev->idle_timeout = TLV_GET_LE32(buffer); + break; default: bt_dev_warn(hdev, "unsupported parameter %u", type); break; From d02dfc95df003dcbcd74f695433eae63b50b80ca Mon Sep 17 00:00:00 2001 From: Naga Bhavani Akella Date: Wed, 17 Dec 2025 16:55:23 +0530 Subject: [PATCH 11/43] Bluetooth: hci_sync: Add LE Channel Sounding HCI Command/event structures 1. Implement LE Event Mask to include events required for LE Channel Sounding 2. Enable Channel Sounding feature bit in the LE Host Supported Features command 3. Define HCI command and event structures necessary for LE Channel Sounding functionality Signed-off-by: Naga Bhavani Akella Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 323 +++++++++++++++++++++++++++++++ include/net/bluetooth/hci_core.h | 6 + net/bluetooth/hci_sync.c | 45 ++++- 3 files changed, 367 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a27cd3626b872..4a731e1bec53b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -654,6 +654,8 @@ enum { #define HCI_LE_ISO_BROADCASTER 0x40 #define HCI_LE_ISO_SYNC_RECEIVER 0x80 #define HCI_LE_LL_EXT_FEATURE 0x80 +#define HCI_LE_CS 0x40 +#define HCI_LE_CS_HOST 0x80 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 @@ -2269,6 +2271,204 @@ struct hci_cp_le_read_all_remote_features { __u8 pages; } __packed; +/* Channel Sounding Commands */ +#define HCI_OP_LE_CS_RD_LOCAL_SUPP_CAP 0x2089 +struct hci_rp_le_cs_rd_local_supp_cap { + __u8 status; + __u8 num_config_supported; + __le16 max_consecutive_procedures_supported; + __u8 num_antennas_supported; + __u8 max_antenna_paths_supported; + __u8 roles_supported; + __u8 modes_supported; + __u8 rtt_capability; + __u8 rtt_aa_only_n; + __u8 rtt_sounding_n; + __u8 rtt_random_payload_n; + __le16 nadm_sounding_capability; + __le16 nadm_random_capability; + __u8 cs_sync_phys_supported; + __le16 subfeatures_supported; + __le16 t_ip1_times_supported; + __le16 t_ip2_times_supported; + __le16 t_fcs_times_supported; + __le16 t_pm_times_supported; + __u8 t_sw_time_supported; + __u8 tx_snr_capability; +} __packed; + +#define HCI_OP_LE_CS_RD_RMT_SUPP_CAP 0x208A +struct hci_cp_le_cs_rd_local_supp_cap { + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_WR_CACHED_RMT_SUPP_CAP 0x208B +struct hci_cp_le_cs_wr_cached_rmt_supp_cap { + __le16 handle; + __u8 num_config_supported; + __le16 max_consecutive_procedures_supported; + __u8 num_antennas_supported; + __u8 max_antenna_paths_supported; + __u8 roles_supported; + __u8 modes_supported; + __u8 rtt_capability; + __u8 rtt_aa_only_n; + __u8 rtt_sounding_n; + __u8 rtt_random_payload_n; + __le16 nadm_sounding_capability; + __le16 nadm_random_capability; + __u8 cs_sync_phys_supported; + __le16 subfeatures_supported; + __le16 t_ip1_times_supported; + __le16 t_ip2_times_supported; + __le16 t_fcs_times_supported; + __le16 t_pm_times_supported; + __u8 t_sw_time_supported; + __u8 tx_snr_capability; +} __packed; + +struct hci_rp_le_cs_wr_cached_rmt_supp_cap { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_SEC_ENABLE 0x208C +struct hci_cp_le_cs_sec_enable { + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_SET_DEFAULT_SETTINGS 0x208D +struct hci_cp_le_cs_set_default_settings { + __le16 handle; + __u8 role_enable; + __u8 cs_sync_ant_sel; + __s8 max_tx_power; +} __packed; + +struct hci_rp_le_cs_set_default_settings { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_RD_RMT_FAE_TABLE 0x208E +struct hci_cp_le_cs_rd_rmt_fae_table { + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_WR_CACHED_RMT_FAE_TABLE 0x208F +struct hci_cp_le_cs_wr_rmt_cached_fae_table { + __le16 handle; + __u8 remote_fae_table[72]; +} __packed; + +struct hci_rp_le_cs_wr_rmt_cached_fae_table { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_CREATE_CONFIG 0x2090 +struct hci_cp_le_cs_create_config { + __le16 handle; + __u8 config_id; + __u8 create_context; + __u8 main_mode_type; + __u8 sub_mode_type; + __u8 min_main_mode_steps; + __u8 max_main_mode_steps; + __u8 main_mode_repetition; + __u8 mode_0_steps; + __u8 role; + __u8 rtt_type; + __u8 cs_sync_phy; + __u8 channel_map[10]; + __u8 channel_map_repetition; + __u8 channel_selection_type; + __u8 ch3c_shape; + __u8 ch3c_jump; + __u8 reserved; +} __packed; + +#define HCI_OP_LE_CS_REMOVE_CONFIG 0x2091 +struct hci_cp_le_cs_remove_config { + __le16 handle; + __u8 config_id; +} __packed; + +#define HCI_OP_LE_CS_SET_CH_CLASSIFICATION 0x2092 +struct hci_cp_le_cs_set_ch_classification { + __u8 ch_classification[10]; +} __packed; + +struct hci_rp_le_cs_set_ch_classification { + __u8 status; +} __packed; + +#define HCI_OP_LE_CS_SET_PROC_PARAM 0x2093 +struct hci_cp_le_cs_set_proc_param { + __le16 handle; + __u8 config_id; + __le16 max_procedure_len; + __le16 min_procedure_interval; + __le16 max_procedure_interval; + __le16 max_procedure_count; + __u8 min_subevent_len[3]; + __u8 max_subevent_len[3]; + __u8 tone_antenna_config_selection; + __u8 phy; + __u8 tx_power_delta; + __u8 preferred_peer_antenna; + __u8 snr_control_initiator; + __u8 snr_control_reflector; +} __packed; + +struct hci_rp_le_cs_set_proc_param { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_SET_PROC_ENABLE 0x2094 +struct hci_cp_le_cs_set_proc_enable { + __le16 handle; + __u8 config_id; + __u8 enable; +} __packed; + +#define HCI_OP_LE_CS_TEST 0x2095 +struct hci_cp_le_cs_test { + __u8 main_mode_type; + __u8 sub_mode_type; + __u8 main_mode_repetition; + __u8 mode_0_steps; + __u8 role; + __u8 rtt_type; + __u8 cs_sync_phy; + __u8 cs_sync_antenna_selection; + __u8 subevent_len[3]; + __le16 subevent_interval; + __u8 max_num_subevents; + __u8 transmit_power_level; + __u8 t_ip1_time; + __u8 t_ip2_time; + __u8 t_fcs_time; + __u8 t_pm_time; + __u8 t_sw_time; + __u8 tone_antenna_config_selection; + __u8 reserved; + __u8 snr_control_initiator; + __u8 snr_control_reflector; + __le16 drbg_nonce; + __u8 channel_map_repetition; + __le16 override_config; + __u8 override_parameters_length; + __u8 override_parameters_data[]; +} __packed; + +struct hci_rp_le_cs_test { + __u8 status; +} __packed; + +#define HCI_OP_LE_CS_TEST_END 0x2096 + /* ---- HCI Events ---- */ struct hci_ev_status { __u8 status; @@ -2960,6 +3160,129 @@ struct hci_evt_le_read_all_remote_features_complete { __u8 features[248]; } __packed; +/* Channel Sounding Events */ +#define HCI_EVT_LE_CS_READ_RMT_SUPP_CAP_COMPLETE 0x2C +struct hci_evt_le_cs_read_rmt_supp_cap_complete { + __u8 status; + __le16 handle; + __u8 num_configs_supp; + __le16 max_consec_proc_supp; + __u8 num_ant_supp; + __u8 max_ant_path_supp; + __u8 roles_supp; + __u8 modes_supp; + __u8 rtt_cap; + __u8 rtt_aa_only_n; + __u8 rtt_sounding_n; + __u8 rtt_rand_payload_n; + __le16 nadm_sounding_cap; + __le16 nadm_rand_cap; + __u8 cs_sync_phys_supp; + __le16 sub_feat_supp; + __le16 t_ip1_times_supp; + __le16 t_ip2_times_supp; + __le16 t_fcs_times_supp; + __le16 t_pm_times_supp; + __u8 t_sw_times_supp; + __u8 tx_snr_cap; +} __packed; + +#define HCI_EVT_LE_CS_READ_RMT_FAE_TABLE_COMPLETE 0x2D +struct hci_evt_le_cs_read_rmt_fae_table_complete { + __u8 status; + __le16 handle; + __u8 remote_fae_table[72]; +} __packed; + +#define HCI_EVT_LE_CS_SECURITY_ENABLE_COMPLETE 0x2E +struct hci_evt_le_cs_security_enable_complete { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_EVT_LE_CS_CONFIG_COMPLETE 0x2F +struct hci_evt_le_cs_config_complete { + __u8 status; + __le16 handle; + __u8 config_id; + __u8 action; + __u8 main_mode_type; + __u8 sub_mode_type; + __u8 min_main_mode_steps; + __u8 max_main_mode_steps; + __u8 main_mode_rep; + __u8 mode_0_steps; + __u8 role; + __u8 rtt_type; + __u8 cs_sync_phy; + __u8 channel_map[10]; + __u8 channel_map_rep; + __u8 channel_sel_type; + __u8 ch3c_shape; + __u8 ch3c_jump; + __u8 reserved; + __u8 t_ip1_time; + __u8 t_ip2_time; + __u8 t_fcs_time; + __u8 t_pm_time; +} __packed; + +#define HCI_EVT_LE_CS_PROCEDURE_ENABLE_COMPLETE 0x30 +struct hci_evt_le_cs_procedure_enable_complete { + __u8 status; + __le16 handle; + __u8 config_id; + __u8 state; + __u8 tone_ant_config_sel; + __s8 sel_tx_pwr; + __u8 sub_evt_len[3]; + __u8 sub_evts_per_evt; + __le16 sub_evt_intrvl; + __le16 evt_intrvl; + __le16 proc_intrvl; + __le16 proc_counter; + __le16 max_proc_len; +} __packed; + +#define HCI_EVT_LE_CS_SUBEVENT_RESULT 0x31 +struct hci_evt_le_cs_subevent_result { + __le16 handle; + __u8 config_id; + __le16 start_acl_conn_evt_counter; + __le16 proc_counter; + __le16 freq_comp; + __u8 ref_pwr_lvl; + __u8 proc_done_status; + __u8 subevt_done_status; + __u8 abort_reason; + __u8 num_ant_paths; + __u8 num_steps_reported; + __u8 step_mode[0]; /* depends on num_steps_reported */ + __u8 step_channel[0]; /* depends on num_steps_reported */ + __u8 step_data_length[0]; /* depends on num_steps_reported */ + __u8 step_data[0]; /* depends on num_steps_reported */ +} __packed; + +#define HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE 0x32 +struct hci_evt_le_cs_subevent_result_continue { + __le16 handle; + __u8 config_id; + __u8 proc_done_status; + __u8 subevt_done_status; + __u8 abort_reason; + __u8 num_ant_paths; + __u8 num_steps_reported; + __u8 step_mode[0]; /* depends on num_steps_reported */ + __u8 step_channel[0]; /* depends on num_steps_reported */ + __u8 step_data_length[0]; /* depends on num_steps_reported */ + __u8 step_data[0]; /* depends on num_steps_reported */ +} __packed; + +#define HCI_EVT_LE_CS_TEST_END_COMPLETE 0x33 +struct hci_evt_le_cs_test_end_complete { + __u8 status; +} __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 8aadf4cdead2b..a7d5beb01b695 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2073,6 +2073,12 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define ll_ext_feature_capable(dev) \ ((dev)->le_features[7] & HCI_LE_LL_EXT_FEATURE) +/* Channel sounding support */ +#define le_cs_capable(dev) \ + ((dev)->le_features[5] & HCI_LE_CS) +#define le_cs_host_capable(dev) \ + ((dev)->le_features[5] & HCI_LE_CS_HOST) + #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_sync.c b/net/bluetooth/hci_sync.c index 3b620c6eed70c..dd3f7bd98b1c6 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4427,6 +4427,17 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) events[4] |= 0x02; /* LE BIG Info Advertising Report */ } + if (le_cs_capable(hdev)) { + /* Channel Sounding events */ + events[5] |= 0x08; /* LE CS Read Remote Supported Cap Complete event */ + events[5] |= 0x10; /* LE CS Read Remote FAE Table Complete event */ + events[5] |= 0x20; /* LE CS Security Enable Complete event */ + events[5] |= 0x40; /* LE CS Config Complete event */ + events[5] |= 0x80; /* LE CS Procedure Enable Complete event */ + events[6] |= 0x01; /* LE CS Subevent Result event */ + events[6] |= 0x02; /* LE CS Subevent Result Continue event */ + events[6] |= 0x04; /* LE CS Test End Complete event */ + } return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), events, HCI_CMD_TIMEOUT); } @@ -4559,23 +4570,43 @@ static int hci_set_le_support_sync(struct hci_dev *hdev) } /* LE Set Host Feature */ -static int hci_le_set_host_feature_sync(struct hci_dev *hdev) +static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u8 bit, u8 value) { struct hci_cp_le_set_host_feature cp; - if (!iso_capable(hdev)) - return 0; - memset(&cp, 0, sizeof(cp)); /* Connected Isochronous Channels (Host Support) */ - cp.bit_number = 32; - cp.bit_value = iso_enabled(hdev) ? 0x01 : 0x00; + cp.bit_number = bit; + cp.bit_value = value; return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_HOST_FEATURE, 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; + + if (iso_capable(hdev)) { + /* Connected Isochronous Channels (Host Support) */ + err = hci_le_set_host_feature_sync(hdev, 32, + (iso_enabled(hdev) ? 0x01 : + 0x00)); + if (err) + return err; + } + + if (le_cs_capable(hdev)) + /* Channel Sounding (Host Support) */ + err = hci_le_set_host_feature_sync(hdev, 47, 0x01); + + return err; +} + /* LE Controller init stage 3 command sequence */ static const struct hci_init_stage le_init3[] = { /* HCI_OP_LE_SET_EVENT_MASK */ @@ -4603,7 +4634,7 @@ static const struct hci_init_stage le_init3[] = { /* HCI_OP_WRITE_LE_HOST_SUPPORTED */ HCI_INIT(hci_set_le_support_sync), /* HCI_OP_LE_SET_HOST_FEATURE */ - HCI_INIT(hci_le_set_host_feature_sync), + HCI_INIT(hci_le_set_host_features_sync), {} }; From 1d7a8fd800eb96f1175ddc3517822900c382cb80 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 17 Dec 2025 10:50:51 -0500 Subject: [PATCH 12/43] Bluetooth: L2CAP: Add support for setting BT_PHY This enables client to use setsockopt(BT_PHY) to set the connection packet type/PHY: Example setting BT_PHY_BR_1M_1SLOT: < HCI Command: Change Conne.. (0x01|0x000f) plen 4 Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation) Packet type: 0x331e 2-DH1 may not be used 3-DH1 may not be used DM1 may be used DH1 may be used 2-DH3 may not be used 3-DH3 may not be used 2-DH5 may not be used 3-DH5 may not be used > HCI Event: Command Status (0x0f) plen 4 Change Connection Packet Type (0x01|0x000f) ncmd 1 Status: Success (0x00) > HCI Event: Connection Packet Typ.. (0x1d) plen 5 Status: Success (0x00) Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation) Packet type: 0x331e 2-DH1 may not be used 3-DH1 may not be used DM1 may be used DH1 may be used 2-DH3 may not be used 3-DH3 may not be used 2-DH5 may not be used Example setting BT_PHY_LE_1M_TX and BT_PHY_LE_1M_RX: < HCI Command: LE Set PHY (0x08|0x0032) plen 7 Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation) All PHYs preference: 0x00 TX PHYs preference: 0x01 LE 1M RX PHYs preference: 0x01 LE 1M PHY options preference: Reserved (0x0000) > HCI Event: Command Status (0x0f) plen 4 LE Set PHY (0x08|0x0032) ncmd 1 Status: Success (0x00) > HCI Event: LE Meta Event (0x3e) plen 6 LE PHY Update Complete (0x0c) Status: Success (0x00) Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation) TX PHY: LE 1M (0x01) RX PHY: LE 1M (0x01) Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/bluetooth.h | 39 ++++++----- include/net/bluetooth/hci.h | 9 +++ include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/hci_sync.h | 3 + net/bluetooth/hci_conn.c | 105 ++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 26 ++++++++ net/bluetooth/hci_sync.c | 72 ++++++++++++++++++++ net/bluetooth/l2cap_sock.c | 20 +++++- 8 files changed, 259 insertions(+), 16 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index d46ed9011ee5d..89a60919050b0 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -130,21 +130,30 @@ struct bt_voice { #define BT_RCVMTU 13 #define BT_PHY 14 -#define BT_PHY_BR_1M_1SLOT 0x00000001 -#define BT_PHY_BR_1M_3SLOT 0x00000002 -#define BT_PHY_BR_1M_5SLOT 0x00000004 -#define BT_PHY_EDR_2M_1SLOT 0x00000008 -#define BT_PHY_EDR_2M_3SLOT 0x00000010 -#define BT_PHY_EDR_2M_5SLOT 0x00000020 -#define BT_PHY_EDR_3M_1SLOT 0x00000040 -#define BT_PHY_EDR_3M_3SLOT 0x00000080 -#define BT_PHY_EDR_3M_5SLOT 0x00000100 -#define BT_PHY_LE_1M_TX 0x00000200 -#define BT_PHY_LE_1M_RX 0x00000400 -#define BT_PHY_LE_2M_TX 0x00000800 -#define BT_PHY_LE_2M_RX 0x00001000 -#define BT_PHY_LE_CODED_TX 0x00002000 -#define BT_PHY_LE_CODED_RX 0x00004000 +#define BT_PHY_BR_1M_1SLOT BIT(0) +#define BT_PHY_BR_1M_3SLOT BIT(1) +#define BT_PHY_BR_1M_5SLOT BIT(2) +#define BT_PHY_EDR_2M_1SLOT BIT(3) +#define BT_PHY_EDR_2M_3SLOT BIT(4) +#define BT_PHY_EDR_2M_5SLOT BIT(5) +#define BT_PHY_EDR_3M_1SLOT BIT(6) +#define BT_PHY_EDR_3M_3SLOT BIT(7) +#define BT_PHY_EDR_3M_5SLOT BIT(8) +#define BT_PHY_LE_1M_TX BIT(9) +#define BT_PHY_LE_1M_RX BIT(10) +#define BT_PHY_LE_2M_TX BIT(11) +#define BT_PHY_LE_2M_RX BIT(12) +#define BT_PHY_LE_CODED_TX BIT(13) +#define BT_PHY_LE_CODED_RX BIT(14) + +#define BT_PHY_BREDR_MASK (BT_PHY_BR_1M_1SLOT | BT_PHY_BR_1M_3SLOT | \ + BT_PHY_BR_1M_5SLOT | BT_PHY_EDR_2M_1SLOT | \ + BT_PHY_EDR_2M_3SLOT | BT_PHY_EDR_2M_5SLOT | \ + BT_PHY_EDR_3M_1SLOT | BT_PHY_EDR_3M_3SLOT | \ + BT_PHY_EDR_3M_5SLOT) +#define BT_PHY_LE_MASK (BT_PHY_LE_1M_TX | BT_PHY_LE_1M_RX | \ + BT_PHY_LE_2M_TX | BT_PHY_LE_2M_RX | \ + BT_PHY_LE_CODED_TX | BT_PHY_LE_CODED_RX) #define BT_MODE 15 diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4a731e1bec53b..db76c2d1eeaa1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1885,6 +1885,15 @@ struct hci_cp_le_set_default_phy { #define HCI_LE_SET_PHY_2M 0x02 #define HCI_LE_SET_PHY_CODED 0x04 +#define HCI_OP_LE_SET_PHY 0x2032 +struct hci_cp_le_set_phy { + __le16 handle; + __u8 all_phys; + __u8 tx_phys; + __u8 rx_phys; + __le16 phy_opts; +} __packed; + #define HCI_OP_LE_SET_EXT_SCAN_PARAMS 0x2041 struct hci_cp_le_set_ext_scan_params { __u8 own_addr_type; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a7d5beb01b695..a7bffb908c1ec 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2342,6 +2342,7 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void *hci_recv_event_data(struct hci_dev *hdev, __u8 event); u32 hci_conn_get_phy(struct hci_conn *conn); +int hci_conn_set_phy(struct hci_conn *conn, u32 phys); /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h index 56076bbc981d9..73e494b2591de 100644 --- a/include/net/bluetooth/hci_sync.h +++ b/include/net/bluetooth/hci_sync.h @@ -191,3 +191,6 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn); int hci_past_sync(struct hci_conn *conn, struct hci_conn *le); int hci_le_read_remote_features(struct hci_conn *conn); + +int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type); +int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index dc085856f5e91..1a4b6badf2b35 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2958,6 +2958,111 @@ u32 hci_conn_get_phy(struct hci_conn *conn) return phys; } +static u16 bt_phy_pkt_type(struct hci_conn *conn, u32 phys) +{ + u16 pkt_type = conn->pkt_type; + + if (phys & BT_PHY_BR_1M_3SLOT) + pkt_type |= HCI_DM3 | HCI_DH3; + else + pkt_type &= ~(HCI_DM3 | HCI_DH3); + + if (phys & BT_PHY_BR_1M_5SLOT) + pkt_type |= HCI_DM5 | HCI_DH5; + else + pkt_type &= ~(HCI_DM5 | HCI_DH5); + + if (phys & BT_PHY_EDR_2M_1SLOT) + pkt_type &= ~HCI_2DH1; + else + pkt_type |= HCI_2DH1; + + if (phys & BT_PHY_EDR_2M_3SLOT) + pkt_type &= ~HCI_2DH3; + else + pkt_type |= HCI_2DH3; + + if (phys & BT_PHY_EDR_2M_5SLOT) + pkt_type &= ~HCI_2DH5; + else + pkt_type |= HCI_2DH5; + + if (phys & BT_PHY_EDR_3M_1SLOT) + pkt_type &= ~HCI_3DH1; + else + pkt_type |= HCI_3DH1; + + if (phys & BT_PHY_EDR_3M_3SLOT) + pkt_type &= ~HCI_3DH3; + else + pkt_type |= HCI_3DH3; + + if (phys & BT_PHY_EDR_3M_5SLOT) + pkt_type &= ~HCI_3DH5; + else + pkt_type |= HCI_3DH5; + + return pkt_type; +} + +static int bt_phy_le_phy(u32 phys, u8 *tx_phys, u8 *rx_phys) +{ + if (!tx_phys || !rx_phys) + return -EINVAL; + + *tx_phys = 0; + *rx_phys = 0; + + if (phys & BT_PHY_LE_1M_TX) + *tx_phys |= HCI_LE_SET_PHY_1M; + + if (phys & BT_PHY_LE_1M_RX) + *rx_phys |= HCI_LE_SET_PHY_1M; + + if (phys & BT_PHY_LE_2M_TX) + *tx_phys |= HCI_LE_SET_PHY_2M; + + if (phys & BT_PHY_LE_2M_RX) + *rx_phys |= HCI_LE_SET_PHY_2M; + + if (phys & BT_PHY_LE_CODED_TX) + *tx_phys |= HCI_LE_SET_PHY_CODED; + + if (phys & BT_PHY_LE_CODED_RX) + *rx_phys |= HCI_LE_SET_PHY_CODED; + + return 0; +} + +int hci_conn_set_phy(struct hci_conn *conn, u32 phys) +{ + u8 tx_phys, rx_phys; + + switch (conn->type) { + case SCO_LINK: + case ESCO_LINK: + return -EINVAL; + case ACL_LINK: + /* Only allow setting BR/EDR PHYs if link type is ACL */ + if (phys & ~BT_PHY_BREDR_MASK) + return -EINVAL; + + return hci_acl_change_pkt_type(conn, + bt_phy_pkt_type(conn, phys)); + case LE_LINK: + /* Only allow setting LE PHYs if link type is LE */ + if (phys & ~BT_PHY_LE_MASK) + return -EINVAL; + + if (bt_phy_le_phy(phys, &tx_phys, &rx_phys)) + return -EINVAL; + + return hci_le_set_phy(conn, tx_phys, rx_phys); + default: + return -EINVAL; + } +} + static int abort_conn_sync(struct hci_dev *hdev, void *data) { struct hci_conn *conn = data; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 58075bf720554..467710a42d453 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2869,6 +2869,31 @@ static void hci_cs_le_ext_create_conn(struct hci_dev *hdev, u8 status) hci_dev_unlock(hdev); } +static void hci_cs_le_set_phy(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_le_set_phy *cp; + struct hci_conn *conn; + + bt_dev_dbg(hdev, "status 0x%2.2x", status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PHY); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); + if (conn) { + conn->le_tx_def_phys = cp->tx_phys; + conn->le_rx_def_phys = cp->rx_phys; + } + + hci_dev_unlock(hdev); +} + static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status) { struct hci_cp_le_read_remote_features *cp; @@ -4359,6 +4384,7 @@ static const struct hci_cs { HCI_CS(HCI_OP_LE_CREATE_CONN, hci_cs_le_create_conn), HCI_CS(HCI_OP_LE_READ_REMOTE_FEATURES, hci_cs_le_read_remote_features), HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc), + HCI_CS(HCI_OP_LE_SET_PHY, hci_cs_le_set_phy), HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn), HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis), HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big), diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index dd3f7bd98b1c6..ab0b68faa61c6 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7447,3 +7447,75 @@ int hci_le_read_remote_features(struct hci_conn *conn) return err; } + +static void pkt_type_changed(struct hci_dev *hdev, void *data, int err) +{ + struct hci_cp_change_conn_ptype *cp = data; + + bt_dev_dbg(hdev, "err %d", err); + + kfree(cp); +} + +static int hci_change_conn_ptype_sync(struct hci_dev *hdev, void *data) +{ + struct hci_cp_change_conn_ptype *cp = data; + + return __hci_cmd_sync_status_sk(hdev, HCI_OP_CHANGE_CONN_PTYPE, + sizeof(*cp), cp, + HCI_EV_PKT_TYPE_CHANGE, + HCI_CMD_TIMEOUT, NULL); +} + +int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_change_conn_ptype *cp; + + cp = kmalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + cp->handle = cpu_to_le16(conn->handle); + cp->pkt_type = cpu_to_le16(pkt_type); + + return hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp, + pkt_type_changed); +} + +static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err) +{ + struct hci_cp_le_set_phy *cp = data; + + bt_dev_dbg(hdev, "err %d", err); + + kfree(cp); +} + +static int hci_le_set_phy_sync(struct hci_dev *hdev, void *data) +{ + struct hci_cp_le_set_phy *cp = data; + + return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_SET_PHY, + sizeof(*cp), cp, + HCI_EV_LE_PHY_UPDATE_COMPLETE, + HCI_CMD_TIMEOUT, NULL); +} + +int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_set_phy *cp; + + cp = kmalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + memset(cp, 0, sizeof(*cp)); + cp->handle = cpu_to_le16(conn->handle); + cp->tx_phys = tx_phys; + cp->rx_phys = rx_phys; + + return hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp, + le_phy_update_complete); +} diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 9ee189c815d49..3ba3ce7eaa98a 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -885,7 +885,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, struct bt_power pwr; struct l2cap_conn *conn; int err = 0; - u32 opt; + u32 opt, phys; u16 mtu; u8 mode; @@ -1059,6 +1059,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; + case BT_PHY: + if (sk->sk_state != BT_CONNECTED) { + err = -ENOTCONN; + break; + } + + err = copy_safe_from_sockptr(&phys, sizeof(phys), optval, + optlen); + if (err) + break; + + if (!chan->conn) + break; + + conn = chan->conn; + err = hci_conn_set_phy(conn->hcon, phys); + break; + case BT_MODE: if (!enable_ecred) { err = -ENOPROTOOPT; From d6af4afb70c9b73ce344ec9b2414dab2c4dcd61f Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Tue, 6 Jan 2026 12:35:02 +0800 Subject: [PATCH 13/43] Bluetooth: btqca: move WCN7850 workaround to the caller WCN7850 will first attempt to use ELF_TYPE_PATCH, and if that fails, it will fall back to TLV_TYPE_PATCH. To code uniformity, move WCN7850 workaround to the caller. Reviewed-by: Dmitry Baryshkov Signed-off-by: Shuai Zhang Acked-by: Bartosz Golaszewski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btqca.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 7c958d6065bec..0af3e2109f310 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -581,28 +581,11 @@ static int qca_download_firmware(struct hci_dev *hdev, ret = request_firmware(&fw, config->fwname, &hdev->dev); if (ret) { - /* For WCN6750, if mbn file is not present then check for - * tlv file. - */ - if (soc_type == QCA_WCN6750 && config->type == ELF_TYPE_PATCH) { - bt_dev_dbg(hdev, "QCA Failed to request file: %s (%d)", - config->fwname, ret); - config->type = TLV_TYPE_PATCH; - snprintf(config->fwname, sizeof(config->fwname), - "qca/msbtfw%02x.tlv", rom_ver); - bt_dev_info(hdev, "QCA Downloading %s", config->fwname); - ret = request_firmware(&fw, config->fwname, &hdev->dev); - if (ret) { - bt_dev_err(hdev, "QCA Failed to request file: %s (%d)", - config->fwname, ret); - return ret; - } - } /* If the board-specific file is missing, try loading the default * one, unless that was attempted already. */ - else if (config->type == TLV_TYPE_NVM && - qca_get_alt_nvm_file(config->fwname, sizeof(config->fwname))) { + if (config->type == TLV_TYPE_NVM && + qca_get_alt_nvm_file(config->fwname, sizeof(config->fwname))) { bt_dev_info(hdev, "QCA Downloading %s", config->fwname); ret = request_firmware(&fw, config->fwname, &hdev->dev); if (ret) { @@ -861,8 +844,22 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, } err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + /* For WCN6750, if mbn file is not present then check for + * tlv file. + */ + if (err < 0 && soc_type == QCA_WCN6750) { + bt_dev_dbg(hdev, "QCA Failed to request file: %s (%d)", + config.fwname, err); + config.type = TLV_TYPE_PATCH; + snprintf(config.fwname, sizeof(config.fwname), + "qca/msbtfw%02x.tlv", rom_ver); + bt_dev_info(hdev, "QCA Downloading %s", config.fwname); + err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + } + if (err < 0) { - bt_dev_err(hdev, "QCA Failed to download patch (%d)", err); + bt_dev_err(hdev, "QCA Failed to request file: %s (%d)", + config.fwname, err); return err; } From 030d2c0e9c1d68e67f91c08704482ad9881583eb Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Tue, 6 Jan 2026 12:35:03 +0800 Subject: [PATCH 14/43] Bluetooth: btqca: Add WCN6855 firmware priority selection feature Historically, WCN685x and QCA2066 shared the same firmware files. Now, changes are planned for the firmware that will make it incompatible with QCA2066, so a new firmware name is required for WCN685x. Test Steps: - Boot device - Check the BTFW loading status via dmesg Sanity pass and Test Log: QCA Downloading qca/wcnhpbftfw21.tlv Direct firmware load for qca/wcnhpbftfw21.tlv failed with error -2 QCA Downloading qca/hpbftfw21.tlv Reviewed-by: Dmitry Baryshkov Signed-off-by: Shuai Zhang Acked-by: Bartosz Golaszewski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btqca.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 0af3e2109f310..74f820e89655e 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -830,8 +830,12 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, "qca/msbtfw%02x.mbn", rom_ver); break; case QCA_WCN6855: + /* Due to historical reasons, WCN685x chip has been using firmware + * without the "wcn" prefix. The mapping between the chip and its + * corresponding firmware has now been corrected. + */ snprintf(config.fwname, sizeof(config.fwname), - "qca/hpbtfw%02x.tlv", rom_ver); + "qca/wcnhpbtfw%02x.tlv", rom_ver); break; case QCA_WCN7850: snprintf(config.fwname, sizeof(config.fwname), @@ -855,6 +859,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, "qca/msbtfw%02x.tlv", rom_ver); bt_dev_info(hdev, "QCA Downloading %s", config.fwname); err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + } else if (err < 0 && !rampatch_name && soc_type == QCA_WCN6855) { + snprintf(config.fwname, sizeof(config.fwname), + "qca/hpbtfw%02x.tlv", rom_ver); + err = qca_download_firmware(hdev, &config, soc_type, rom_ver); } if (err < 0) { @@ -920,7 +928,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, case QCA_WCN6855: qca_read_fw_board_id(hdev, &boardid); qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname), - "hpnv", soc_type, ver, rom_ver, boardid); + "wcnhpnv", soc_type, ver, rom_ver, boardid); break; case QCA_WCN7850: qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname), @@ -933,8 +941,15 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, } err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + if (err < 0 && !firmware_name && soc_type == QCA_WCN6855) { + qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname), + "hpnv", soc_type, ver, rom_ver, boardid); + err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + } + if (err < 0) { - bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err); + bt_dev_err(hdev, "QCA Failed to request file: %s (%d)", + config.fwname, err); return err; } From 6538549c619d915e35142f49e3bb1e2dfc7a7f3c Mon Sep 17 00:00:00 2001 From: Techie Ernie Date: Wed, 24 Dec 2025 11:31:29 +0800 Subject: [PATCH 15/43] Bluetooth: btusb: Add USB ID 0489:e112 for Realtek 8851BE Add USB ID 0489:e112 for the Realtek 8851BE Bluetooth adapter. Without this entry, the device is not handled correctly by btusb and Bluetooth fails to initialise. Adding the ID enables proper Realtek initialization for Bluetooth to work on various motherboards using this Bluetooth adapter. The device identifies as: Bus 001 Device XXX: ID 0489:e112 Foxconn / Hon Hai Bluetooth Radio Tested on Realtek 8851BE. Bluetooth works after this change is made. Signed-off-by: Techie Ernie 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 646de80c7e7be..de9e484efef71 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -521,6 +521,8 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x0bda, 0xb850), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3600), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3601), .driver_info = BTUSB_REALTEK }, + { USB_DEVICE(0x0489, 0xe112), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek 8851BU Bluetooth devices */ { USB_DEVICE(0x3625, 0x010b), .driver_info = BTUSB_REALTEK | From d4f7cb6e2df7b63af740d5848c74ad1d9d157033 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Fri, 19 Dec 2025 10:43:09 +0800 Subject: [PATCH 16/43] Bluetooth: hci_sync: enable PA Sync Lost event Enable the PA Sync Lost event mask to ensure PA sync loss is properly reported and handled. Fixes: 485e0626e587 ("Bluetooth: hci_event: Fix not handling PA Sync Lost event") Signed-off-by: Yang Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index ab0b68faa61c6..0e6660ddc00a7 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4420,6 +4420,7 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) if (bis_capable(hdev)) { events[1] |= 0x20; /* LE PA Report */ events[1] |= 0x40; /* LE PA Sync Established */ + events[1] |= 0x80; /* LE PA Sync Lost */ events[3] |= 0x04; /* LE Create BIG Complete */ events[3] |= 0x08; /* LE Terminate BIG Complete */ events[3] |= 0x10; /* LE BIG Sync Established */ From bdb9aba465a85122bcba01a6154e237b9843e25b Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 22 Dec 2025 14:36:45 -0500 Subject: [PATCH 17/43] Bluetooth: Fix using PHYs bitfields as PHY value This renames the PHY fields in bt_iso_io_qos to PHYs (plural) since it represents a bitfield where multiple PHYs can be set and make the same change also to HCI_OP_LE_SET_CIG_PARAMS since both c_phy and p_phy fields are bitfields. This also fixes the assumption that hci_evt_le_cis_established PHYs fields are compatible with bt_iso_io_qos, they are not, the fields in hci_evt_le_cis_established represent just a single PHY value so they need to be converted to bitfield when set in bt_iso_io_qos. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/bluetooth.h | 8 +++---- include/net/bluetooth/hci.h | 6 +++--- net/bluetooth/hci_conn.c | 35 ++++++++++++++++--------------- net/bluetooth/hci_event.c | 23 ++++++++++++++++---- net/bluetooth/hci_sync.c | 6 +++--- net/bluetooth/iso.c | 10 ++++----- 6 files changed, 52 insertions(+), 36 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 89a60919050b0..69eed69f7f265 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -182,7 +182,7 @@ struct bt_iso_io_qos { __u32 interval; __u16 latency; __u16 sdu; - __u8 phy; + __u8 phys; __u8 rtn; }; @@ -221,9 +221,9 @@ struct bt_iso_qos { }; }; -#define BT_ISO_PHY_1M 0x01 -#define BT_ISO_PHY_2M 0x02 -#define BT_ISO_PHY_CODED 0x04 +#define BT_ISO_PHY_1M BIT(0) +#define BT_ISO_PHY_2M BIT(1) +#define BT_ISO_PHY_CODED BIT(2) #define BT_ISO_PHY_ANY (BT_ISO_PHY_1M | BT_ISO_PHY_2M | \ BT_ISO_PHY_CODED) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index db76c2d1eeaa1..89ad9470fa71b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1891,7 +1891,7 @@ struct hci_cp_le_set_phy { __u8 all_phys; __u8 tx_phys; __u8 rx_phys; - __le16 phy_opts; + __le16 phy_opts; } __packed; #define HCI_OP_LE_SET_EXT_SCAN_PARAMS 0x2041 @@ -2147,8 +2147,8 @@ struct hci_cis_params { __u8 cis_id; __le16 c_sdu; __le16 p_sdu; - __u8 c_phy; - __u8 p_phy; + __u8 c_phys; + __u8 p_phys; __u8 c_rtn; __u8 p_rtn; } __packed; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 1a4b6badf2b35..0795818963a52 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1825,7 +1825,7 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) cp.bis.sdu = cpu_to_le16(qos->bcast.out.sdu); cp.bis.latency = cpu_to_le16(qos->bcast.out.latency); cp.bis.rtn = qos->bcast.out.rtn; - cp.bis.phy = qos->bcast.out.phy; + cp.bis.phy = qos->bcast.out.phys; cp.bis.packing = qos->bcast.packing; cp.bis.framing = qos->bcast.framing; cp.bis.encryption = qos->bcast.encryption; @@ -1875,10 +1875,10 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data) cis->cis_id = cis_id; cis->c_sdu = cpu_to_le16(conn->iso_qos.ucast.out.sdu); cis->p_sdu = cpu_to_le16(conn->iso_qos.ucast.in.sdu); - cis->c_phy = qos->ucast.out.phy ? qos->ucast.out.phy : - qos->ucast.in.phy; - cis->p_phy = qos->ucast.in.phy ? qos->ucast.in.phy : - qos->ucast.out.phy; + cis->c_phys = qos->ucast.out.phys ? qos->ucast.out.phys : + qos->ucast.in.phys; + cis->p_phys = qos->ucast.in.phys ? qos->ucast.in.phys : + qos->ucast.out.phys; cis->c_rtn = qos->ucast.out.rtn; cis->p_rtn = qos->ucast.in.rtn; } @@ -1980,8 +1980,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, return cis; /* Update LINK PHYs according to QoS preference */ - cis->le_tx_phy = qos->ucast.out.phy; - cis->le_rx_phy = qos->ucast.in.phy; + cis->le_tx_phy = qos->ucast.out.phys; + cis->le_rx_phy = qos->ucast.in.phys; /* If output interval is not set use the input interval as it cannot be * 0x000000. @@ -2096,15 +2096,15 @@ int hci_le_create_cis_pending(struct hci_dev *hdev) } static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn, - struct bt_iso_io_qos *qos, __u8 phy) + struct bt_iso_io_qos *qos, __u8 phys) { /* Only set MTU if PHY is enabled */ - if (!qos->sdu && qos->phy) + if (!qos->sdu && qos->phys) qos->sdu = conn->mtu; /* Use the same PHY as ACL if set to any */ - if (qos->phy == BT_ISO_PHY_ANY) - qos->phy = phy; + if (qos->phys == BT_ISO_PHY_ANY) + qos->phys = phys; /* Use LE ACL connection interval if not set */ if (!qos->interval) @@ -2124,7 +2124,7 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; - if (qos->bcast.out.phy == 0x02) + if (qos->bcast.out.phys == BIT(1)) flags |= MGMT_ADV_FLAG_SEC_2M; /* Align intervals */ @@ -2233,8 +2233,7 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, return conn; /* Update LINK PHYs according to QoS preference */ - conn->le_tx_phy = qos->bcast.out.phy; - conn->le_tx_phy = qos->bcast.out.phy; + conn->le_tx_def_phys = qos->bcast.out.phys; /* Add Basic Announcement into Peridic Adv Data if BASE is set */ if (base_len && base) { @@ -2243,7 +2242,7 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, } hci_iso_qos_setup(hdev, conn, &qos->bcast.out, - conn->le_tx_phy ? conn->le_tx_phy : + conn->le_tx_def_phys ? conn->le_tx_def_phys : hdev->le_tx_def_phys); conn->iso_qos = *qos; @@ -2363,9 +2362,11 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, return le; hci_iso_qos_setup(hdev, le, &qos->ucast.out, - le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys); + le->le_tx_def_phys ? le->le_tx_def_phys : + hdev->le_tx_def_phys); hci_iso_qos_setup(hdev, le, &qos->ucast.in, - le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys); + le->le_rx_def_phys ? le->le_rx_def_phys : + hdev->le_rx_def_phys); cis = hci_bind_cis(hdev, dst, dst_type, qos, timeout); if (IS_ERR(cis)) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 467710a42d453..286529d2e554a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -6867,6 +6867,21 @@ static void hci_le_phy_update_evt(struct hci_dev *hdev, void *data, hci_dev_unlock(hdev); } +/* Convert LE PHY to QoS PHYs */ +static u8 le_phy_qos(u8 phy) +{ + switch (phy) { + case 0x01: + return HCI_LE_SET_PHY_1M; + case 0x02: + return HCI_LE_SET_PHY_2M; + case 0x03: + return HCI_LE_SET_PHY_CODED; + } + + return 0; +} + static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) { @@ -6928,8 +6943,8 @@ static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data, 1000); qos->ucast.in.sdu = ev->c_bn ? le16_to_cpu(ev->c_mtu) : 0; qos->ucast.out.sdu = ev->p_bn ? le16_to_cpu(ev->p_mtu) : 0; - qos->ucast.in.phy = ev->c_phy; - qos->ucast.out.phy = ev->p_phy; + qos->ucast.in.phys = le_phy_qos(ev->c_phy); + qos->ucast.out.phys = le_phy_qos(ev->p_phy); break; case HCI_ROLE_MASTER: qos->ucast.in.interval = p_sdu_interval; @@ -6943,8 +6958,8 @@ static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data, 1000); qos->ucast.out.sdu = ev->c_bn ? le16_to_cpu(ev->c_mtu) : 0; qos->ucast.in.sdu = ev->p_bn ? le16_to_cpu(ev->p_mtu) : 0; - qos->ucast.out.phy = ev->c_phy; - qos->ucast.in.phy = ev->p_phy; + qos->ucast.out.phys = le_phy_qos(ev->c_phy); + qos->ucast.in.phys = le_phy_qos(ev->p_phy); break; } diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 0e6660ddc00a7..f04a90bce4a94 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -2948,8 +2948,8 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type, if (conn) { struct bt_iso_qos *qos = &conn->iso_qos; - if (qos->bcast.in.phy & BT_ISO_PHY_1M || - qos->bcast.in.phy & BT_ISO_PHY_2M) { + if (qos->bcast.in.phys & BT_ISO_PHY_1M || + qos->bcast.in.phys & BT_ISO_PHY_2M) { cp->scanning_phys |= LE_SCAN_PHY_1M; hci_le_scan_phy_params(phy, type, interval, @@ -2958,7 +2958,7 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type, phy++; } - if (qos->bcast.in.phy & BT_ISO_PHY_CODED) { + if (qos->bcast.in.phys & BT_ISO_PHY_CODED) { cp->scanning_phys |= LE_SCAN_PHY_CODED; hci_le_scan_phy_params(phy, type, interval * 3, diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index e36d24a9098b9..1459ab161fd2a 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -361,7 +361,7 @@ static int iso_connect_bis(struct sock *sk) } /* Fail if out PHYs are marked as disabled */ - if (!iso_pi(sk)->qos.bcast.out.phy) { + if (!iso_pi(sk)->qos.bcast.out.phys) { err = -EINVAL; goto unlock; } @@ -458,7 +458,7 @@ static int iso_connect_cis(struct sock *sk) } /* Fail if either PHYs are marked as disabled */ - if (!iso_pi(sk)->qos.ucast.in.phy && !iso_pi(sk)->qos.ucast.out.phy) { + if (!iso_pi(sk)->qos.ucast.in.phys && !iso_pi(sk)->qos.ucast.out.phys) { err = -EINVAL; goto unlock; } @@ -894,7 +894,7 @@ static struct proto iso_proto = { .interval = 10000u, \ .latency = 10u, \ .sdu = 40u, \ - .phy = BT_ISO_PHY_2M, \ + .phys = BT_ISO_PHY_2M, \ .rtn = 2u, \ } @@ -1661,7 +1661,7 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg, static bool check_io_qos(struct bt_iso_io_qos *qos) { /* If no PHY is enable SDU must be 0 */ - if (!qos->phy && qos->sdu) + if (!qos->phys && qos->sdu) return false; if (qos->interval && (qos->interval < 0xff || qos->interval > 0xfffff)) @@ -1670,7 +1670,7 @@ static bool check_io_qos(struct bt_iso_io_qos *qos) if (qos->latency && (qos->latency < 0x05 || qos->latency > 0xfa0)) return false; - if (qos->phy > BT_ISO_PHY_ANY) + if (qos->phys > BT_ISO_PHY_ANY) return false; return true; From b2b584d1317a275c9526200d6720349ff552b5ef Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:48:58 +0100 Subject: [PATCH 18/43] dt-bindings: bluetooth: qcom,qca2066-bt: Split to separate schema One big Qualcomm Bluetooth schema is hardly manageable: it lists all possible properties (19 supplies). Split common part and qcom,qca2066-bt to separate bindings, so each schema will be easier to read/maintain and list only relevant properties. The existing bindings do not mention interrupts, but am335x-sancloud-bbe-extended-wifi.dts already defines such. This issue is not being fixed here. Existing binding also did not mention any supplies (which do exist as confirmed in datasheet) and Linux driver does not ask for any, thus keep this state unchanged. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/qcom,bluetooth-common.yaml | 25 ++++++++++ .../net/bluetooth/qcom,qca2066-bt.yaml | 49 +++++++++++++++++++ .../net/bluetooth/qualcomm-bluetooth.yaml | 25 +--------- MAINTAINERS | 1 + 4 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml create mode 100644 Documentation/devicetree/bindings/net/bluetooth/qcom,qca2066-bt.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml new file mode 100644 index 0000000000000..c8e9c55c1afb4 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,bluetooth-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Bluetooth Common Properties + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + firmware-name: + minItems: 1 + items: + - description: specify the name of nvm firmware to load + - description: specify the name of rampatch firmware to load + + qcom,local-bd-address-broken: + type: boolean + description: + boot firmware is incorrectly passing the address in big-endian order + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,qca2066-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca2066-bt.yaml new file mode 100644 index 0000000000000..d4f167c9b7e14 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca2066-bt.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,qca2066-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCA2006 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,qca2066-bt + - qcom,qca6174-bt + + clocks: + items: + - description: External low-power 32.768 kHz clock input + + enable-gpios: + maxItems: 1 + +required: + - compatible + - clocks + - enable-gpios + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + serial { + bluetooth { + compatible = "qcom,qca6174-bt"; + clocks = <&divclk4>; + enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>; + firmware-name = "nvm_00440302.bin"; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index 6353a336f382e..85d10d94700f4 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -16,8 +16,6 @@ description: properties: compatible: enum: - - qcom,qca2066-bt - - qcom,qca6174-bt - qcom,qca9377-bt - qcom,wcn3950-bt - qcom,wcn3988-bt @@ -122,17 +120,6 @@ additionalProperties: false allOf: - $ref: bluetooth-controller.yaml# - $ref: /schemas/serial/serial-peripheral-props.yaml# - - if: - properties: - compatible: - contains: - enum: - - qcom,qca2066-bt - - qcom,qca6174-bt - then: - required: - - enable-gpios - - clocks - if: properties: @@ -211,17 +198,6 @@ allOf: - vddrfa1p7-supply examples: - - | - #include - serial { - - bluetooth { - compatible = "qcom,qca6174-bt"; - enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>; - clocks = <&divclk4>; - firmware-name = "nvm_00440302.bin"; - }; - }; - | serial { @@ -236,6 +212,7 @@ examples: }; }; - | + #include serial { bluetooth { diff --git a/MAINTAINERS b/MAINTAINERS index 4b2603ed45f4a..4c01e0eeba7de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21074,6 +21074,7 @@ QUALCOMM BLUETOOTH DRIVER M: Bartosz Golaszewski L: linux-arm-msm@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/net/bluetooth/qcom,* F: drivers/bluetooth/btqca.[ch] F: drivers/bluetooth/btqcomsmd.c F: drivers/bluetooth/hci_qca.c From fecb6f46ab6faf0d4db6a8b45a71eb1ee6a7ce9a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:48:59 +0100 Subject: [PATCH 19/43] dt-bindings: bluetooth: qcom,qca9377-bt: Split to separate schema One big Qualcomm Bluetooth schema is hardly manageable: it lists all possible properties (19 supplies). Split qcom,qca9377-bt to separate bindings, so device schema will be easier to read/maintain and list only relevant properties. Existing binding has incomplete and incorrect list of supplies (e.g. there is no VDD_XO) and Linux driver does not ask for any, thus keep this state unchanged. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/qcom,qca9377-bt.yaml | 58 +++++++++++++++++++ .../net/bluetooth/qualcomm-bluetooth.yaml | 1 - 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/qcom,qca9377-bt.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,qca9377-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca9377-bt.yaml new file mode 100644 index 0000000000000..3fe9476c1d74c --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca9377-bt.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,qca9377-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCA9377 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,qca9377-bt + + clocks: + items: + - description: External low-power 32.768 kHz clock input + + enable-gpios: + maxItems: 1 + + vddio-supply: + description: VDD_IO supply regulator handle + + vddxo-supply: + description: VDD_XO supply regulator handle + +required: + - compatible + - clocks + - enable-gpios + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + serial { + bluetooth { + compatible = "qcom,qca9377-bt"; + clocks = <&rk809 1>; + enable-gpios = <&gpio3 5 GPIO_ACTIVE_HIGH>; + max-speed = <2000000>; + pinctrl-names = "default"; + pinctrl-0 = <&bt_enable>; + vddio-supply = <&vcc_1v8>; + vddxo-supply = <&vcc3v3_sys>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index 85d10d94700f4..dba867ef3d065 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -16,7 +16,6 @@ description: properties: compatible: enum: - - qcom,qca9377-bt - qcom,wcn3950-bt - qcom,wcn3988-bt - qcom,wcn3990-bt From e41a69e668ef74fa4e077f3b82b3f58fc7f58056 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:00 +0100 Subject: [PATCH 20/43] dt-bindings: bluetooth: qcom,qca6390-bt: Split to separate schema One big Qualcomm Bluetooth schema is hardly manageable: it lists all possible properties (19 supplies). Split qcom,qca6390-bt to separate bindings, so device schema will be easier to read/maintain and list only relevant properties. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/qcom,qca6390-bt.yaml | 64 +++++++++++++++++++ .../net/bluetooth/qualcomm-bluetooth.yaml | 15 ----- 2 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/qcom,qca6390-bt.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,qca6390-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca6390-bt.yaml new file mode 100644 index 0000000000000..cffbc9e61cd6c --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca6390-bt.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,qca6390-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCA6390 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,qca6390-bt + + vddaon-supply: + description: VDD_AON supply regulator handle + + vddbtcmx-supply: + description: VDD_BT_CMX supply regulator handle + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p7-supply: + description: VDD_RFA_1P7 supply regulator handle + + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + +required: + - compatible + - vddaon-supply + - vddbtcmx-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p7-supply + - vddrfacmn-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,qca6390-bt"; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index dba867ef3d065..ac58d6598091a 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -21,7 +21,6 @@ properties: - qcom,wcn3990-bt - qcom,wcn3991-bt - qcom,wcn3998-bt - - qcom,qca6390-bt - qcom,wcn6750-bt - qcom,wcn6855-bt - qcom,wcn7850-bt @@ -181,20 +180,6 @@ allOf: - vddrfa0p8-supply - vddrfa1p2-supply - vddrfa1p8-supply - - if: - properties: - compatible: - contains: - enum: - - qcom,qca6390-bt - then: - required: - - vddrfacmn-supply - - vddaon-supply - - vddbtcmx-supply - - vddrfa0p8-supply - - vddrfa1p2-supply - - vddrfa1p7-supply examples: - | From 439375d993c70cfd9ba2d1bfd04bdfb195f48438 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:01 +0100 Subject: [PATCH 21/43] dt-bindings: bluetooth: qcom,wcn3950-bt: Split to separate schema One big Qualcomm Bluetooth schema is hardly manageable: it lists all possible properties (19 supplies). Split qcom,wcn3950-bt to separate bindings, so device schema will be easier to read/maintain and list only relevant properties. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/qcom,wcn3950-bt.yaml | 67 +++++++++++++++++++ .../net/bluetooth/qualcomm-bluetooth.yaml | 4 -- 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3950-bt.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3950-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3950-bt.yaml new file mode 100644 index 0000000000000..83382f3c90498 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3950-bt.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn3950-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN3950/WCN3988 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn3950-bt + - qcom,wcn3988-bt + + enable-gpios: + maxItems: 1 + + swctrl-gpios: + maxItems: 1 + description: gpio specifier is used to find status + of clock supply to SoC + + vddch0-supply: + description: VDD_CH0 supply regulator handle + + vddio-supply: + description: VDD_IO supply regulator handle + + vddrf-supply: + description: VDD_RF supply regulator handle + + vddxo-supply: + description: VDD_XO supply regulator handle + +required: + - compatible + - vddch0-supply + - vddio-supply + - vddrf-supply + - vddxo-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + serial { + bluetooth { + compatible = "qcom,wcn3950-bt"; + enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>; + max-speed = <3200000>; + vddch0-supply = <&pm4125_l22>; + vddio-supply = <&pm4125_l15>; + vddrf-supply = <&pm4125_l10>; + vddxo-supply = <&pm4125_l13>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index ac58d6598091a..82cce508e2955 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -16,8 +16,6 @@ description: properties: compatible: enum: - - qcom,wcn3950-bt - - qcom,wcn3988-bt - qcom,wcn3990-bt - qcom,wcn3991-bt - qcom,wcn3998-bt @@ -124,8 +122,6 @@ allOf: compatible: contains: enum: - - qcom,wcn3950-bt - - qcom,wcn3988-bt - qcom,wcn3990-bt - qcom,wcn3991-bt - qcom,wcn3998-bt From 57507201df35363a0677863a897d58103d7fc49d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:02 +0100 Subject: [PATCH 22/43] dt-bindings: bluetooth: qcom,wcn3990-bt: Split to separate schema One big Qualcomm Bluetooth schema is hardly manageable: it lists all possible properties (19 supplies). Split qcom,wcn3990-bt to separate bindings, so device schema will be easier to read/maintain and list only relevant properties. This binding is very similar to qcom,wcn3950-bt, however devices have additional VDD_CH1 supply. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/qcom,wcn3990-bt.yaml | 66 +++++++++++++++++++ .../net/bluetooth/qualcomm-bluetooth.yaml | 47 ------------- 2 files changed, 66 insertions(+), 47 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3990-bt.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3990-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3990-bt.yaml new file mode 100644 index 0000000000000..89ceb1f7def07 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3990-bt.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn3990-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN3990/WCN3991/WCN3998 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn3990-bt + - qcom,wcn3991-bt + - qcom,wcn3998-bt + + clocks: + items: + - description: External low-power 32.768 kHz clock input + + vddch0-supply: + description: VDD_CH0 supply regulator handle + + vddch1-supply: + description: VDD_CH1 supply regulator handle + + vddio-supply: + description: VDD_IO supply regulator handle + + vddrf-supply: + description: VDD_RF supply regulator handle + + vddxo-supply: + description: VDD_XO supply regulator handle + +required: + - compatible + - vddch0-supply + - vddio-supply + - vddrf-supply + - vddxo-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,wcn3990-bt"; + firmware-name = "crnv21.bin"; + max-speed = <3200000>; + vddio-supply = <&vreg_s4a_1p8>; + vddch0-supply = <&vreg_l25a_3p3>; + vddch1-supply = <&vreg_l23a_3p3>; + vddrf-supply = <&vreg_l17a_1p3>; + vddxo-supply = <&vreg_l7a_1p8>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index 82cce508e2955..5581e810f08e7 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -16,9 +16,6 @@ description: properties: compatible: enum: - - qcom,wcn3990-bt - - qcom,wcn3991-bt - - qcom,wcn3998-bt - qcom,wcn6750-bt - qcom,wcn6855-bt - qcom,wcn7850-bt @@ -32,25 +29,9 @@ properties: description: gpio specifier is used to find status of clock supply to SoC - clocks: - maxItems: 1 - description: clock provided to the controller (SUSCLK_32KHZ) - vddio-supply: description: VDD_IO supply regulator handle - vddxo-supply: - description: VDD_XO supply regulator handle - - vddrf-supply: - description: VDD_RF supply regulator handle - - vddch0-supply: - description: VDD_CH0 supply regulator handle - - vddch1-supply: - description: VDD_CH1 supply regulator handle - vddaon-supply: description: VDD_AON supply regulator handle @@ -117,21 +98,6 @@ allOf: - $ref: bluetooth-controller.yaml# - $ref: /schemas/serial/serial-peripheral-props.yaml# - - if: - properties: - compatible: - contains: - enum: - - qcom,wcn3990-bt - - qcom,wcn3991-bt - - qcom,wcn3998-bt - then: - required: - - vddio-supply - - vddxo-supply - - vddrf-supply - - vddch0-supply - - if: properties: compatible: @@ -178,19 +144,6 @@ allOf: - vddrfa1p8-supply examples: - - | - serial { - - bluetooth { - compatible = "qcom,wcn3990-bt"; - vddio-supply = <&vreg_s4a_1p8>; - vddxo-supply = <&vreg_l7a_1p8>; - vddrf-supply = <&vreg_l17a_1p3>; - vddch0-supply = <&vreg_l25a_3p3>; - max-speed = <3200000>; - firmware-name = "crnv21.bin"; - }; - }; - | #include serial { From 4d3a212deeea06f8414f81fae9844b3b87216961 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:03 +0100 Subject: [PATCH 23/43] dt-bindings: bluetooth: qcom,wcn6750-bt: Split to separate schema One big Qualcomm Bluetooth schema is hardly manageable: it lists all possible properties (19 supplies). Split qcom,wcn6750-bt to separate bindings, so device schema will be easier to read/maintain and list only relevant properties. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/qcom,wcn6750-bt.yaml | 86 +++++++++++++++++++ .../net/bluetooth/qualcomm-bluetooth.yaml | 45 ---------- 2 files changed, 86 insertions(+), 45 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml new file mode 100644 index 0000000000000..f11d12c205fad --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn6750-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN6750 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn6750-bt + + enable-gpios: + maxItems: 1 + + swctrl-gpios: + maxItems: 1 + description: gpio specifier is used to find status + of clock supply to SoC + + vddaon-supply: + description: VDD_AON supply regulator handle + + vddasd-supply: + description: VDD_ASD supply regulator handle + + vddbtcmx-supply: + description: VDD_BT_CMX supply regulator handle + + vddbtcxmx-supply: + description: VDD_BT_CXMX supply regulator handle + + vddio-supply: + description: VDD_IO supply regulator handle + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p7-supply: + description: VDD_RFA_1P7 supply regulator handle + + vddrfa2p2-supply: + description: VDD_RFA_2P2 supply regulator handle + + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + +required: + - compatible + - vddaon-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p7-supply + - vddrfacmn-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,wcn6750-bt"; + + firmware-name = "msnv11.bin"; + max-speed = <3200000>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index 5581e810f08e7..1ee97089e1d5e 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -16,7 +16,6 @@ description: properties: compatible: enum: - - qcom,wcn6750-bt - qcom,wcn6855-bt - qcom,wcn7850-bt @@ -62,12 +61,6 @@ properties: vddrfa1p9-supply: description: VDD_RFA_1P9 supply regulator handle - vddrfa2p2-supply: - description: VDD_RFA_2P2 supply regulator handle - - vddasd-supply: - description: VDD_ASD supply regulator handle - vddwlcx-supply: description: VDD_WLCX supply regulator handle @@ -98,19 +91,6 @@ allOf: - $ref: bluetooth-controller.yaml# - $ref: /schemas/serial/serial-peripheral-props.yaml# - - if: - properties: - compatible: - contains: - enum: - - qcom,wcn6750-bt - then: - required: - - vddaon-supply - - vddrfacmn-supply - - vddrfa0p8-supply - - vddrfa1p7-supply - - vddrfa1p2-supply - if: properties: compatible: @@ -142,28 +122,3 @@ allOf: - vddrfa0p8-supply - vddrfa1p2-supply - vddrfa1p8-supply - -examples: - - | - #include - serial { - - bluetooth { - compatible = "qcom,wcn6750-bt"; - pinctrl-names = "default"; - pinctrl-0 = <&bt_en_default>; - enable-gpios = <&tlmm 85 GPIO_ACTIVE_HIGH>; - swctrl-gpios = <&tlmm 86 GPIO_ACTIVE_HIGH>; - vddio-supply = <&vreg_l19b_1p8>; - vddaon-supply = <&vreg_s7b_0p9>; - vddbtcxmx-supply = <&vreg_s7b_0p9>; - vddrfacmn-supply = <&vreg_s7b_0p9>; - vddrfa0p8-supply = <&vreg_s7b_0p9>; - vddrfa1p7-supply = <&vreg_s1b_1p8>; - vddrfa1p2-supply = <&vreg_s8b_1p2>; - vddrfa2p2-supply = <&vreg_s1c_2p2>; - vddasd-supply = <&vreg_l11c_2p8>; - max-speed = <3200000>; - firmware-name = "msnv11.bin"; - }; - }; From 74ee2b6db9f491a01c4481888335e218bc1fdc87 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:04 +0100 Subject: [PATCH 24/43] dt-bindings: bluetooth: qcom,wcn6750-bt: Deprecate old supplies Commit cca4fe34979a ("dt-bindings: bluetooth: Utilize PMU abstraction for WCN6750") changed the binding to new description with a Power Management Unit (PMU), thus certain power-controller properties are considered deprecated and are part of that PMUs binding. Deprecate them to mark clearly that new PMU-based approach is preferred. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml index f11d12c205fad..8606a45ac9b97 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml @@ -17,26 +17,31 @@ properties: enable-gpios: maxItems: 1 + deprecated: true swctrl-gpios: maxItems: 1 description: gpio specifier is used to find status of clock supply to SoC + deprecated: true vddaon-supply: description: VDD_AON supply regulator handle vddasd-supply: description: VDD_ASD supply regulator handle + deprecated: true vddbtcmx-supply: description: VDD_BT_CMX supply regulator handle vddbtcxmx-supply: description: VDD_BT_CXMX supply regulator handle + deprecated: true vddio-supply: description: VDD_IO supply regulator handle + deprecated: true vddrfa0p8-supply: description: VDD_RFA_0P8 supply regulator handle From 691c0da555621c28fafdc5b762d4673721344c11 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:05 +0100 Subject: [PATCH 25/43] dt-bindings: bluetooth: qcom,wcn6855-bt: Split to separate schema One big Qualcomm Bluetooth schema is hardly manageable: it lists all possible properties (19 supplies). Split qcom,wcn6855-bt to separate bindings, so device schema will be easier to read/maintain and list only relevant properties. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/qcom,wcn6855-bt.yaml | 94 +++++++++++++++++++ .../net/bluetooth/qualcomm-bluetooth.yaml | 26 ----- 2 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml new file mode 100644 index 0000000000000..a4a887a4dde1d --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn6855-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN6855 Bluetooth + +maintainers: + - Bartosz Golaszewski + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn6855-bt + + enable-gpios: + maxItems: 1 + + swctrl-gpios: + maxItems: 1 + description: gpio specifier is used to find status + of clock supply to SoC + + vddaon-supply: + description: VDD_AON supply regulator handle + + vddbtcmx-supply: + description: VDD_BT_CMX supply regulator handle + + vddbtcxmx-supply: + description: VDD_BT_CXMX supply regulator handle + + vddio-supply: + description: VDD_IO supply regulator handle + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p7-supply: + description: VDD_RFA_1P7 supply regulator handle + + vddrfa1p8-supply: + description: VDD_RFA_1P8 supply regulator handle + + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + + vddwlcx-supply: + description: VDD_WLCX supply regulator handle + + vddwlmx-supply: + description: VDD_WLMX supply regulator handle + +required: + - compatible + - vddaon-supply + - vddbtcmx-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p8-supply + - vddrfacmn-supply + - vddwlcx-supply + - vddwlmx-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,wcn6855-bt"; + + max-speed = <3000000>; + vddaon-supply = <&vreg_pmu_aon_0p8>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p8>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn_0p8>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p8>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index 1ee97089e1d5e..62d7cdb67a3a9 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -16,7 +16,6 @@ description: properties: compatible: enum: - - qcom,wcn6855-bt - qcom,wcn7850-bt enable-gpios: @@ -37,21 +36,12 @@ properties: vdddig-supply: description: VDD_DIG supply regulator handle - vddbtcmx-supply: - description: VDD_BT_CMX supply regulator handle - - vddbtcxmx-supply: - description: VDD_BT_CXMX supply regulator handle - vddrfacmn-supply: description: VDD_RFA_CMN supply regulator handle vddrfa0p8-supply: description: VDD_RFA_0P8 supply regulator handle - vddrfa1p7-supply: - description: VDD_RFA_1P7 supply regulator handle - vddrfa1p8-supply: description: VDD_RFA_1P8 supply regulator handle @@ -91,22 +81,6 @@ allOf: - $ref: bluetooth-controller.yaml# - $ref: /schemas/serial/serial-peripheral-props.yaml# - - if: - properties: - compatible: - contains: - enum: - - qcom,wcn6855-bt - then: - required: - - vddrfacmn-supply - - vddaon-supply - - vddwlcx-supply - - vddwlmx-supply - - vddbtcmx-supply - - vddrfa0p8-supply - - vddrfa1p2-supply - - vddrfa1p8-supply - if: properties: compatible: From 9d2f4c4a4be2351050815c81771a45016ce03a10 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:06 +0100 Subject: [PATCH 26/43] dt-bindings: bluetooth: qcom,wcn6855-bt: Deprecate old supplies Commit 5f4f954bba12 ("dt-bindings: bluetooth: bring the HW description closer to reality for wcn6855") changed the binding to new description with a Power Management Unit (PMU), thus certain power-controller properties are considered deprecated and are part of that PMUs binding. Deprecate them to mark clearly that new PMU-based approach is preferred. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml index a4a887a4dde1d..45630067d3c8e 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml @@ -18,11 +18,13 @@ properties: enable-gpios: maxItems: 1 + deprecated: true swctrl-gpios: maxItems: 1 description: gpio specifier is used to find status of clock supply to SoC + deprecated: true vddaon-supply: description: VDD_AON supply regulator handle @@ -32,9 +34,11 @@ properties: vddbtcxmx-supply: description: VDD_BT_CXMX supply regulator handle + deprecated: true vddio-supply: description: VDD_IO supply regulator handle + deprecated: true vddrfa0p8-supply: description: VDD_RFA_0P8 supply regulator handle @@ -44,6 +48,7 @@ properties: vddrfa1p7-supply: description: VDD_RFA_1P7 supply regulator handle + deprecated: true vddrfa1p8-supply: description: VDD_RFA_1P8 supply regulator handle From 0f755f17590dbbddd367b94fd8cdf84f153ab18e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:07 +0100 Subject: [PATCH 27/43] dt-bindings: bluetooth: qcom,wcn7850-bt: Split to separate schema One big Qualcomm Bluetooth schema is hardly manageable: it lists all possible properties (19 supplies). Split qcom,wcn7850-bt to separate bindings, so device schema will be easier to read/maintain and list only relevant properties. Re-shuffle few properties to alphabetical order while moving them and drop redundant enable-gpios description. This makes the source qualcomm-bluetooth.yaml binding empty, thus drop it making entire change a variant of file rename. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- ...mm-bluetooth.yaml => qcom,wcn7850-bt.yaml} | 83 +++++++++---------- 1 file changed, 37 insertions(+), 46 deletions(-) rename Documentation/devicetree/bindings/net/bluetooth/{qualcomm-bluetooth.yaml => qcom,wcn7850-bt.yaml} (57%) diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml similarity index 57% rename from Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml rename to Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml index 62d7cdb67a3a9..bd628e48b4e1d 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml @@ -1,18 +1,16 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/net/bluetooth/qualcomm-bluetooth.yaml# +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn7850-bt.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm Bluetooth Chips +title: Qualcomm WCN7850 Bluetooth maintainers: + - Bartosz Golaszewski - Balakrishna Godavarthi - Rocky Liao -description: - This binding describes Qualcomm UART-attached bluetooth chips. - properties: compatible: enum: @@ -20,79 +18,72 @@ properties: enable-gpios: maxItems: 1 - description: gpio specifier used to enable chip swctrl-gpios: maxItems: 1 description: gpio specifier is used to find status of clock supply to SoC - vddio-supply: - description: VDD_IO supply regulator handle - vddaon-supply: description: VDD_AON supply regulator handle vdddig-supply: description: VDD_DIG supply regulator handle - vddrfacmn-supply: - description: VDD_RFA_CMN supply regulator handle + vddio-supply: + description: VDD_IO supply regulator handle vddrfa0p8-supply: description: VDD_RFA_0P8 supply regulator handle - vddrfa1p8-supply: - description: VDD_RFA_1P8 supply regulator handle - vddrfa1p2-supply: description: VDD_RFA_1P2 supply regulator handle + vddrfa1p8-supply: + description: VDD_RFA_1P8 supply regulator handle + vddrfa1p9-supply: description: VDD_RFA_1P9 supply regulator handle + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + vddwlcx-supply: description: VDD_WLCX supply regulator handle vddwlmx-supply: description: VDD_WLMX supply regulator handle - max-speed: true - - firmware-name: - minItems: 1 - items: - - description: specify the name of nvm firmware to load - - description: specify the name of rampatch firmware to load - - local-bd-address: true - - qcom,local-bd-address-broken: - type: boolean - description: - boot firmware is incorrectly passing the address in big-endian order - required: - compatible - -additionalProperties: false + - vddrfacmn-supply + - vddaon-supply + - vddwlcx-supply + - vddwlmx-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p8-supply allOf: - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml - $ref: /schemas/serial/serial-peripheral-props.yaml# - - if: - properties: - compatible: - contains: - enum: - - qcom,wcn7850-bt - then: - required: - - vddrfacmn-supply - - vddaon-supply - - vddwlcx-supply - - vddwlmx-supply - - vddrfa0p8-supply - - vddrfa1p2-supply - - vddrfa1p8-supply +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,wcn7850-bt"; + + max-speed = <3200000>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + }; + }; From fc1e82e745e7344942f1737f42beacc0aa04c42d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 11 Jan 2026 16:49:08 +0100 Subject: [PATCH 28/43] dt-bindings: bluetooth: qcom,wcn7850-bt: Deprecate old supplies Commit bd3f305886ad ("dt-bindings: bluetooth: qualcomm: describe the inputs from PMU for wcn7850") changed the binding to new description with a Power Management Unit (PMU), thus certain power-controller properties are considered deprecated and are part of that PMUs binding. Deprecate them to mark clearly that new PMU-based approach is preferred. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Reviewed-by: Bartosz Golaszewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml index bd628e48b4e1d..8108ef83e99b9 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml @@ -18,20 +18,24 @@ properties: enable-gpios: maxItems: 1 + deprecated: true swctrl-gpios: maxItems: 1 description: gpio specifier is used to find status of clock supply to SoC + deprecated: true vddaon-supply: description: VDD_AON supply regulator handle vdddig-supply: description: VDD_DIG supply regulator handle + deprecated: true vddio-supply: description: VDD_IO supply regulator handle + deprecated: true vddrfa0p8-supply: description: VDD_RFA_0P8 supply regulator handle @@ -44,6 +48,7 @@ properties: vddrfa1p9-supply: description: VDD_RFA_1P9 supply regulator handle + deprecated: true vddrfacmn-supply: description: VDD_RFA_CMN supply regulator handle From d1e94a4934e88d4dd1c308185a757a5899236b92 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Mon, 12 Jan 2026 11:51:13 +0100 Subject: [PATCH 29/43] Bluetooth: btusb: Use pm_ptr instead of #ifdef CONFIG_PM This increases build coverage and allows to drop an #ifdef. Signed-off-by: Bastien Nocera Reviewed-by: Paul Menzel Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index de9e484efef71..024296ae1dc92 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4637,10 +4637,8 @@ static struct usb_driver btusb_driver = { .name = "btusb", .probe = btusb_probe, .disconnect = btusb_disconnect, -#ifdef CONFIG_PM - .suspend = btusb_suspend, - .resume = btusb_resume, -#endif + .suspend = pm_ptr(btusb_suspend), + .resume = pm_ptr(btusb_resume), .id_table = btusb_table, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, From e133883028d00f1b994ab2a1bef24847cfade8e9 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 9 Jan 2026 10:05:37 +0800 Subject: [PATCH 30/43] Bluetooth: hci_core: Export hci_discovery_active Export hci_discovery_active() so it can be used by bluetooth drivers built as modules. Signed-off-by: Linmao Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8ccec73dce45c..b069607b145b5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -117,6 +117,7 @@ bool hci_discovery_active(struct hci_dev *hdev) return false; } } +EXPORT_SYMBOL(hci_discovery_active); void hci_discovery_set_state(struct hci_dev *hdev, int state) { From 2e5da9653691cc24211877062a8e969a52096808 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 9 Jan 2026 10:05:38 +0800 Subject: [PATCH 31/43] Bluetooth: btusb: Reject autosuspend if discovery is active If USB autosuspend occurs while discovery is active, the ongoing HCI operation may not complete successfully. On some devices, this can leave discovery.state stuck in DISCOVERY_FINDING. Signed-off-by: Linmao Li Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 024296ae1dc92..51278a0e4fd67 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4473,10 +4473,11 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message) BT_DBG("intf %p", intf); - /* Don't auto-suspend if there are connections; external suspend calls - * shall never fail. + /* Don't auto-suspend if there are connections or discovery in + * progress; external suspend calls shall never fail. */ - if (PMSG_IS_AUTO(message) && hci_conn_count(data->hdev)) + if (PMSG_IS_AUTO(message) && + (hci_conn_count(data->hdev) || hci_discovery_active(data->hdev))) return -EBUSY; if (data->suspend_count++) From e7984f7933b13b687352f301a6812757e4809903 Mon Sep 17 00:00:00 2001 From: Tedd Ho-Jeong An Date: Wed, 4 Nov 2020 21:09:48 -0800 Subject: [PATCH 32/43] workflow: Add workflow files for ci This patch adds workflow files for ci: [sync.yml] - The workflow file for scheduled work - Sync the repo with upstream repo and rebase the workflow branch - Review the patches in the patchwork and creates the PR if needed [ci.yml] - The workflow file for CI tasks - Run CI tests when PR is created Signed-off-by: Tedd Ho-Jeong An --- .github/workflows/ci.yml | 25 ++++++++++++++++++++++ .github/workflows/sync.yml | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/sync.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000..3a2c45c37553c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CI + +on: [pull_request] + +jobs: + ci: + runs-on: ubuntu-latest + name: CI for Pull Request + steps: + - name: Checkout the source code + uses: actions/checkout@v3 + with: + path: src/src + + - name: CI + uses: tedd-an/bzcafe@main + with: + task: ci + base_folder: src + space: kernel + github_token: ${{ secrets.GITHUB_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} + diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000000000..3883d55a23267 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,43 @@ +name: Sync + +on: + schedule: + - cron: "*/30 * * * *" + +jobs: + sync_repo: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: master + + - name: Sync Repo + uses: tedd-an/bzcafe@main + with: + task: sync + upstream_repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git' + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Cleanup PR + uses: tedd-an/bzcafe@main + with: + task: cleanup + github_token: ${{ secrets.ACTION_TOKEN }} + + sync_patchwork: + needs: sync_repo + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Sync Patchwork + uses: tedd-an/bzcafe@main + with: + task: patchwork + space: kernel + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} + From 928f53eea386b08f77d0d727a7a082e7658966a0 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:08 +0530 Subject: [PATCH 33/43] Bluetooth: btnxpuart: Add firmware metadata parsing for secure interface This adds support for parsing firmware metadata TLVs to extract FW UUID and ECDSA Public Key from FW metadata for secure interface authentication. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 133 ++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 8 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 3b1e9224e9657..78a7651d55d61 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -134,6 +135,14 @@ #define BT_CTRL_WAKEUP_METHOD_EXT_BREAK 0x04 #define BT_CTRL_WAKEUP_METHOD_RTS 0x05 +/* FW Metadata */ +#define FW_METADATA_TLV_UUID 0x40 +#define FW_METADATA_TLV_ECDSA_KEY 0x50 +#define FW_METADATA_FLAG_BT 0x02 + +#define NXP_FW_UUID_SIZE 16 +#define NXP_FW_ECDSA_PUBKEY_SIZE 65 + struct ps_data { u8 target_ps_mode; /* ps mode to be set */ u8 cur_psmode; /* current ps_mode */ @@ -180,6 +189,11 @@ enum bootloader_param_change { changed }; +struct btnxpuart_crypto { + u8 ecdsa_public[NXP_FW_ECDSA_PUBKEY_SIZE]; /* ECDSA public key, Authentication*/ + u8 fw_uuid[NXP_FW_UUID_SIZE]; +}; + struct btnxpuart_dev { struct hci_dev *hdev; struct serdev_device *serdev; @@ -213,6 +227,7 @@ struct btnxpuart_dev { struct btnxpuart_data *nxp_data; struct reset_control *pdn; struct hci_uart hu; + struct btnxpuart_crypto crypto; }; #define NXP_V1_FW_REQ_PKT 0xa5 @@ -362,6 +377,26 @@ union nxp_set_bd_addr_payload { u8 buf[8]; }; +/* FW Meta Data */ +struct fw_metadata_hdr { + __le32 cmd; + __le32 addr; + __le32 len; + __le32 crc; +}; + +struct fw_metadata_tail { + __le32 len; + u8 magic[8]; + __le32 crc; +}; + +struct fw_metadata_tlv { + __le16 id; + __le16 flag; + __le32 len; +}; + static u8 crc8_table[CRC8_TABLE_SIZE]; /* Default configurations */ @@ -1190,6 +1225,85 @@ static void nxp_handle_fw_download_error(struct hci_dev *hdev, struct v3_data_re } } +static u32 nxp_process_fw_metadata_tlv(struct hci_dev *hdev, char **payload) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct fw_metadata_tlv *tlv = (struct fw_metadata_tlv *)(*payload); + u32 ret = sizeof(*tlv) + le32_to_cpu(tlv->len); + + /* Process only BT specific metadata TLVs */ + if (!(le16_to_cpu(tlv->flag) & FW_METADATA_FLAG_BT)) + goto align_and_return; + + switch (le16_to_cpu(tlv->id)) { + case FW_METADATA_TLV_UUID: + if (le32_to_cpu(tlv->len) == NXP_FW_UUID_SIZE) + memcpy(nxpdev->crypto.fw_uuid, + *payload + sizeof(*tlv), NXP_FW_UUID_SIZE); + break; + case FW_METADATA_TLV_ECDSA_KEY: + if (le32_to_cpu(tlv->len) == NXP_FW_ECDSA_PUBKEY_SIZE) + memcpy(nxpdev->crypto.ecdsa_public, + *payload + sizeof(*tlv), NXP_FW_ECDSA_PUBKEY_SIZE); + break; + default: + bt_dev_err(hdev, "Unknown metadata TLV ID: 0x%x", le16_to_cpu(tlv->id)); + break; + } + +align_and_return: + /* Align the pointer to 4 byte structure alignment */ + ret = round_up(ret, 4); + *payload += ret; + + return ret; +} + +static void nxp_process_fw_meta_data(struct hci_dev *hdev, const struct firmware *fw) +{ + const char *metamagc = "metamagc"; + struct fw_metadata_hdr *hdr = NULL; + struct fw_metadata_tail *tail; + u32 hdr_crc = 0; + u32 payload_crc = 0; + char *payload; + u32 payload_len = 0; + + /* FW metadata should contain at least header and tail */ + if (fw->size < (sizeof(*hdr) + sizeof(*tail))) + return; + + tail = (struct fw_metadata_tail *)&fw->data[fw->size - sizeof(*tail)]; + + /* If tail doesn't contain the string "metamagc", this is invalid FW metadata */ + if (memcmp(metamagc, tail->magic, strlen(metamagc))) + return; + + hdr = (struct fw_metadata_hdr *)&fw->data[fw->size - + sizeof(*tail) - + le32_to_cpu(tail->len)]; + + /* If metadata header isn't cmd24, this is invalid FW metadata */ + if (le32_to_cpu(hdr->cmd) != 24) + return; + + /* If header CRC doesn't match, this is invalid FW metadata */ + hdr_crc = crc32_be(0, (u8 *)hdr, offsetof(struct fw_metadata_hdr, crc)); + if (hdr_crc != le32_to_cpu(hdr->crc)) + return; + + /* If payload CRC doesn't match, this is invalid FW metadata */ + payload = (u8 *)hdr + sizeof(*hdr); + payload_crc = crc32_be(0, payload, le32_to_cpu(hdr->len) - 4); + if (payload_crc != le32_to_cpu(tail->crc)) + return; + + payload_len = le32_to_cpu(hdr->len) - sizeof(*tail); + + while (payload_len > sizeof(struct fw_metadata_tlv)) + payload_len -= nxp_process_fw_metadata_tlv(hdev, &payload); +} + static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); @@ -1248,14 +1362,6 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) goto free_skb; } - if (req->len == 0) { - bt_dev_info(hdev, "FW Download Complete: %zu bytes", - nxpdev->fw->size); - clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); - wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); - goto free_skb; - } - offset = __le32_to_cpu(req->offset); if (offset < nxpdev->fw_v3_offset_correction) { /* This scenario should ideally never occur. But if it ever does, @@ -1267,6 +1373,17 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; + + if (req->len == 0) { + if (nxpdev->fw_dnld_v3_offset < nxpdev->fw->size) + nxp_process_fw_meta_data(hdev, nxpdev->fw); + bt_dev_info(hdev, "FW Download Complete: %u bytes.", + req->offset - nxpdev->fw_v3_offset_correction); + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); + goto free_skb; + } + serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); From f0871e85d9cf4cfafe31914e327324dbace64cfb Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:09 +0530 Subject: [PATCH 34/43] Bluetooth: btnxpuart: Print FW version and enable chip specific features This adds a print for FW version after FW is downloaded, and a way to enable chip specific features. Currently, secure interface feature is enabled for AW693 chipset. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 78a7651d55d61..d2c79c462ebb9 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -105,6 +105,8 @@ #define PS_STATE_SLEEP 1 /* NXP Vendor Commands. Refer user manual UM11628 on nxp.com */ +/* Get FW version */ +#define HCI_NXP_GET_FW_VERSION 0xfc0f /* Set custom BD Address */ #define HCI_NXP_SET_BD_ADDR 0xfc22 /* Set Auto-Sleep mode */ @@ -227,6 +229,7 @@ struct btnxpuart_dev { struct btnxpuart_data *nxp_data; struct reset_control *pdn; struct hci_uart hu; + bool secure_interface; struct btnxpuart_crypto crypto; }; @@ -1554,6 +1557,47 @@ static int nxp_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) return 0; } +static void nxp_handle_chip_specific_features(struct hci_dev *hdev, u8 *version) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + if (!version || strlen(version) == 0) + return; + + if (!strncmp(version, "aw693n-V1", strlen("aw693n-V1"))) + nxpdev->secure_interface = true; +} + +static void nxp_get_fw_version(struct hci_dev *hdev) +{ + struct sk_buff *skb; + u8 version[100] = {0}; + u8 cmd = 0; + u8 *status; + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_GET_FW_VERSION, 1, &cmd, true); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Failed to get firmware version (%ld)", + PTR_ERR(skb)); + return; + } + + status = skb_pull_data(skb, 1); + if (status) { + if (*status) { + bt_dev_err(hdev, "Error get FW version: %d", *status); + } else if (skb->len < 10 || skb->len >= 100) { + bt_dev_err(hdev, "Invalid FW version"); + } else { + memcpy(version, skb->data, skb->len); + bt_dev_info(hdev, "FW Version: %s", version); + nxp_handle_chip_specific_features(hdev, version); + } + } + + kfree_skb(skb); +} + /* NXP protocol */ static int nxp_setup(struct hci_dev *hdev) { @@ -1583,6 +1627,8 @@ static int nxp_setup(struct hci_dev *hdev) serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate); nxpdev->current_baudrate = nxpdev->fw_init_baudrate; + nxp_get_fw_version(hdev); + ps_init(hdev); if (test_and_clear_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state)) From 3381f36c840f4c33736141523911b3a1cb1f7f02 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:10 +0530 Subject: [PATCH 35/43] Bluetooth: btnxpuart: Add secure interface TLS authentication support This includes a placeholder nxp_authenticate_device() function if the chip supports secure interface feature. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index d2c79c462ebb9..3455460d30f5e 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1598,6 +1598,23 @@ static void nxp_get_fw_version(struct hci_dev *hdev) kfree_skb(skb); } +/* Secure Interface */ +static int nxp_authenticate_device(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int ret = 0; + + /* TODO: Implement actual TLS handshake protocol + * This will include: + * 1. Crypto allocation (SHA256, ECDH-P256) + * 2. Host/Device hello message exchange + * 3. Master secret and traffic key derivation + * 4. Proper error handling and cleanup + */ + + return ret; +} + /* NXP protocol */ static int nxp_setup(struct hci_dev *hdev) { @@ -1629,6 +1646,12 @@ static int nxp_setup(struct hci_dev *hdev) nxp_get_fw_version(hdev); + if (nxpdev->secure_interface) { + err = nxp_authenticate_device(hdev); + if (err) + return -EACCES; + } + ps_init(hdev); if (test_and_clear_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state)) From 38413597c92d4e390f3ae676278f5d9f48b4f192 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:11 +0530 Subject: [PATCH 36/43] Bluetooth: btnxpuart: Implement TLS authentication crypto framework This adds nxp_authenticate_device() function that sets up cryptographic resources for TLS handshake authentication. Allocates SHA256 hash and ECDH-P256 key exchange with proper cleanup after handshake completion to maintain only traffic keys. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 48 ++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 3455460d30f5e..7c94d8ab94f38 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -22,6 +22,11 @@ #include #include +#include +#include +#include +#include + #include #include @@ -192,6 +197,9 @@ enum bootloader_param_change { }; struct btnxpuart_crypto { + struct crypto_shash *tls_handshake_hash_tfm; + struct shash_desc *tls_handshake_hash_desc; + struct crypto_kpp *kpp; u8 ecdsa_public[NXP_FW_ECDSA_PUBKEY_SIZE]; /* ECDSA public key, Authentication*/ u8 fw_uuid[NXP_FW_UUID_SIZE]; }; @@ -1602,16 +1610,48 @@ static void nxp_get_fw_version(struct hci_dev *hdev) static int nxp_authenticate_device(struct hci_dev *hdev) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + size_t desc_size = 0; int ret = 0; + nxpdev->crypto.tls_handshake_hash_tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(nxpdev->crypto.tls_handshake_hash_tfm)) + return PTR_ERR(nxpdev->crypto.tls_handshake_hash_tfm); + + desc_size = sizeof(struct shash_desc) + + crypto_shash_descsize(nxpdev->crypto.tls_handshake_hash_tfm); + nxpdev->crypto.tls_handshake_hash_desc = kzalloc(desc_size, GFP_KERNEL); + if (!nxpdev->crypto.tls_handshake_hash_desc) { + ret = -ENOMEM; + goto free_tfm; + } + + nxpdev->crypto.kpp = crypto_alloc_kpp("ecdh-nist-p256", 0, 0); + if (IS_ERR(nxpdev->crypto.kpp)) { + ret = PTR_ERR(nxpdev->crypto.kpp); + goto free_desc; + } + + nxpdev->crypto.tls_handshake_hash_desc->tfm = nxpdev->crypto.tls_handshake_hash_tfm; + crypto_shash_init(nxpdev->crypto.tls_handshake_hash_desc); + /* TODO: Implement actual TLS handshake protocol * This will include: - * 1. Crypto allocation (SHA256, ECDH-P256) - * 2. Host/Device hello message exchange - * 3. Master secret and traffic key derivation - * 4. Proper error handling and cleanup + * 1. Host/Device hello message exchange + * 2. Master secret and traffic key derivation */ +free_kpp: + crypto_free_kpp(nxpdev->crypto.kpp); + nxpdev->crypto.kpp = NULL; +free_desc: + kfree(nxpdev->crypto.tls_handshake_hash_desc); + nxpdev->crypto.tls_handshake_hash_desc = NULL; +free_tfm: + crypto_free_shash(nxpdev->crypto.tls_handshake_hash_tfm); + nxpdev->crypto.tls_handshake_hash_tfm = NULL; + if (ret) + bt_dev_err(hdev, "Device Authentication failed: %d", ret); + return ret; } From aa01398299307e79854de3ae5082aa4c6386d3ea Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:12 +0530 Subject: [PATCH 37/43] Bluetooth: btnxpuart: Add TLS host hello handshake implementation Implement TLS handshake initiation for secure interface authentication. Includes ECDH public key generation, host hello message creation, and handshake hash computation for secure chip authentication. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 189 +++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 7c94d8ab94f38..8208b0748f975 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -124,6 +125,8 @@ #define HCI_NXP_IND_RESET 0xfcfc /* Bluetooth vendor command: Trigger FW dump */ #define HCI_NXP_TRIGGER_DUMP 0xfe91 +/* Bluetooth vendor command: Secure Host Interface */ +#define HCI_NXP_SHI_ENCRYPT 0xfe9c /* Bluetooth Power State : Vendor cmd params */ #define BT_PS_ENABLE 0x02 @@ -388,6 +391,55 @@ union nxp_set_bd_addr_payload { u8 buf[8]; }; +/* Secure Host Interface */ +#define NXP_TLS_MAGIC 0x43b826f3 +#define NXP_TLS_VERSION 1 + +#define NXP_TLS_ECDH_PUBLIC_KEY_SIZE 64 + +enum nxp_tls_signature_algorithm { + NXP_TLS_ECDSA_SECP256R1_SHA256 = 0x0403, +}; + +enum nxp_tls_key_exchange_type { + NXP_TLS_ECDHE_SECP256R1 = 0x0017, +}; + +enum nxp_tls_cipher_suite { + NXP_TLS_AES_128_GCM_SHA256 = 0x1301, +}; + +enum nxp_tls_message_id { + NXP_TLS_HOST_HELLO = 1, + NXP_TLS_DEVICE_HELLO = 2, + NXP_TLS_HOST_FINISHED = 3, +}; + +struct nxp_tls_message_hdr { + __le32 magic; + __le16 len; + u8 message_id; + u8 protocol_version; +}; + +struct nxp_tls_host_hello { + struct nxp_tls_message_hdr hdr; + __le16 sig_alg; + __le16 key_exchange_type; + __le16 cipher_suite; + __le16 reserved; + u8 random[32]; + u8 pubkey[NXP_TLS_ECDH_PUBLIC_KEY_SIZE]; /* ECDHE */ +}; + +union nxp_tls_host_hello_payload { + struct { + u8 msg_type; + struct nxp_tls_host_hello host_hello; + } __packed; + u8 buf[113]; +}; + /* FW Meta Data */ struct fw_metadata_hdr { __le32 cmd; @@ -1607,10 +1659,137 @@ static void nxp_get_fw_version(struct hci_dev *hdev) } /* Secure Interface */ +static int nxp_generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64]) +{ + DECLARE_CRYPTO_WAIT(result); + struct kpp_request *req; + u8 *tmp; + struct scatterlist dst; + int err; + + tmp = kzalloc(64, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + req = kpp_request_alloc(tfm, GFP_KERNEL); + if (!req) { + err = -ENOMEM; + goto free_tmp; + } + + sg_init_one(&dst, tmp, 64); + kpp_request_set_input(req, NULL, 0); + kpp_request_set_output(req, &dst, 64); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &result); + + err = crypto_kpp_generate_public_key(req); + err = crypto_wait_req(err, &result); + if (err < 0) + goto free_all; + + memcpy(public_key, tmp, 64); + +free_all: + kpp_request_free(req); +free_tmp: + kfree(tmp); + return err; +} + +static inline void nxp_tls_hdr_init(struct nxp_tls_message_hdr *hdr, size_t len, + enum nxp_tls_message_id id) +{ + hdr->magic = cpu_to_le32(NXP_TLS_MAGIC); + hdr->len = cpu_to_le16((u16)len); + hdr->message_id = (u8)id; + hdr->protocol_version = NXP_TLS_VERSION; +} + +static struct sk_buff *nxp_host_do_hello(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + union nxp_tls_host_hello_payload tls_hello; + struct nxp_tls_host_hello *host_hello = &tls_hello.host_hello; + struct ecdh p = {0}; + u8 *buf = NULL; + unsigned int buf_len; + struct sk_buff *skb; + int ret; + + nxp_tls_hdr_init(&host_hello->hdr, sizeof(*host_hello), NXP_TLS_HOST_HELLO); + + host_hello->sig_alg = cpu_to_le16(NXP_TLS_ECDSA_SECP256R1_SHA256); + host_hello->key_exchange_type = cpu_to_le16(NXP_TLS_ECDHE_SECP256R1); + host_hello->cipher_suite = cpu_to_le16(NXP_TLS_AES_128_GCM_SHA256); + + get_random_bytes(host_hello->random, sizeof(host_hello->random)); + + /* Generate random private key */ + p.key_size = 32; + p.key = kzalloc(p.key_size, GFP_KERNEL); + if (!p.key) + return ERR_PTR(-ENOMEM); + + get_random_bytes(p.key, p.key_size); + + buf_len = crypto_ecdh_key_len(&p); + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto free_key; + } + + ret = crypto_ecdh_encode_key(buf, buf_len, &p); + if (ret) { + bt_dev_err(hdev, "crypto_ecdh_encode_key() failed"); + goto free_buf; + } + + ret = crypto_kpp_set_secret(nxpdev->crypto.kpp, buf, buf_len); + if (ret) { + bt_dev_err(hdev, "crypto_kpp_set_secret() failed"); + goto free_buf; + } + + ret = nxp_generate_ecdh_public_key(nxpdev->crypto.kpp, host_hello->pubkey); + if (ret) { + bt_dev_err(hdev, "Failed to generate ECDH public key: %d", ret); + goto free_buf; + } + + ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)host_hello, sizeof(*host_hello)); + if (ret) { + bt_dev_err(hdev, "Failed to update handshake hash: %d", ret); + goto free_buf; + } + + tls_hello.msg_type = 0; + + skb = __hci_cmd_sync(hdev, HCI_NXP_SHI_ENCRYPT, sizeof(tls_hello), + tls_hello.buf, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Host Hello command failed: %ld", PTR_ERR(skb)); + ret = PTR_ERR(skb); + } + +free_buf: + kfree(buf); +free_key: + memset(p.key, 0, p.key_size); + kfree(p.key); + if (ret) + return ERR_PTR(ret); + else + return skb; +} + static int nxp_authenticate_device(struct hci_dev *hdev) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); size_t desc_size = 0; + struct sk_buff *skb; int ret = 0; nxpdev->crypto.tls_handshake_hash_tfm = crypto_alloc_shash("sha256", 0, 0); @@ -1634,12 +1813,20 @@ static int nxp_authenticate_device(struct hci_dev *hdev) nxpdev->crypto.tls_handshake_hash_desc->tfm = nxpdev->crypto.tls_handshake_hash_tfm; crypto_shash_init(nxpdev->crypto.tls_handshake_hash_desc); + skb = nxp_host_do_hello(hdev); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + goto free_kpp; + } + /* TODO: Implement actual TLS handshake protocol * This will include: - * 1. Host/Device hello message exchange + * 1. Handle Device hello message exchange * 2. Master secret and traffic key derivation */ +free_skb: + kfree_skb(skb); free_kpp: crypto_free_kpp(nxpdev->crypto.kpp); nxpdev->crypto.kpp = NULL; From c64d572425743df41f561834e8c5dfe2e2d8328f Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:13 +0530 Subject: [PATCH 38/43] Bluetooth: btnxpuart: Add TLS device hello processing This implements device hello message processing to derive handshake traffic secrets: - Add HKDF-SHA256 functions for TLS 1.3 traffic secret derivation following RFC 5869/8446 - Extract device ECDH public key and compute shared secret using KPP API with host private key and device public key - Derive handshake traffic secret from ECDH shared secret following TLS 1.3 key schedule - Validate device hello message and update handshake hash state The handshake traffic secret enables decryption of the device_finish portion within the device_hello message. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 274 +++++++++++++++++++++++++++++++++- 1 file changed, 270 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 8208b0748f975..0e71f68a408ef 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -150,8 +150,9 @@ #define FW_METADATA_TLV_ECDSA_KEY 0x50 #define FW_METADATA_FLAG_BT 0x02 -#define NXP_FW_UUID_SIZE 16 -#define NXP_FW_ECDSA_PUBKEY_SIZE 65 +#define NXP_FW_UUID_SIZE 16 +#define NXP_FW_ECDH_PUBKEY_SIZE 64 +#define NXP_FW_ECDSA_PUBKEY_SIZE 65 struct ps_data { u8 target_ps_mode; /* ps mode to be set */ @@ -203,8 +204,11 @@ struct btnxpuart_crypto { struct crypto_shash *tls_handshake_hash_tfm; struct shash_desc *tls_handshake_hash_desc; struct crypto_kpp *kpp; + uint8_t ecdh_public[NXP_FW_ECDH_PUBKEY_SIZE]; /* ECDH public key, Key negotiation */ u8 ecdsa_public[NXP_FW_ECDSA_PUBKEY_SIZE]; /* ECDSA public key, Authentication*/ u8 fw_uuid[NXP_FW_UUID_SIZE]; + u8 handshake_h2_hash[SHA256_DIGEST_SIZE]; + u8 handshake_secret[SHA256_DIGEST_SIZE]; }; struct btnxpuart_dev { @@ -396,6 +400,11 @@ union nxp_set_bd_addr_payload { #define NXP_TLS_VERSION 1 #define NXP_TLS_ECDH_PUBLIC_KEY_SIZE 64 +#define NXP_DEVICE_UUID_LEN 16 +#define NXP_ENC_AUTH_TAG_SIZE 16 + +#define NXP_TLS_LABEL(str) str, strlen(str) +#define NXP_TLS_DEVICE_HS_TS_LABEL NXP_TLS_LABEL("D HS TS") enum nxp_tls_signature_algorithm { NXP_TLS_ECDSA_SECP256R1_SHA256 = 0x0403, @@ -440,6 +449,38 @@ union nxp_tls_host_hello_payload { u8 buf[113]; }; +struct nxp_tls_device_info { + __le16 chip_id; + __le16 device_flags; + u8 reserved[4]; + u8 uuid[NXP_DEVICE_UUID_LEN]; +}; + +struct nxp_tls_signature { + u8 sig[64]; /* P-256 ECDSA signature, two points */ +}; + +struct nxp_tls_finished { + u8 verify_data[32]; +}; + +struct nxp_tls_device_hello { + struct nxp_tls_message_hdr hdr; + __le32 reserved; + u8 random[32]; + u8 pubkey[NXP_TLS_ECDH_PUBLIC_KEY_SIZE]; + /* Encrypted portion */ + struct { + struct nxp_tls_device_info device_info; + struct nxp_tls_signature device_handshake_sig; /* TLS Certificate Verify */ + struct nxp_tls_finished device_finished; + } enc; + u8 auth_tag[NXP_ENC_AUTH_TAG_SIZE]; /* Auth tag for the encrypted portion */ +}; + +#define DEVICE_HELLO_SIG_CUTOFF_POS \ + offsetof(struct nxp_tls_device_hello, enc) + /* FW Meta Data */ struct fw_metadata_hdr { __le32 cmd; @@ -1698,7 +1739,7 @@ static int nxp_generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64 } static inline void nxp_tls_hdr_init(struct nxp_tls_message_hdr *hdr, size_t len, - enum nxp_tls_message_id id) + enum nxp_tls_message_id id) { hdr->magic = cpu_to_le32(NXP_TLS_MAGIC); hdr->len = cpu_to_le16((u16)len); @@ -1785,11 +1826,222 @@ static struct sk_buff *nxp_host_do_hello(struct hci_dev *hdev) return skb; } +static int nxp_crypto_shash_final(struct shash_desc *desc, u8 *out) +{ + struct shash_desc *desc_tmp = kzalloc(sizeof(struct shash_desc) + + crypto_shash_descsize(desc->tfm), + GFP_KERNEL); + + if (!desc_tmp) + return -ENOMEM; + + crypto_shash_export(desc, desc_tmp); + crypto_shash_final(desc, out); + crypto_shash_import(desc, desc_tmp); + kfree(desc_tmp); + + return 0; +} + +static int nxp_compute_shared_secret(struct crypto_kpp *tfm, const u8 public_key[64], u8 secret[32]) +{ + DECLARE_CRYPTO_WAIT(result); + struct kpp_request *req; + struct scatterlist src, dst; + int err; + + req = kpp_request_alloc(tfm, GFP_KERNEL); + if (!req) { + pr_err("Failed to allocate memory for KPP request\n"); + return -ENOMEM; + } + + sg_init_one(&src, public_key, 64); + sg_init_one(&dst, secret, 32); + kpp_request_set_input(req, &src, 64); + kpp_request_set_output(req, &dst, 32); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &result); + err = crypto_kpp_compute_shared_secret(req); + err = crypto_wait_req(err, &result); + if (err < 0) { + pr_err("alg: ecdh: compute shared secret failed. err %d\n", err); + goto free_all; + } + +free_all: + kpp_request_free(req); + return err; +} + +static int nxp_hkdf_sha256_extract(const void *salt, size_t salt_len, + const void *ikm, size_t ikm_len, + u8 result[SHA256_DIGEST_SIZE]) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + u8 zeroes[SHA256_DIGEST_SIZE] = {0}; + int ret = 0; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + + /* RFC 5869: If salt is empty, use HashLen zero octets */ + if (salt_len == 0) + ret = crypto_shash_setkey(tfm, zeroes, SHA256_DIGEST_SIZE); + else + ret = crypto_shash_setkey(tfm, salt, salt_len); + + if (ret) + goto cleanup; + + ret = crypto_shash_init(desc); + if (ret) + goto cleanup; + + ret = crypto_shash_update(desc, ikm, ikm_len); + if (ret) + goto cleanup; + + ret = crypto_shash_final(desc, result); + +cleanup: + kfree(desc); + crypto_free_shash(tfm); + return ret; +} + +static int nxp_hkdf_expand_label(const u8 secret[SHA256_DIGEST_SIZE], + const char *label, size_t label_size, + u8 *context, size_t context_size, + void *output, size_t output_size) +{ + struct crypto_shash *tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + struct shash_desc *desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), + GFP_KERNEL); + u8 hmac_out[SHA256_DIGEST_SIZE]; + u16 length = output_size; + u8 one = 0x01; + + if (IS_ERR(tfm)) { + pr_err("Failed to alloc shash for HMAC\n"); + return -ENOMEM; + } + + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + crypto_shash_setkey(tfm, secret, SHA256_DIGEST_SIZE); + desc->tfm = tfm; + + crypto_shash_init(desc); + crypto_shash_update(desc, (u8 *)&length, sizeof(length)); + crypto_shash_update(desc, label, label_size); + + if (context && context_size > 0) + crypto_shash_update(desc, context, context_size); + + /* RFC 5869: HKDF-Expand counter starts at 0x01 */ + crypto_shash_update(desc, &one, sizeof(one)); + crypto_shash_final(desc, hmac_out); + + memcpy(output, hmac_out, output_size); + + kfree(desc); + crypto_free_shash(tfm); + return 0; +} + +static int nxp_hkdf_derive_secret(u8 secret[32], const char *label, size_t label_size, + u8 context[SHA256_DIGEST_SIZE], + u8 output[SHA256_DIGEST_SIZE]) +{ + return nxp_hkdf_expand_label(secret, label, label_size, context, SHA256_DIGEST_SIZE, + output, SHA256_DIGEST_SIZE); +} + +static int nxp_process_device_hello(struct hci_dev *hdev, struct nxp_tls_device_hello *msg) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_tls_message_hdr *hdr; + u8 hs_traffic_secret[SHA256_DIGEST_SIZE]; + u8 *shared_secret = NULL; + int ret; + + if (!msg) + return -EINVAL; + + hdr = &msg->hdr; + + if (le32_to_cpu(hdr->magic) != NXP_TLS_MAGIC || + le16_to_cpu(hdr->len) != sizeof(*msg) || + hdr->message_id != NXP_TLS_DEVICE_HELLO || + hdr->protocol_version != NXP_TLS_VERSION) { + bt_dev_err(hdev, "Invalid device hello header"); + return -EINVAL; + } + + shared_secret = kzalloc(32, GFP_KERNEL); + if (!shared_secret) + return -ENOMEM; + + ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, (u8 *)msg, + DEVICE_HELLO_SIG_CUTOFF_POS); + if (ret) + goto fail; + + ret = nxp_crypto_shash_final(nxpdev->crypto.tls_handshake_hash_desc, + nxpdev->crypto.handshake_h2_hash); + if (ret) + goto fail; + + memcpy(nxpdev->crypto.ecdh_public, msg->pubkey, NXP_FW_ECDH_PUBKEY_SIZE); + + ret = nxp_compute_shared_secret(nxpdev->crypto.kpp, nxpdev->crypto.ecdh_public, + shared_secret); + if (ret) + goto fail; + + ret = nxp_hkdf_sha256_extract(NULL, 0, shared_secret, 32, + nxpdev->crypto.handshake_secret); + if (ret) + goto fail; + + ret = nxp_hkdf_derive_secret(nxpdev->crypto.handshake_secret, + NXP_TLS_DEVICE_HS_TS_LABEL, + nxpdev->crypto.handshake_h2_hash, + hs_traffic_secret); + if (ret) + goto fail; + + /* TODO: Verify Signature in Device Hello using ECDSA Public Key + * extracted from the FW metadata. + */ + +fail: + memset(shared_secret, 0, 32); + kfree(shared_secret); + return ret; +} + static int nxp_authenticate_device(struct hci_dev *hdev) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_tls_device_hello *device_hello; size_t desc_size = 0; struct sk_buff *skb; + u8 *status; int ret = 0; nxpdev->crypto.tls_handshake_hash_tfm = crypto_alloc_shash("sha256", 0, 0); @@ -1819,9 +2071,23 @@ static int nxp_authenticate_device(struct hci_dev *hdev) goto free_kpp; } + status = skb_pull_data(skb, 1); + if (*status) + goto free_skb; + + if (skb->len != sizeof(struct nxp_tls_device_hello)) { + bt_dev_err(hdev, "Invalid Device Hello Length: %d", skb->len); + goto free_skb; + } + + device_hello = skb_pull_data(skb, sizeof(*device_hello)); + ret = nxp_process_device_hello(hdev, device_hello); + if (ret) + goto free_skb; + /* TODO: Implement actual TLS handshake protocol * This will include: - * 1. Handle Device hello message exchange + * 1. Send Host Finish TLS message * 2. Master secret and traffic key derivation */ From 37dcb72652c183f7bd0c0fd17a0118067a969415 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:14 +0530 Subject: [PATCH 39/43] Bluetooth: btnxpuart: Add device authentication This implements secure device authentication during TLS 1.3-like handshake with ECDSA signature verification. The authentication flow: - Derive handshake traffic secret from ECDH shared secret - Decrypt device hello encrypted section using AES-GCM with traffic secret - Extract ECDSA public key from firmware metadata for verification - Verify device handshake signature to authenticate device identity - Validate device finished message using calculated verify data - Clear handshake traffic secret after successful authentication This ensures only devices with valid private keys can complete the handshake. Key components added: - AES-GCM encrypt/decrypt with traffic secret derived keys - ECDSA P-256 signature verification using kernel crypto API - X9.62 to P1363 signature format conversion - TLS 1.3 finished message verification - Secure memory cleanup of cryptographic material Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 504 +++++++++++++++++++++++++++++++++- 1 file changed, 499 insertions(+), 5 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 0e71f68a408ef..9ed4cece7e424 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -27,6 +27,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include @@ -204,11 +210,13 @@ struct btnxpuart_crypto { struct crypto_shash *tls_handshake_hash_tfm; struct shash_desc *tls_handshake_hash_desc; struct crypto_kpp *kpp; - uint8_t ecdh_public[NXP_FW_ECDH_PUBKEY_SIZE]; /* ECDH public key, Key negotiation */ + u8 ecdh_public[NXP_FW_ECDH_PUBKEY_SIZE]; /* ECDH public key, Key negotiation */ u8 ecdsa_public[NXP_FW_ECDSA_PUBKEY_SIZE]; /* ECDSA public key, Authentication*/ u8 fw_uuid[NXP_FW_UUID_SIZE]; u8 handshake_h2_hash[SHA256_DIGEST_SIZE]; u8 handshake_secret[SHA256_DIGEST_SIZE]; + struct completion completion; + int decrypt_result; }; struct btnxpuart_dev { @@ -405,6 +413,10 @@ union nxp_set_bd_addr_payload { #define NXP_TLS_LABEL(str) str, strlen(str) #define NXP_TLS_DEVICE_HS_TS_LABEL NXP_TLS_LABEL("D HS TS") +#define NXP_TLS_KEYING_IV_LABEL NXP_TLS_LABEL("iv") +#define NXP_TLS_KEYING_KEY_LABEL NXP_TLS_LABEL("key") +#define NXP_TLS_FINISHED_LABEL NXP_TLS_LABEL("finished") +#define NXP_TLS_HOST_HS_TS_LABEL NXP_TLS_LABEL("H HS TS") enum nxp_tls_signature_algorithm { NXP_TLS_ECDSA_SECP256R1_SHA256 = 0x0403, @@ -478,9 +490,42 @@ struct nxp_tls_device_hello { u8 auth_tag[NXP_ENC_AUTH_TAG_SIZE]; /* Auth tag for the encrypted portion */ }; +struct nxp_tls_data_add { + u8 version; /* NXP_TLS_VERSION */ + u8 reserved[5]; /* zeroes */ + __le16 len; +}; + +struct nxp_tls_host_finished { + struct nxp_tls_message_hdr hdr; + __le32 reserved; + /* Encrypted portion */ + struct { + struct nxp_tls_signature reserved2; + struct nxp_tls_finished host_finished; + } enc; + u8 auth_tag[NXP_ENC_AUTH_TAG_SIZE]; /* Auth tag for the encrypted portion */ +}; + +union nxp_tls_host_finished_payload { + struct { + u8 msg_type; + struct nxp_tls_host_finished host_finished; + } __packed; + u8 buf[125]; +}; + #define DEVICE_HELLO_SIG_CUTOFF_POS \ offsetof(struct nxp_tls_device_hello, enc) +#define DEVICE_HELLO_FINISHED_ENC_CUTOFF_POS \ + (offsetof(struct nxp_tls_device_hello, enc.device_finished) - \ + DEVICE_HELLO_SIG_CUTOFF_POS) + + +#define HOST_FINISHED_CUTOFF_POS \ + offsetof(struct nxp_tls_host_finished, enc.host_finished) + /* FW Meta Data */ struct fw_metadata_hdr { __le32 cmd; @@ -1700,6 +1745,38 @@ static void nxp_get_fw_version(struct hci_dev *hdev) } /* Secure Interface */ +static int nxp_get_pub_key(struct hci_dev *hdev, + const struct nxp_tls_device_info *device_info, + u8 ecdsa_pub_key[NXP_FW_ECDSA_PUBKEY_SIZE]) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + const char *fw_name; + + if (ecdsa_pub_key[0] == 0x04) + return 0; + + fw_name = nxp_get_fw_name_from_chipid(hdev, + le16_to_cpu(device_info->chip_id), + le16_to_cpu(device_info->device_flags)); + if (nxp_request_firmware(hdev, fw_name, NULL)) + return -ENOENT; + + nxp_process_fw_meta_data(hdev, nxpdev->fw); + release_firmware(nxpdev->fw); + memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name)); + + if (memcmp(nxpdev->crypto.fw_uuid, device_info->uuid, 16) || + nxpdev->crypto.ecdsa_public[0] != 0x04) { + bt_dev_err(hdev, + "UUID check failed while trying to read ECDSA public key from FW."); + return -EBADF; + } + + memcpy(ecdsa_pub_key, nxpdev->crypto.ecdsa_public, 65); + + return 0; +} + static int nxp_generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64]) { DECLARE_CRYPTO_WAIT(result); @@ -1971,6 +2048,320 @@ static int nxp_hkdf_derive_secret(u8 secret[32], const char *label, size_t label output, SHA256_DIGEST_SIZE); } +/* + * The digital signature is computed over the concatenation of: + * - A string that consists of octet 32 (0x20) repeated 64 times + * - The context string + * - A single 0 byte which serves as the separator + * - The content to be signed + */ +static int nxp_handshake_sig_hash(const u8 transcript_hash[SHA256_DIGEST_SIZE], + const char *context, size_t context_len, + u8 output_hash[SHA256_DIGEST_SIZE]) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + const u8 zero = 0; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + + memset(output_hash, 0x20, SHA256_DIGEST_SIZE); + + crypto_shash_init(desc); + /* 2x hash size = block size of 0x20 */ + crypto_shash_update(desc, output_hash, SHA256_DIGEST_SIZE); + crypto_shash_update(desc, output_hash, SHA256_DIGEST_SIZE); + + crypto_shash_update(desc, context, context_len); + crypto_shash_update(desc, &zero, sizeof(zero)); + + crypto_shash_update(desc, transcript_hash, SHA256_DIGEST_SIZE); + crypto_shash_final(desc, output_hash); + + kfree(desc); + crypto_free_shash(tfm); + return 0; +} + + +static void nxp_aead_complete(void *req, int err) +{ + struct btnxpuart_crypto *crypto = req; + + crypto->decrypt_result = err; + complete(&crypto->completion); +} + +static int nxp_aes_gcm_decrypt(struct hci_dev *hdev, void *buf, size_t size, + u8 auth_tag[16], u8 key[AES_KEYSIZE_128], + u8 iv[GCM_AES_IV_SIZE]) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct crypto_aead *tfm; + struct aead_request *req; + struct scatterlist src, dst; + struct nxp_tls_data_add aad = { + .version = NXP_TLS_VERSION, + .len = cpu_to_le16((u16)size) + }; + u8 *ciphertext; + u8 *plaintext; + int ret = 0; + + ciphertext = kzalloc(sizeof(aad) + size + NXP_ENC_AUTH_TAG_SIZE, + GFP_KERNEL); + if (!ciphertext) + return -ENOMEM; + + plaintext = kzalloc(size + NXP_ENC_AUTH_TAG_SIZE, GFP_KERNEL); + if (!plaintext) { + ret = -ENOMEM; + goto free_ciphertext; + } + + memcpy(ciphertext, &aad, sizeof(aad)); + memcpy(ciphertext + sizeof(aad), buf, size); + memcpy(ciphertext + sizeof(aad) + size, auth_tag, NXP_ENC_AUTH_TAG_SIZE); + + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto free_plaintext; + } + + crypto_aead_setkey(tfm, key, AES_KEYSIZE_128); + crypto_aead_setauthsize(tfm, NXP_ENC_AUTH_TAG_SIZE); + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto free_tfm; + } + + sg_init_one(&src, ciphertext, sizeof(aad) + size + NXP_ENC_AUTH_TAG_SIZE); + sg_init_one(&dst, plaintext, size + NXP_ENC_AUTH_TAG_SIZE); + init_completion(&nxpdev->crypto.completion); + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + nxp_aead_complete, &nxpdev->crypto); + aead_request_set_crypt(req, &src, &dst, size + NXP_ENC_AUTH_TAG_SIZE, iv); + aead_request_set_ad(req, sizeof(aad)); + + ret = crypto_aead_decrypt(req); + if (ret == -EINPROGRESS || ret == -EBUSY) { + wait_for_completion(&nxpdev->crypto.completion); + ret = nxpdev->crypto.decrypt_result; + } + if (!ret) + memcpy(buf, plaintext + sizeof(aad), size); + + aead_request_free(req); +free_tfm: + crypto_free_aead(tfm); +free_plaintext: + kfree(plaintext); +free_ciphertext: + kfree(ciphertext); + return ret; +} + +static int nxp_aes_gcm_encrypt(struct hci_dev *hdev, void *buf, size_t size, u8 auth_tag[16], + u8 key[AES_KEYSIZE_128], u8 iv[GCM_AES_IV_SIZE]) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct crypto_aead *tfm; + struct aead_request *req; + struct scatterlist src, dst; + struct nxp_tls_data_add aad = { + .version = NXP_TLS_VERSION, + .len = cpu_to_le16((u16)size) + }; + u8 *ciphertext; + u8 *plaintext; + int ret = 0; + + ciphertext = kzalloc(sizeof(aad) + size + NXP_ENC_AUTH_TAG_SIZE, + GFP_KERNEL); + if (!ciphertext) + return -ENOMEM; + + plaintext = kzalloc(size + NXP_ENC_AUTH_TAG_SIZE, GFP_KERNEL); + if (!plaintext) { + ret = -ENOMEM; + goto free_ciphertext; + } + + memcpy(plaintext, &aad, sizeof(aad)); + memcpy(plaintext + sizeof(aad), buf, size); + + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto free_plaintext; + } + + crypto_aead_setkey(tfm, key, AES_KEYSIZE_128); + crypto_aead_setauthsize(tfm, NXP_ENC_AUTH_TAG_SIZE); + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto free_tfm; + } + + sg_init_one(&src, plaintext, size + NXP_ENC_AUTH_TAG_SIZE); + sg_init_one(&dst, ciphertext, sizeof(aad) + size + NXP_ENC_AUTH_TAG_SIZE); + init_completion(&nxpdev->crypto.completion); + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + nxp_aead_complete, &nxpdev->crypto); + aead_request_set_crypt(req, &src, &dst, size, iv); + aead_request_set_ad(req, sizeof(aad)); + + ret = crypto_aead_encrypt(req); + if (ret == -EINPROGRESS || ret == -EBUSY) { + wait_for_completion(&nxpdev->crypto.completion); + ret = nxpdev->crypto.decrypt_result; + } + if (!ret) { + memcpy(buf, ciphertext + sizeof(aad), size); + memcpy(auth_tag, ciphertext + size + sizeof(aad), NXP_ENC_AUTH_TAG_SIZE); + } + + aead_request_free(req); +free_tfm: + crypto_free_aead(tfm); +free_plaintext: + kfree(plaintext); +free_ciphertext: + kfree(ciphertext); + return ret; +} + +static int nxp_handshake_decrypt_verify(struct hci_dev *hdev, void *buf, size_t size, + u8 auth_tag[16], + u8 traffic_secret[SHA256_DIGEST_SIZE]) +{ + u8 key[AES_KEYSIZE_128] = {0}; + u8 iv[GCM_AES_IV_SIZE] = {0}; + + nxp_hkdf_expand_label(traffic_secret, NXP_TLS_KEYING_KEY_LABEL, NULL, 0, + key, AES_KEYSIZE_128); + nxp_hkdf_expand_label(traffic_secret, NXP_TLS_KEYING_IV_LABEL, NULL, 0, + iv, GCM_AES_IV_SIZE); + + return nxp_aes_gcm_decrypt(hdev, buf, size, auth_tag, key, iv); +} + +static int nxp_handshake_encrypt(struct hci_dev *hdev, void *buf, + size_t size, u8 auth_tag[16], + u8 traffic_secret[SHA256_DIGEST_SIZE]) +{ + u8 key[AES_KEYSIZE_128] = {0}; + u8 iv[GCM_AES_IV_SIZE] = {0}; + + nxp_hkdf_expand_label(traffic_secret, NXP_TLS_KEYING_KEY_LABEL, NULL, + 0, key, AES_KEYSIZE_128); + nxp_hkdf_expand_label(traffic_secret, NXP_TLS_KEYING_IV_LABEL, NULL, + 0, iv, GCM_AES_IV_SIZE); + + return nxp_aes_gcm_encrypt(hdev, buf, size, auth_tag, key, iv); +} + +static int nxp_p256_ecdsa_verify(const u8 sig[64], const u8 pub[65], + const u8 *hash, size_t hash_len) +{ + struct public_key_signature sig_info = {0}; + struct public_key pub_key = {0}; + int ret; + + sig_info.s = (u8 *)sig; + sig_info.s_size = 64; + sig_info.digest = (u8 *)hash; + sig_info.digest_size = hash_len; + sig_info.pkey_algo = "ecdsa"; + sig_info.hash_algo = "sha256"; + sig_info.encoding = "p1363"; + + pub_key.key = (void *)pub; + pub_key.keylen = 65; + pub_key.algo = OID_id_ecPublicKey; + pub_key.key_is_private = false; + pub_key.pkey_algo = "ecdsa-nist-p256"; + pub_key.id_type = NULL; + + ret = public_key_verify_signature(&pub_key, &sig_info); + if (ret) + pr_err("ECDSA signature verification failed: %d\n", ret); + + return ret; +} + +static int nxp_device_hello_sig_verify(struct hci_dev *hdev, struct nxp_tls_device_hello *msg) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + u8 hash_sig[SHA256_DIGEST_SIZE]; + + nxp_handshake_sig_hash(nxpdev->crypto.handshake_h2_hash, + "D HS SIG", 8, hash_sig); + return nxp_p256_ecdsa_verify(msg->enc.device_handshake_sig.sig, + nxpdev->crypto.ecdsa_public, + hash_sig, SHA256_DIGEST_SIZE); +} + +static int nxp_write_finished(struct hci_dev *hdev, + const u8 hs_traffic_secret[SHA256_DIGEST_SIZE], + u8 verify_data[SHA256_DIGEST_SIZE]) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + u8 transcript_hash[SHA256_DIGEST_SIZE]; + u8 finished_key[SHA256_DIGEST_SIZE]; + int ret = 0; + + ret = nxp_crypto_shash_final(nxpdev->crypto.tls_handshake_hash_desc, + transcript_hash); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(hs_traffic_secret, NXP_TLS_FINISHED_LABEL, + NULL, 0, finished_key, sizeof(finished_key)); + if (ret) + return ret; + + nxp_hkdf_sha256_extract(finished_key, SHA256_DIGEST_SIZE, transcript_hash, + SHA256_DIGEST_SIZE, verify_data); + + return 0; +} + +static int nxp_verify_device_finished(struct hci_dev *hdev, + struct nxp_tls_device_hello *msg, + const u8 hs_traffic_secret[SHA256_DIGEST_SIZE]) +{ + u8 verify_data[SHA256_DIGEST_SIZE] = {0}; + int ret = 0; + + ret = nxp_write_finished(hdev, hs_traffic_secret, verify_data); + if (ret) + return ret; + + if (memcmp(verify_data, msg->enc.device_finished.verify_data, + SHA256_DIGEST_SIZE)) + return -EBADMSG; + + return 0; +} + static int nxp_process_device_hello(struct hci_dev *hdev, struct nxp_tls_device_hello *msg) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); @@ -2025,9 +2416,51 @@ static int nxp_process_device_hello(struct hci_dev *hdev, struct nxp_tls_device_ if (ret) goto fail; - /* TODO: Verify Signature in Device Hello using ECDSA Public Key - * extracted from the FW metadata. + ret = nxp_handshake_decrypt_verify(hdev, &msg->enc, sizeof(msg->enc), + msg->auth_tag, hs_traffic_secret); + if (ret) + goto fail; + + /* + * Verify ECDSA signature handshake_sig using Device's public key from FW metadata. + * + * This is the key point where Device authentication happens: + * - Host generates a random (HostHello.random) + * - Device signs the entire handshake (incl. Host's random) with its + * private key (DeviceHello.device_handshake_sig) + * - Host now verifies ECDSA signature generated by device using Device's + * public key + * + * Only the device that possesses the proper private key could sign the + * Host's random. + * If the device is an impostor and does not pose a valid private key, + * the handshake will fail at this point. */ + ret = nxp_get_pub_key(hdev, &msg->enc.device_info, nxpdev->crypto.ecdsa_public); + if (ret) + goto fail; + + ret = nxp_device_hello_sig_verify(hdev, msg); + if (ret) + goto fail; + + ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)&msg->enc, + DEVICE_HELLO_FINISHED_ENC_CUTOFF_POS); + if (ret) + goto fail; + + ret = nxp_verify_device_finished(hdev, msg, hs_traffic_secret); + if (ret) + goto fail; + + ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)&msg->enc.device_finished, + sizeof(msg->enc.device_finished)); + if (ret) + goto fail; + + memset(hs_traffic_secret, 0, SHA256_DIGEST_SIZE); fail: memset(shared_secret, 0, 32); @@ -2035,6 +2468,64 @@ static int nxp_process_device_hello(struct hci_dev *hdev, struct nxp_tls_device_ return ret; } +static int nxp_host_do_finished(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + union nxp_tls_host_finished_payload finished; + struct nxp_tls_host_finished *msg = &finished.host_finished; + u8 hs_traffic_secret[SHA256_DIGEST_SIZE]; + struct sk_buff *skb; + u8 *status; + int ret = 0; + + memset(msg, 0, sizeof(*msg)); + nxp_tls_hdr_init(&msg->hdr, sizeof(*msg), NXP_TLS_HOST_FINISHED); + + crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)msg, HOST_FINISHED_CUTOFF_POS); + + ret = nxp_hkdf_derive_secret(nxpdev->crypto.handshake_secret, + NXP_TLS_HOST_HS_TS_LABEL, + nxpdev->crypto.handshake_h2_hash, + hs_traffic_secret); + if (ret) + return ret; + + ret = nxp_write_finished(hdev, hs_traffic_secret, + msg->enc.host_finished.verify_data); + if (ret) + return ret; + + crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)&msg->enc.host_finished, sizeof(msg->enc.host_finished)); + + nxp_handshake_encrypt(hdev, &msg->enc, sizeof(msg->enc), + msg->auth_tag, hs_traffic_secret); + + finished.msg_type = 0x01; + + skb = __hci_cmd_sync(hdev, HCI_NXP_SHI_ENCRYPT, + sizeof(finished), finished.buf, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Host Finished error %ld", PTR_ERR(skb)); + return PTR_ERR(skb); + } + status = skb_pull_data(skb, 1); + if (!status) { + ret = -EIO; + goto fail; + } + if (*status) { + ret = -EIO; + bt_dev_err(hdev, "Host Finished status error: %d", *status); + } + +fail: + kfree_skb(skb); + return ret; +} + static int nxp_authenticate_device(struct hci_dev *hdev) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); @@ -2085,10 +2576,13 @@ static int nxp_authenticate_device(struct hci_dev *hdev) if (ret) goto free_skb; + ret = nxp_host_do_finished(hdev); + if (ret) + goto free_skb; + /* TODO: Implement actual TLS handshake protocol * This will include: - * 1. Send Host Finish TLS message - * 2. Master secret and traffic key derivation + * 1. Master secret and traffic key derivation */ free_skb: From 5df6aeccea849cd96e5393e843dc74f74e6fa795 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:15 +0530 Subject: [PATCH 40/43] Bluetooth: btnxpuart: Derive traffic keys from TLS 1.3 handshake This completes the TLS handshake implementation by adding master secret derivation and traffic key generation. These traffic keys will be used to encrypt/decrypt sensitive HCI commands, response and events. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 88 +++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 9ed4cece7e424..cabed02e09645 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -206,6 +206,16 @@ enum bootloader_param_change { changed }; +struct nxp_tls_traffic_keys { + u8 h2d_secret[SHA256_DIGEST_SIZE]; + u8 d2h_secret[SHA256_DIGEST_SIZE]; + /* These keys below should be used for message encryption/decryption */ + u8 h2d_iv[GCM_AES_IV_SIZE]; + u8 h2d_key[AES_KEYSIZE_128]; + u8 d2h_iv[GCM_AES_IV_SIZE]; + u8 d2h_key[AES_KEYSIZE_128]; +}; + struct btnxpuart_crypto { struct crypto_shash *tls_handshake_hash_tfm; struct shash_desc *tls_handshake_hash_desc; @@ -215,8 +225,10 @@ struct btnxpuart_crypto { u8 fw_uuid[NXP_FW_UUID_SIZE]; u8 handshake_h2_hash[SHA256_DIGEST_SIZE]; u8 handshake_secret[SHA256_DIGEST_SIZE]; + u8 master_secret[SHA256_DIGEST_SIZE]; struct completion completion; int decrypt_result; + struct nxp_tls_traffic_keys keys; }; struct btnxpuart_dev { @@ -416,7 +428,10 @@ union nxp_set_bd_addr_payload { #define NXP_TLS_KEYING_IV_LABEL NXP_TLS_LABEL("iv") #define NXP_TLS_KEYING_KEY_LABEL NXP_TLS_LABEL("key") #define NXP_TLS_FINISHED_LABEL NXP_TLS_LABEL("finished") +#define NXP_TLS_DERIVED_LABEL NXP_TLS_LABEL("derived") #define NXP_TLS_HOST_HS_TS_LABEL NXP_TLS_LABEL("H HS TS") +#define NXP_TLS_D_AP_TS_LABEL NXP_TLS_LABEL("D AP TS") +#define NXP_TLS_H_AP_TS_LABEL NXP_TLS_LABEL("H AP TS") enum nxp_tls_signature_algorithm { NXP_TLS_ECDSA_SECP256R1_SHA256 = 0x0403, @@ -2526,6 +2541,71 @@ static int nxp_host_do_finished(struct hci_dev *hdev) return ret; } +static void nxp_handshake_derive_master_secret(u8 master_secret[SHA256_DIGEST_SIZE], + u8 handshake_secret[SHA256_DIGEST_SIZE]) +{ + u8 zeros[SHA256_DIGEST_SIZE] = {0}; + u8 dhs[SHA256_DIGEST_SIZE]; + + /* Derive intermediate secret */ + nxp_hkdf_expand_label(handshake_secret, NXP_TLS_DERIVED_LABEL, + NULL, 0, dhs, sizeof(dhs)); + /* Extract master secret from derived handshake secret */ + nxp_hkdf_sha256_extract(dhs, SHA256_DIGEST_SIZE, zeros, + sizeof(zeros), master_secret); + + memset(dhs, 0, sizeof(dhs)); +} + +static int nxp_handshake_derive_traffic_keys(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_tls_traffic_keys *keys = &nxpdev->crypto.keys; + u8 hash[SHA256_DIGEST_SIZE]; + int ret = 0; + + ret = crypto_shash_final(nxpdev->crypto.tls_handshake_hash_desc, hash); + if (ret) + return ret; + + ret = nxp_hkdf_derive_secret(nxpdev->crypto.master_secret, + NXP_TLS_D_AP_TS_LABEL, hash, keys->d2h_secret); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(keys->d2h_secret, + NXP_TLS_KEYING_KEY_LABEL, NULL, 0, + keys->d2h_key, AES_KEYSIZE_128); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(keys->d2h_secret, + NXP_TLS_KEYING_IV_LABEL, NULL, 0, + keys->d2h_iv, GCM_AES_IV_SIZE); + if (ret) + return ret; + + ret = nxp_hkdf_derive_secret(nxpdev->crypto.master_secret, + NXP_TLS_H_AP_TS_LABEL, hash, keys->h2d_secret); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(keys->h2d_secret, + NXP_TLS_KEYING_KEY_LABEL, NULL, 0, + keys->h2d_key, AES_KEYSIZE_128); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(keys->h2d_secret, + NXP_TLS_KEYING_IV_LABEL, NULL, 0, + keys->h2d_iv, GCM_AES_IV_SIZE); + if (ret) + return ret; + + memset(hash, 0, sizeof(hash)); + return ret; +} + static int nxp_authenticate_device(struct hci_dev *hdev) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); @@ -2580,10 +2660,10 @@ static int nxp_authenticate_device(struct hci_dev *hdev) if (ret) goto free_skb; - /* TODO: Implement actual TLS handshake protocol - * This will include: - * 1. Master secret and traffic key derivation - */ + nxp_handshake_derive_master_secret(nxpdev->crypto.master_secret, + nxpdev->crypto.handshake_secret); + + nxp_handshake_derive_traffic_keys(hdev); free_skb: kfree_skb(skb); From 82df2634be928a906bc7ff0d1a7c849a0dc6cb39 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:16 +0530 Subject: [PATCH 41/43] Bluetooth: btnxpuart: Add command encryption for sensitive HCI commands This adds support for command encryption for sensitive HCI commands when secure interface is enabled. This commands containt sensitive data such as Link Key in plain text over UART lines. AES-GCM encryption is used to encrypt sensitive commands using encryption key and IV derived from traffic keys. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index cabed02e09645..e2be9012ef589 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -159,6 +159,7 @@ #define NXP_FW_UUID_SIZE 16 #define NXP_FW_ECDH_PUBKEY_SIZE 64 #define NXP_FW_ECDSA_PUBKEY_SIZE 65 +#define NXP_MAX_ENCRYPT_CMD_LEN 256 struct ps_data { u8 target_ps_mode; /* ps mode to be set */ @@ -226,6 +227,7 @@ struct btnxpuart_crypto { u8 handshake_h2_hash[SHA256_DIGEST_SIZE]; u8 handshake_secret[SHA256_DIGEST_SIZE]; u8 master_secret[SHA256_DIGEST_SIZE]; + u64 enc_seq_no; struct completion completion; int decrypt_result; struct nxp_tls_traffic_keys keys; @@ -2682,6 +2684,71 @@ static int nxp_authenticate_device(struct hci_dev *hdev) return ret; } +static void nxp_data_calc_nonce(u8 iv[GCM_AES_IV_SIZE], u64 seq_no, + u8 nonce[GCM_AES_IV_SIZE]) +{ + u64 tmp; + + /* XOR sequence number with IV to create unique nonce */ + memcpy(&tmp, iv, sizeof(tmp)); + tmp ^= seq_no; + memcpy(nonce, &tmp, sizeof(tmp)); + memcpy(nonce + sizeof(tmp), iv + sizeof(tmp), + GCM_AES_IV_SIZE - sizeof(tmp)); +} + +static struct sk_buff *nxp_crypto_encrypt_cmd(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + __le16 vendor_opcode = __cpu_to_le16(HCI_NXP_SHI_ENCRYPT); + u8 nonce[GCM_AES_IV_SIZE]; + u8 tag[NXP_ENC_AUTH_TAG_SIZE]; + u8 *enc_data; + u8 sub_opcode = 0x10; + int ret; + u32 plen, enc_data_len; + struct nxp_tls_traffic_keys *keys = &nxpdev->crypto.keys; + + if (skb->len > NXP_MAX_ENCRYPT_CMD_LEN) { + bt_dev_err(hdev, "Invalid skb->len: %d", skb->len); + return skb; + } + + nxp_data_calc_nonce(keys->h2d_iv, nxpdev->crypto.enc_seq_no, nonce); + + enc_data_len = skb->len; + enc_data = kmemdup(skb->data, skb->len, GFP_KERNEL); + if (!enc_data) + return skb; + + ret = nxp_aes_gcm_encrypt(hdev, enc_data, enc_data_len, tag, + keys->h2d_key, nonce); + if (ret) { + kfree(enc_data); + return skb; + } + + kfree_skb(skb); + + plen = enc_data_len + NXP_ENC_AUTH_TAG_SIZE + 1; + skb = bt_skb_alloc(plen, GFP_ATOMIC); + if (!skb) { + kfree(enc_data); + return ERR_PTR(-ENOMEM); + } + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; + skb_put_data(skb, &vendor_opcode, 2); + skb_put_data(skb, &plen, 1); + skb_put_data(skb, &sub_opcode, 1); + skb_put_data(skb, enc_data, enc_data_len); + skb_put_data(skb, tag, NXP_ENC_AUTH_TAG_SIZE); + + nxpdev->crypto.enc_seq_no++; + kfree(enc_data); + return skb; +} + /* NXP protocol */ static int nxp_setup(struct hci_dev *hdev) { @@ -2885,6 +2952,20 @@ static int nxp_enqueue(struct hci_dev *hdev, struct sk_buff *skb) goto free_skb; } break; + case HCI_OP_LINK_KEY_REPLY: + case HCI_OP_LE_START_ENC: + case HCI_OP_LE_LTK_REPLY: + case HCI_OP_LE_ADD_TO_RESOLV_LIST: + if (nxpdev->secure_interface) { + /* Re-alloc skb and encrypt sensitive command + * and payload. Command complete event + * won't be encrypted. + */ + skb = nxp_crypto_encrypt_cmd(hdev, skb); + if (IS_ERR(skb)) + return PTR_ERR(skb); + } + break; default: break; } From a565f7030781d61926d31ccd84920c36f7239a35 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:17 +0530 Subject: [PATCH 42/43] Bluetooth: btnxpuart: Add encrypted event handling This adds support for receiving and decrypting vendor events from secure NXP chip and forwarding the decrypted sensitive HCI events to the BT stack. The NXP BT chip encrypts the Link Key Notification event that is usually sent in plaintext over UART lines. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/btnxpuart.c | 99 ++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index e2be9012ef589..115f727c2572b 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -228,6 +228,7 @@ struct btnxpuart_crypto { u8 handshake_secret[SHA256_DIGEST_SIZE]; u8 master_secret[SHA256_DIGEST_SIZE]; u64 enc_seq_no; + u64 dec_seq_no; struct completion completion; int decrypt_result; struct nxp_tls_traffic_keys keys; @@ -2749,6 +2750,102 @@ static struct sk_buff *nxp_crypto_encrypt_cmd(struct hci_dev *hdev, return skb; } +static int nxp_crypto_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int ciphertext_size; + u8 *ciphertext; + u8 aes_gcm_tag[NXP_ENC_AUTH_TAG_SIZE]; + u8 nonce[GCM_AES_IV_SIZE]; + int ret; + struct sk_buff *event_skb; + struct nxp_tls_traffic_keys *keys = &nxpdev->crypto.keys; + + if (skb->len < NXP_ENC_AUTH_TAG_SIZE) { + bt_dev_err(hdev, "Encrypted event too short: %d", skb->len); + return -EINVAL; + } + ciphertext_size = skb->len - NXP_ENC_AUTH_TAG_SIZE; + ciphertext = kzalloc(ciphertext_size, GFP_KERNEL); + if (!ciphertext) + return -ENOMEM; + + memcpy(ciphertext, skb->data, ciphertext_size); + memcpy(aes_gcm_tag, skb->data + ciphertext_size, NXP_ENC_AUTH_TAG_SIZE); + + nxp_data_calc_nonce(keys->d2h_iv, nxpdev->crypto.dec_seq_no, nonce); + + ret = nxp_aes_gcm_decrypt(hdev, ciphertext, ciphertext_size, + aes_gcm_tag, keys->d2h_key, nonce); + if (ret) { + kfree(ciphertext); + return ret; + } + + event_skb = bt_skb_alloc(ciphertext_size, GFP_ATOMIC); + if (!event_skb) { + kfree(ciphertext); + return -ENOMEM; + } + + hci_skb_pkt_type(event_skb) = HCI_EVENT_PKT; + skb_put_data(event_skb, ciphertext, ciphertext_size); + + nxpdev->crypto.dec_seq_no++; + + kfree(ciphertext); + + /* Inject Decrypted Event to upper stack */ + return hci_recv_frame(hdev, event_skb); +} + +static int nxp_process_vendor_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct hci_event_hdr *vendor_event_hdr; + u8 *vendor_sub_event; + + vendor_event_hdr = (struct hci_event_hdr *)skb_pull_data(skb, + sizeof(*vendor_event_hdr)); + if (!vendor_event_hdr) + goto free_skb; + + if (!vendor_event_hdr->plen) + goto free_skb; + + vendor_sub_event = skb_pull_data(skb, 1); + if (!vendor_sub_event) + goto free_skb; + + switch (*vendor_sub_event) { + case 0x23: + break; // Power Save Enable/Disable vendor response. Can be ignored. + case 0xe3: + if (nxpdev->secure_interface) + nxp_crypto_event(hdev, skb); + else + bt_dev_warn(hdev, "Unexpected encrypted event"); + break; + default: + bt_dev_err(hdev, "Unknown vendor event subtype: %d", *vendor_sub_event); + break; + } + +free_skb: + kfree_skb(skb); + return 0; +} + +static int nxp_recv_event_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ + u8 event = hci_event_hdr(skb)->evt; + + if (event == 0xff) + return nxp_process_vendor_event(hdev, skb); + else + return hci_recv_frame(hdev, skb); +} + /* NXP protocol */ static int nxp_setup(struct hci_dev *hdev) { @@ -3076,7 +3173,7 @@ static int btnxpuart_flush(struct hci_dev *hdev) static const struct h4_recv_pkt nxp_recv_pkts[] = { { H4_RECV_ACL, .recv = nxp_recv_acl_pkt }, { H4_RECV_SCO, .recv = hci_recv_frame }, - { H4_RECV_EVENT, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = nxp_recv_event_frame }, { H4_RECV_ISO, .recv = hci_recv_frame }, { NXP_RECV_CHIP_VER_V1, .recv = nxp_recv_chip_ver_v1 }, { NXP_RECV_FW_REQ_V1, .recv = nxp_recv_fw_req_v1 }, From 5b6018b75e79b548b47362a64f02eb1188c2bbf6 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Tue, 13 Jan 2026 13:17:18 +0530 Subject: [PATCH 43/43] Bluetooth: btnxpuart: Select crypto algorithms for secure interface This adds crypto dependencies (ECDSA, ECDH, AES-GCM, SHA256, HMAC) needed for TLS-based secure host interface authentication and encryption. Signed-off-by: Neeraj Sanjay Kale --- drivers/bluetooth/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index c5d45cf91f882..ccbd2e13977e9 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -493,6 +493,13 @@ config BT_NXPUART select BT_HCIUART_H4 select CRC32 select CRC8 + select CRYPTO + select CRYPTO_ECDSA + select CRYPTO_ECDH + select CRYPTO_AES + select CRYPTO_GCM + select CRYPTO_SHA256 + select CRYPTO_HMAC help NXP is serial driver required for NXP Bluetooth devices with UART interface.