From a80d2c545ded86d0350b9a870735565d8b749786 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 13 Feb 2023 14:28:55 -0800 Subject: [PATCH 01/72] Bluetooth: MGMT: Use BIT macro when defining bitfields This makes use of BIT macro when defining bitfields which makes it clearer what bit it is toggling. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/mgmt.h | 80 ++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index e18a927669c0a..a5801649f6196 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -91,26 +91,26 @@ struct mgmt_rp_read_index_list { #define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1) #define MGMT_MAX_SHORT_NAME_LENGTH (HCI_MAX_SHORT_NAME_LENGTH + 1) -#define MGMT_SETTING_POWERED 0x00000001 -#define MGMT_SETTING_CONNECTABLE 0x00000002 -#define MGMT_SETTING_FAST_CONNECTABLE 0x00000004 -#define MGMT_SETTING_DISCOVERABLE 0x00000008 -#define MGMT_SETTING_BONDABLE 0x00000010 -#define MGMT_SETTING_LINK_SECURITY 0x00000020 -#define MGMT_SETTING_SSP 0x00000040 -#define MGMT_SETTING_BREDR 0x00000080 -#define MGMT_SETTING_HS 0x00000100 -#define MGMT_SETTING_LE 0x00000200 -#define MGMT_SETTING_ADVERTISING 0x00000400 -#define MGMT_SETTING_SECURE_CONN 0x00000800 -#define MGMT_SETTING_DEBUG_KEYS 0x00001000 -#define MGMT_SETTING_PRIVACY 0x00002000 -#define MGMT_SETTING_CONFIGURATION 0x00004000 -#define MGMT_SETTING_STATIC_ADDRESS 0x00008000 -#define MGMT_SETTING_PHY_CONFIGURATION 0x00010000 -#define MGMT_SETTING_WIDEBAND_SPEECH 0x00020000 -#define MGMT_SETTING_CIS_CENTRAL 0x00040000 -#define MGMT_SETTING_CIS_PERIPHERAL 0x00080000 +#define MGMT_SETTING_POWERED BIT(0) +#define MGMT_SETTING_CONNECTABLE BIT(1) +#define MGMT_SETTING_FAST_CONNECTABLE BIT(2) +#define MGMT_SETTING_DISCOVERABLE BIT(3) +#define MGMT_SETTING_BONDABLE BIT(4) +#define MGMT_SETTING_LINK_SECURITY BIT(5) +#define MGMT_SETTING_SSP BIT(6) +#define MGMT_SETTING_BREDR BIT(7) +#define MGMT_SETTING_HS BIT(8) +#define MGMT_SETTING_LE BIT(9) +#define MGMT_SETTING_ADVERTISING BIT(10) +#define MGMT_SETTING_SECURE_CONN BIT(11) +#define MGMT_SETTING_DEBUG_KEYS BIT(12) +#define MGMT_SETTING_PRIVACY BIT(13) +#define MGMT_SETTING_CONFIGURATION BIT(14) +#define MGMT_SETTING_STATIC_ADDRESS BIT(15) +#define MGMT_SETTING_PHY_CONFIGURATION BIT(16) +#define MGMT_SETTING_WIDEBAND_SPEECH BIT(17) +#define MGMT_SETTING_CIS_CENTRAL BIT(18) +#define MGMT_SETTING_CIS_PERIPHERAL BIT(19) #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 @@ -635,21 +635,21 @@ struct mgmt_rp_get_phy_configuration { } __packed; #define MGMT_GET_PHY_CONFIGURATION_SIZE 0 -#define MGMT_PHY_BR_1M_1SLOT 0x00000001 -#define MGMT_PHY_BR_1M_3SLOT 0x00000002 -#define MGMT_PHY_BR_1M_5SLOT 0x00000004 -#define MGMT_PHY_EDR_2M_1SLOT 0x00000008 -#define MGMT_PHY_EDR_2M_3SLOT 0x00000010 -#define MGMT_PHY_EDR_2M_5SLOT 0x00000020 -#define MGMT_PHY_EDR_3M_1SLOT 0x00000040 -#define MGMT_PHY_EDR_3M_3SLOT 0x00000080 -#define MGMT_PHY_EDR_3M_5SLOT 0x00000100 -#define MGMT_PHY_LE_1M_TX 0x00000200 -#define MGMT_PHY_LE_1M_RX 0x00000400 -#define MGMT_PHY_LE_2M_TX 0x00000800 -#define MGMT_PHY_LE_2M_RX 0x00001000 -#define MGMT_PHY_LE_CODED_TX 0x00002000 -#define MGMT_PHY_LE_CODED_RX 0x00004000 +#define MGMT_PHY_BR_1M_1SLOT BIT(0) +#define MGMT_PHY_BR_1M_3SLOT BIT(1) +#define MGMT_PHY_BR_1M_5SLOT BIT(2) +#define MGMT_PHY_EDR_2M_1SLOT BIT(3) +#define MGMT_PHY_EDR_2M_3SLOT BIT(4) +#define MGMT_PHY_EDR_2M_5SLOT BIT(5) +#define MGMT_PHY_EDR_3M_1SLOT BIT(6) +#define MGMT_PHY_EDR_3M_3SLOT BIT(7) +#define MGMT_PHY_EDR_3M_5SLOT BIT(8) +#define MGMT_PHY_LE_1M_TX BIT(9) +#define MGMT_PHY_LE_1M_RX BIT(10) +#define MGMT_PHY_LE_2M_TX BIT(11) +#define MGMT_PHY_LE_2M_RX BIT(12) +#define MGMT_PHY_LE_CODED_TX BIT(13) +#define MGMT_PHY_LE_CODED_RX BIT(14) #define MGMT_PHY_BREDR_MASK (MGMT_PHY_BR_1M_1SLOT | MGMT_PHY_BR_1M_3SLOT | \ MGMT_PHY_BR_1M_5SLOT | MGMT_PHY_EDR_2M_1SLOT | \ @@ -974,11 +974,11 @@ struct mgmt_ev_auth_failed { __u8 status; } __packed; -#define MGMT_DEV_FOUND_CONFIRM_NAME 0x01 -#define MGMT_DEV_FOUND_LEGACY_PAIRING 0x02 -#define MGMT_DEV_FOUND_NOT_CONNECTABLE 0x04 -#define MGMT_DEV_FOUND_INITIATED_CONN 0x08 -#define MGMT_DEV_FOUND_NAME_REQUEST_FAILED 0x10 +#define MGMT_DEV_FOUND_CONFIRM_NAME BIT(0) +#define MGMT_DEV_FOUND_LEGACY_PAIRING BIT(1) +#define MGMT_DEV_FOUND_NOT_CONNECTABLE BIT(2) +#define MGMT_DEV_FOUND_INITIATED_CONN BIT(3) +#define MGMT_DEV_FOUND_NAME_REQUEST_FAILED BIT(4) #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { From c13380a55522bf14e54779a142487c224509db95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 7 Feb 2023 12:57:41 +0100 Subject: [PATCH 02/72] Bluetooth: btusb: Do not require hardcoded interface numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove hardcoded interface number check because Bluetooth specification since version 4.0 only recommends and no longer requires specific interface numbers. While earlier Bluetooth versions, i.e. 2.1 + EDR and 3.0 + HS, contain required configuration table in Volume 4 - Host Controller Interface Part B - USB Transport Layer, Bluetooth Core Specification Addendum 2 changes the table from required to recommended configuration. Signed-off-by: Tomasz Moń Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 5c536151ef836..4ca91c033d2f4 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3831,13 +3831,9 @@ static int btusb_probe(struct usb_interface *intf, BT_DBG("intf %p id %p", intf, id); - /* interface numbers are hardcoded in the spec */ - if (intf->cur_altsetting->desc.bInterfaceNumber != 0) { - if (!(id->driver_info & BTUSB_IFNUM_2)) - return -ENODEV; - if (intf->cur_altsetting->desc.bInterfaceNumber != 2) - return -ENODEV; - } + if ((id->driver_info & BTUSB_IFNUM_2) && + (intf->cur_altsetting->desc.bInterfaceNumber != 2)) + return -ENODEV; ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber; From c43132abfbe9d2f3befdbddfc3097d30905dc3d1 Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Mon, 13 Feb 2023 13:09:22 +0100 Subject: [PATCH 03/72] dt-bindings: bluetooth: marvell: add 88W8997 Update the documentation with the device tree binding for the Marvell 88W8997 bluetooth device. Signed-off-by: Stefan Eichenberger Acked-by: Krzysztof Kozlowski Signed-off-by: Francesco Dolcini Signed-off-by: Luiz Augusto von Dentz --- Documentation/devicetree/bindings/net/marvell-bluetooth.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/marvell-bluetooth.yaml b/Documentation/devicetree/bindings/net/marvell-bluetooth.yaml index 6aa7a078faa2a..65e09476b048e 100644 --- a/Documentation/devicetree/bindings/net/marvell-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/marvell-bluetooth.yaml @@ -15,7 +15,9 @@ maintainers: properties: compatible: - const: mrvl,88w8897 + enum: + - mrvl,88w8897 + - mrvl,88w8997 required: - compatible From 552705fd891b6ed6dda340567ce3ffff943f1f3b Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Mon, 13 Feb 2023 13:09:23 +0100 Subject: [PATCH 04/72] dt-bindings: bluetooth: marvell: add max-speed property The 88W8997 bluetooth module supports setting the max-speed property. Signed-off-by: Stefan Eichenberger Signed-off-by: Francesco Dolcini Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../bindings/net/marvell-bluetooth.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/net/marvell-bluetooth.yaml b/Documentation/devicetree/bindings/net/marvell-bluetooth.yaml index 65e09476b048e..188a42ca6cebe 100644 --- a/Documentation/devicetree/bindings/net/marvell-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/marvell-bluetooth.yaml @@ -19,9 +19,25 @@ properties: - mrvl,88w8897 - mrvl,88w8997 + max-speed: + description: see Documentation/devicetree/bindings/serial/serial.yaml + required: - compatible +allOf: + - if: + properties: + compatible: + contains: + const: mrvl,88w8997 + then: + properties: + max-speed: true + else: + properties: + max-speed: false + additionalProperties: false examples: From 069690a92ffdda399350af072ea62640bcd370df Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Mon, 13 Feb 2023 13:09:24 +0100 Subject: [PATCH 05/72] Bluetooth: hci_mrvl: use maybe_unused macro for device tree ids Use the maybe_unused macro for the device tree ids instead of #ifdef CONFIG_OF. This makes it easier to add support for new devices. Signed-off-by: Stefan Eichenberger Signed-off-by: Francesco Dolcini Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_mrvl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c index fbc3f7c3a5c71..eaa9c51cacfa2 100644 --- a/drivers/bluetooth/hci_mrvl.c +++ b/drivers/bluetooth/hci_mrvl.c @@ -414,13 +414,11 @@ static void mrvl_serdev_remove(struct serdev_device *serdev) hci_uart_unregister_device(&mrvldev->hu); } -#ifdef CONFIG_OF -static const struct of_device_id mrvl_bluetooth_of_match[] = { +static const struct of_device_id __maybe_unused mrvl_bluetooth_of_match[] = { { .compatible = "mrvl,88w8897" }, { }, }; MODULE_DEVICE_TABLE(of, mrvl_bluetooth_of_match); -#endif static struct serdev_device_driver mrvl_serdev_driver = { .probe = mrvl_serdev_probe, From a860c50f7aef5186fcbb73aff224ae6f5e51cecc Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Mon, 13 Feb 2023 13:09:25 +0100 Subject: [PATCH 06/72] Bluetooth: hci_mrvl: Add serdev support for 88W8997 Add serdev support for the 88W8997 from NXP (previously Marvell). It includes support for changing the baud rate. The command to change the baud rate is taken from the user manual UM11483 Rev. 9 in section 7 (Bring-up of Bluetooth interfaces) from NXP. Signed-off-by: Stefan Eichenberger Signed-off-by: Francesco Dolcini Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_mrvl.c | 86 +++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c index eaa9c51cacfa2..e08222395772d 100644 --- a/drivers/bluetooth/hci_mrvl.c +++ b/drivers/bluetooth/hci_mrvl.c @@ -27,10 +27,12 @@ #define MRVL_ACK 0x5A #define MRVL_NAK 0xBF #define MRVL_RAW_DATA 0x1F +#define MRVL_SET_BAUDRATE 0xFC09 enum { STATE_CHIP_VER_PENDING, STATE_FW_REQ_PENDING, + STATE_FW_LOADED, }; struct mrvl_data { @@ -254,6 +256,14 @@ static int mrvl_recv(struct hci_uart *hu, const void *data, int count) if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) return -EUNATCH; + /* We might receive some noise when there is no firmware loaded. Therefore, + * we drop data if the firmware is not loaded yet and if there is no fw load + * request pending. + */ + if (!test_bit(STATE_FW_REQ_PENDING, &mrvl->flags) && + !test_bit(STATE_FW_LOADED, &mrvl->flags)) + return count; + mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count, mrvl_recv_pkts, ARRAY_SIZE(mrvl_recv_pkts)); @@ -354,6 +364,7 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name) static int mrvl_setup(struct hci_uart *hu) { int err; + struct mrvl_data *mrvl = hu->priv; hci_uart_set_flow_control(hu, true); @@ -367,9 +378,9 @@ static int mrvl_setup(struct hci_uart *hu) hci_uart_wait_until_sent(hu); if (hu->serdev) - serdev_device_set_baudrate(hu->serdev, 3000000); + serdev_device_set_baudrate(hu->serdev, hu->oper_speed); else - hci_uart_set_baudrate(hu, 3000000); + hci_uart_set_baudrate(hu, hu->oper_speed); hci_uart_set_flow_control(hu, false); @@ -377,13 +388,54 @@ static int mrvl_setup(struct hci_uart *hu) if (err) return err; + set_bit(STATE_FW_LOADED, &mrvl->flags); + + return 0; +} + +static int mrvl_set_baudrate(struct hci_uart *hu, unsigned int speed) +{ + int err; + struct mrvl_data *mrvl = hu->priv; + __le32 speed_le = cpu_to_le32(speed); + + /* The firmware might be loaded by the Wifi driver over SDIO. We wait + * up to 10s for the CTS to go up. Afterward, we know that the firmware + * is ready. + */ + err = serdev_device_wait_for_cts(hu->serdev, true, 10000); + if (err) { + bt_dev_err(hu->hdev, "Wait for CTS failed with %d\n", err); + return err; + } + + set_bit(STATE_FW_LOADED, &mrvl->flags); + + err = __hci_cmd_sync_status(hu->hdev, MRVL_SET_BAUDRATE, + sizeof(speed_le), &speed_le, + HCI_INIT_TIMEOUT); + if (err) { + bt_dev_err(hu->hdev, "send command failed: %d", err); + return err; + } + + serdev_device_set_baudrate(hu->serdev, speed); + + /* We forcefully have to send a command to the bluetooth module so that + * the driver detects it after a baudrate change. This is foreseen by + * hci_serdev by setting HCI_UART_VND_DETECT which then causes a dummy + * local version read. + */ + set_bit(HCI_UART_VND_DETECT, &hu->hdev_flags); + return 0; } -static const struct hci_uart_proto mrvl_proto = { +static const struct hci_uart_proto mrvl_proto_8897 = { .id = HCI_UART_MRVL, .name = "Marvell", .init_speed = 115200, + .oper_speed = 3000000, .open = mrvl_open, .close = mrvl_close, .flush = mrvl_flush, @@ -393,18 +445,37 @@ static const struct hci_uart_proto mrvl_proto = { .dequeue = mrvl_dequeue, }; +static const struct hci_uart_proto mrvl_proto_8997 = { + .id = HCI_UART_MRVL, + .name = "Marvell 8997", + .init_speed = 115200, + .oper_speed = 3000000, + .open = mrvl_open, + .close = mrvl_close, + .flush = mrvl_flush, + .set_baudrate = mrvl_set_baudrate, + .recv = mrvl_recv, + .enqueue = mrvl_enqueue, + .dequeue = mrvl_dequeue, +}; + static int mrvl_serdev_probe(struct serdev_device *serdev) { struct mrvl_serdev *mrvldev; + const struct hci_uart_proto *mrvl_proto = device_get_match_data(&serdev->dev); mrvldev = devm_kzalloc(&serdev->dev, sizeof(*mrvldev), GFP_KERNEL); if (!mrvldev) return -ENOMEM; + mrvldev->hu.oper_speed = mrvl_proto->oper_speed; + if (mrvl_proto->set_baudrate) + of_property_read_u32(serdev->dev.of_node, "max-speed", &mrvldev->hu.oper_speed); + mrvldev->hu.serdev = serdev; serdev_device_set_drvdata(serdev, mrvldev); - return hci_uart_register_device(&mrvldev->hu, &mrvl_proto); + return hci_uart_register_device(&mrvldev->hu, mrvl_proto); } static void mrvl_serdev_remove(struct serdev_device *serdev) @@ -415,7 +486,8 @@ static void mrvl_serdev_remove(struct serdev_device *serdev) } static const struct of_device_id __maybe_unused mrvl_bluetooth_of_match[] = { - { .compatible = "mrvl,88w8897" }, + { .compatible = "mrvl,88w8897", .data = &mrvl_proto_8897}, + { .compatible = "mrvl,88w8997", .data = &mrvl_proto_8997}, { }, }; MODULE_DEVICE_TABLE(of, mrvl_bluetooth_of_match); @@ -433,12 +505,12 @@ int __init mrvl_init(void) { serdev_device_driver_register(&mrvl_serdev_driver); - return hci_uart_register_proto(&mrvl_proto); + return hci_uart_register_proto(&mrvl_proto_8897); } int __exit mrvl_deinit(void) { serdev_device_driver_unregister(&mrvl_serdev_driver); - return hci_uart_unregister_proto(&mrvl_proto); + return hci_uart_unregister_proto(&mrvl_proto_8897); } From fe4b71b59ed5067ae591a33d512947160fd9aa89 Mon Sep 17 00:00:00 2001 From: Victor Hassan Date: Wed, 22 Feb 2023 17:47:20 +0800 Subject: [PATCH 07/72] Bluetooth: btrtl: Add support for RTL8852BS Add the support for RTL8852BS BT controller on UART interface. The necessary firmware file will be submitted to linux-firmware. Signed-off-by: Victor Hassan Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 69c3fe649ca7d..aee75f278e60d 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -190,6 +190,14 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8852au_fw.bin", .cfg_name = "rtl_bt/rtl8852au_config" }, + /* 8852B with UART interface */ + { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART), + .config_needed = true, + .has_rom_version = true, + .has_msft_ext = true, + .fw_name = "rtl_bt/rtl8852bs_fw.bin", + .cfg_name = "rtl_bt/rtl8852bs_config" }, + /* 8852B */ { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB), .config_needed = false, @@ -963,6 +971,8 @@ MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852au_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852au_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8852bs_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin"); From d4b20f0b8491bfb3238ebf7df4b13243189620d2 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 24 Feb 2023 15:27:33 -0800 Subject: [PATCH 08/72] Bluetooth: hci_core: Make hci_conn_hash_add append to the list This makes hci_conn_hash_add append to the tail of the conn_hash so it matches the order they are created, this is required if the controller attempts to match the order of ACL with CIS which uses append logic when programming the CIS ids on the CIG. The result of this change affects Create CIS: Before: < HCI Command: LE Create Connected Isochronous Stream (0x08|0x0064) plen 9 Number of CIS: 2 CIS Handle: 2560 ACL Handle: 3586 CIS Handle: 2561 ACL Handle: 3585 After: < HCI Command: LE Create Connected Isochronous Stream (0x08|0x0064) plen 9 Number of CIS: 2 CIS Handle: 2560 ACL Handle: 3585 CIS Handle: 2561 ACL Handle: 3586 Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d5311ceb21c62..e22e45fbe8dbb 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -979,7 +979,7 @@ static inline bool hci_conn_sc_enabled(struct hci_conn *conn) static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; - list_add_rcu(&c->list, &h->list); + list_add_tail_rcu(&c->list, &h->list); switch (c->type) { case ACL_LINK: h->acl_num++; From ae051b044ebf5938940741c25a8989e07a5d7e88 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 27 Feb 2023 15:50:58 -0800 Subject: [PATCH 09/72] Bluetooth: hci_sync: Fix smatch warning This fixes the following new warning: net/bluetooth/hci_sync.c:2403 hci_pause_addr_resolution() warn: missing error code? 'err' Reported-by: kernel test robot Reported-by: Dan Carpenter Link: https://lore.kernel.org/r/202302251952.xryXOegd-lkp@intel.com/ Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 632be12672887..b64af017ab99a 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -2403,7 +2403,7 @@ static int hci_pause_addr_resolution(struct hci_dev *hdev) /* Return if address resolution is disabled and RPA is not used. */ if (!err && scan_use_rpa(hdev)) - return err; + return 0; hci_resume_advertising_sync(hdev); return err; From 13209415d0e88396d99d346b184864834d70d68a Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Tue, 28 Feb 2023 13:55:17 +0800 Subject: [PATCH 10/72] Bluetooth: btusb: Add new PID/VID 04ca:3801 for MT7663 This bluetooth device is found in a combo WLAN/BT card for a MediaTek 7663. Tested on Acer Aspire A315-24P Notebook The device information: T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=04ca ProdID=3801 Rev= 1.00 S: Manufacturer=MediaTek Inc. S: Product=Wireless_Device S: SerialNumber=000000000 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA A: FirstIf#= 0 IfCount= 2 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 Signed-off-by: Meng Tang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 4ca91c033d2f4..372007f784674 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -558,6 +558,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x043e, 0x310c), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3801), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, /* Additional MediaTek MT7668 Bluetooth devices */ { USB_DEVICE(0x043e, 0x3109), .driver_info = BTUSB_MEDIATEK | From 95ee3a93239e172b592dc4613c403de2410e4a82 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Feb 2023 09:22:03 -0600 Subject: [PATCH 11/72] dt-bindings: net: realtek-bluetooth: Add RTL8821CS Add compatible string for RTL8821CS for existing Realtek Bluetooth driver. Signed-off-by: Chris Morgan Reviewed-by: Krzysztof Kozlowski Reviewed-by: Alistair Francis Signed-off-by: Luiz Augusto von Dentz --- .../bindings/net/realtek-bluetooth.yaml | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml b/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml index 143b5667abada..8cc2b9924680a 100644 --- a/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml @@ -4,24 +4,30 @@ $id: http://devicetree.org/schemas/net/realtek-bluetooth.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: RTL8723BS/RTL8723CS/RTL8822CS Bluetooth +title: RTL8723BS/RTL8723CS/RTL8821CS/RTL8822CS Bluetooth maintainers: - Vasily Khoruzhick - Alistair Francis description: - RTL8723CS/RTL8723CS/RTL8822CS is WiFi + BT chip. WiFi part is connected over - SDIO, while BT is connected over serial. It speaks H5 protocol with few - extra commands to upload firmware and change module speed. + RTL8723CS/RTL8723CS/RTL8821CS/RTL8822CS is a WiFi + BT chip. WiFi part + is connected over SDIO, while BT is connected over serial. It speaks + H5 protocol with few extra commands to upload firmware and change + module speed. properties: compatible: - enum: - - realtek,rtl8723bs-bt - - realtek,rtl8723cs-bt - - realtek,rtl8723ds-bt - - realtek,rtl8822cs-bt + oneOf: + - enum: + - realtek,rtl8723bs-bt + - realtek,rtl8723cs-bt + - realtek,rtl8723ds-bt + - realtek,rtl8822cs-bt + - items: + - enum: + - realtek,rtl8821cs-bt + - const: realtek,rtl8822cs-bt device-wake-gpios: maxItems: 1 From 10c9e0104e3bffa31ddc653619f993d6842955d6 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Feb 2023 09:22:04 -0600 Subject: [PATCH 12/72] Bluetooth: hci_h5: btrtl: Add support for RTL8821CS RTL8821CS is a WiFi + Bluetooth combo chip from Realtek that provides WiFi A/B/G/N/AC over an SDIO interface and Bluetooth 4.2 over a UART interface. Note that the firmware this was tested with was firmware version 0x75b8f098. Signed-off-by: Chris Morgan Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index aee75f278e60d..0931f27d2b834 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -128,6 +128,14 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8821c_fw.bin", .cfg_name = "rtl_bt/rtl8821c_config" }, + /* 8821CS */ + { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART), + .config_needed = true, + .has_rom_version = true, + .has_msft_ext = true, + .fw_name = "rtl_bt/rtl8821cs_fw.bin", + .cfg_name = "rtl_bt/rtl8821cs_config" }, + /* 8761A */ { IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB), .config_needed = false, From b6986b7920bb50ed64e96ddd8c4da43bca289ecc Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Feb 2023 09:22:05 -0600 Subject: [PATCH 13/72] arm64: dts: rockchip: Update compatible for bluetooth Update the compatible for the Realtek RTL8821CS bluetooth node. Signed-off-by: Chris Morgan Signed-off-by: Luiz Augusto von Dentz --- arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi index 41262a69d33e4..8fadd8afb1906 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-anbernic-rgxx3.dtsi @@ -716,7 +716,7 @@ status = "okay"; bluetooth { - compatible = "realtek,rtl8821cs-bt"; + compatible = "realtek,rtl8821cs-bt", "realtek,rtl8822cs-bt"; device-wake-gpios = <&gpio4 4 GPIO_ACTIVE_HIGH>; enable-gpios = <&gpio4 3 GPIO_ACTIVE_HIGH>; host-wake-gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; From 125ede3afe5830c3e748dbc66098b7b640899321 Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Mon, 6 Mar 2023 09:44:59 +0800 Subject: [PATCH 14/72] Bluetooth: Add VID/PID 0489/e0e4 for MediaTek MT7922 Tested on Acer Nitro AN515-47 Notebook output from /sys/kernel/debug/usb/devices: T: Bus=03 Lev=01 Prnt=01 Port=02 Cnt=02 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e0e4 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: Meng Tang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 372007f784674..0c94cd7f4af31 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -615,6 +615,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe0e2), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0e4), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, { USB_DEVICE(0x0489, 0xe0f2), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, From f5d13029a41d6504831ab99da0a9a0332fa7ea19 Mon Sep 17 00:00:00 2001 From: Archie Pusaka Date: Mon, 6 Mar 2023 17:07:07 +0800 Subject: [PATCH 15/72] Bluetooth: hci_sync: Don't wait peer's reply when powering off Currently, when we initiate disconnection, we will wait for the peer's reply unless when we are suspending, where we fire and forget the disconnect request. A similar case is when adapter is powering off. However, we still wait for the peer's reply in this case. Therefore, if the peer is unresponsive, the command will time out and the power off sequence will fail, causing "bluetooth powered on by itself" to users. This patch makes the host doesn't wait for the peer's reply when the disconnection reason is powering off. Signed-off-by: Archie Pusaka Reviewed-by: Abhishek Pandit-Subedi Reviewed-by: Simon Horman Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index b64af017ab99a..eb5fd2fe1dc2b 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -5108,10 +5108,12 @@ static int hci_disconnect_sync(struct hci_dev *hdev, struct hci_conn *conn, cp.handle = cpu_to_le16(conn->handle); cp.reason = reason; - /* Wait for HCI_EV_DISCONN_COMPLETE not HCI_EV_CMD_STATUS when not - * suspending. + /* Wait for HCI_EV_DISCONN_COMPLETE, not HCI_EV_CMD_STATUS, when the + * reason is anything but HCI_ERROR_REMOTE_POWER_OFF. This reason is + * used when suspending or powering off, where we don't want to wait + * for the peer's response. */ - if (!hdev->suspended) + if (reason != HCI_ERROR_REMOTE_POWER_OFF) return __hci_cmd_sync_status_sk(hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp, HCI_EV_DISCONN_COMPLETE, From 493ea69970e2a4e4102fdf0be6d262456f564c3a Mon Sep 17 00:00:00 2001 From: Brian Gix Date: Fri, 24 Feb 2023 09:56:32 -0800 Subject: [PATCH 16/72] Bluetooth: Convert MSFT filter HCI cmd to hci_sync The msft_set_filter_enable() command was using the deprecated hci_request mechanism rather than hci_sync. This caused the warning error: hci0: HCI_REQ-0xfcf0 Signed-off-by: Brian Gix Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/msft.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index bee6a4c656be4..bf5cee48916c7 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -743,17 +743,12 @@ __u64 msft_get_features(struct hci_dev *hdev) } static void msft_le_set_advertisement_filter_enable_cb(struct hci_dev *hdev, - u8 status, u16 opcode, - struct sk_buff *skb) + void *user_data, + u8 status) { - struct msft_cp_le_set_advertisement_filter_enable *cp; - struct msft_rp_le_set_advertisement_filter_enable *rp; + struct msft_cp_le_set_advertisement_filter_enable *cp = user_data; struct msft_data *msft = hdev->msft_data; - rp = (struct msft_rp_le_set_advertisement_filter_enable *)skb->data; - if (skb->len < sizeof(*rp)) - return; - /* Error 0x0C would be returned if the filter enabled status is * already set to whatever we were trying to set. * Although the default state should be disabled, some controller set @@ -766,7 +761,6 @@ static void msft_le_set_advertisement_filter_enable_cb(struct hci_dev *hdev, hci_dev_lock(hdev); - cp = hci_sent_cmd_data(hdev, hdev->msft_opcode); msft->filter_enabled = cp->enable; if (status == 0x0C) @@ -804,31 +798,23 @@ int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor) return msft_remove_monitor_sync(hdev, monitor); } -void msft_req_add_set_filter_enable(struct hci_request *req, bool enable) -{ - struct hci_dev *hdev = req->hdev; - struct msft_cp_le_set_advertisement_filter_enable cp; - - cp.sub_opcode = MSFT_OP_LE_SET_ADVERTISEMENT_FILTER_ENABLE; - cp.enable = enable; - - hci_req_add(req, hdev->msft_opcode, sizeof(cp), &cp); -} - int msft_set_filter_enable(struct hci_dev *hdev, bool enable) { - struct hci_request req; + struct msft_cp_le_set_advertisement_filter_enable cp; struct msft_data *msft = hdev->msft_data; int err; if (!msft) return -EOPNOTSUPP; - hci_req_init(&req, hdev); - msft_req_add_set_filter_enable(&req, enable); - err = hci_req_run_skb(&req, msft_le_set_advertisement_filter_enable_cb); + cp.sub_opcode = MSFT_OP_LE_SET_ADVERTISEMENT_FILTER_ENABLE; + cp.enable = enable; + err = __hci_cmd_sync_status(hdev, hdev->msft_opcode, sizeof(cp), &cp, + HCI_CMD_TIMEOUT); + + msft_le_set_advertisement_filter_enable_cb(hdev, &cp, err); - return err; + return 0; } bool msft_curve_validity(struct hci_dev *hdev) From 9f5ae8fcae1227e9bae3d973fcbef8319a1b25c7 Mon Sep 17 00:00:00 2001 From: Inga Stotland Date: Wed, 8 Mar 2023 10:32:35 -0800 Subject: [PATCH 17/72] Bluetooth: hci_sync: Remove duplicate statement This removes the following duplicate statement in hci_le_ext_directed_advertising_sync(): cp.own_addr_type = own_addr_type; Signed-off-by: Inga Stotland Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index eb5fd2fe1dc2b..31231f0e4a28f 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -5855,7 +5855,6 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev, memset(&cp, 0, sizeof(cp)); cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_DIRECT_IND); - cp.own_addr_type = own_addr_type; cp.channel_map = hdev->le_adv_channel_map; cp.tx_power = HCI_TX_POWER_INVALID; cp.primary_phy = HCI_ADV_PHY_1M; From b8b23001b8025a61f0979578884a74faa825023e Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 8 Mar 2023 16:16:31 -0800 Subject: [PATCH 18/72] Bluetooth: L2CAP: Delay identity address updates This delays the identity address updates to give time for userspace to process the new address otherwise there is a risk that userspace creates a duplicated device if the MGMT event is delayed for some reason. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 7 +++---- net/bluetooth/smp.c | 9 ++++++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 2f766e3437ce2..cf393e72d6ed6 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -694,7 +694,7 @@ struct l2cap_conn { struct sk_buff_head pending_rx; struct work_struct pending_rx_work; - struct work_struct id_addr_update_work; + struct delayed_work id_addr_timer; __u8 disc_reason; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 55a7226233f96..5cc95fd17f7d6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -745,7 +745,7 @@ EXPORT_SYMBOL_GPL(l2cap_chan_list); static void l2cap_conn_update_id_addr(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - id_addr_update_work); + id_addr_timer.work); struct hci_conn *hcon = conn->hcon; struct l2cap_chan *chan; @@ -1907,8 +1907,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) if (work_pending(&conn->pending_rx_work)) cancel_work_sync(&conn->pending_rx_work); - if (work_pending(&conn->id_addr_update_work)) - cancel_work_sync(&conn->id_addr_update_work); + cancel_delayed_work_sync(&conn->id_addr_timer); l2cap_unregister_all_users(conn); @@ -7874,7 +7873,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) skb_queue_head_init(&conn->pending_rx); INIT_WORK(&conn->pending_rx_work, process_pending_rx); - INIT_WORK(&conn->id_addr_update_work, l2cap_conn_update_id_addr); + INIT_DELAYED_WORK(&conn->id_addr_timer, l2cap_conn_update_id_addr); conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 70663229b3cc9..f1a9fc0012f09 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -58,6 +58,8 @@ #define SMP_TIMEOUT msecs_to_jiffies(30000) +#define ID_ADDR_TIMEOUT msecs_to_jiffies(200) + #define AUTH_REQ_MASK(dev) (hci_dev_test_flag(dev, HCI_SC_ENABLED) ? \ 0x3f : 0x07) #define KEY_DIST_MASK 0x07 @@ -1067,7 +1069,12 @@ static void smp_notify_keys(struct l2cap_conn *conn) if (hcon->type == LE_LINK) { bacpy(&hcon->dst, &smp->remote_irk->bdaddr); hcon->dst_type = smp->remote_irk->addr_type; - queue_work(hdev->workqueue, &conn->id_addr_update_work); + /* Use a short delay to make sure the new address is + * propagated _before_ the channels. + */ + queue_delayed_work(hdev->workqueue, + &conn->id_addr_timer, + ID_ADDR_TIMEOUT); } } From cd97d47f3069ef578f3b3ddb8c7ae3d6ce38b61a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 12:13:51 +0100 Subject: [PATCH 19/72] Bluetooth: hci_ll: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/bluetooth/hci_ll.c:769:34: error: ‘hci_ti_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_ll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 5abc01a2acf72..4a0b5c3160c2b 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -786,7 +786,7 @@ MODULE_DEVICE_TABLE(of, hci_ti_of_match); static struct serdev_device_driver hci_ti_drv = { .driver = { .name = "hci-ti", - .of_match_table = of_match_ptr(hci_ti_of_match), + .of_match_table = hci_ti_of_match, }, .probe = hci_ti_probe, .remove = hci_ti_remove, From 04909d7bf847bc0b939a5571a1a1fcbe5f2e2ead Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 12:13:52 +0100 Subject: [PATCH 20/72] Bluetooth: btmrvl_sdio: mark OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF making certain data unused: drivers/bluetooth/btmrvl_sdio.c:43:34: error: ‘btmrvl_sdio_of_match_table’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmrvl_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index ba057ebfda5c2..d76c799553aaa 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -40,7 +40,7 @@ static struct memory_type_mapping mem_type_mapping_tbl[] = { {"EXTLAST", NULL, 0, 0xFE}, }; -static const struct of_device_id btmrvl_sdio_of_match_table[] = { +static const struct of_device_id btmrvl_sdio_of_match_table[] __maybe_unused = { { .compatible = "marvell,sd8897-bt" }, { .compatible = "marvell,sd8997-bt" }, { } From 44fac8a2fd2f72ee98ee41e6bc9ecc7765b5d3cc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 12:13:53 +0100 Subject: [PATCH 21/72] Bluetooth: hci_qca: mark OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF making certain data unused: drivers/bluetooth/hci_qca.c:1869:37: error: ‘qca_soc_data_wcn6750’ defined but not used [-Werror=unused-const-variable=] drivers/bluetooth/hci_qca.c:1853:37: error: ‘qca_soc_data_wcn3998’ defined but not used [-Werror=unused-const-variable=] drivers/bluetooth/hci_qca.c:1841:37: error: ‘qca_soc_data_wcn3991’ defined but not used [-Werror=unused-const-variable=] drivers/bluetooth/hci_qca.c:1830:37: error: ‘qca_soc_data_wcn3990’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_qca.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 3df8c3606e933..a199ed52d76dc 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1827,7 +1827,7 @@ static const struct hci_uart_proto qca_proto = { .dequeue = qca_dequeue, }; -static const struct qca_device_data qca_soc_data_wcn3990 = { +static const struct qca_device_data qca_soc_data_wcn3990 __maybe_unused = { .soc_type = QCA_WCN3990, .vregs = (struct qca_vreg []) { { "vddio", 15000 }, @@ -1838,7 +1838,7 @@ static const struct qca_device_data qca_soc_data_wcn3990 = { .num_vregs = 4, }; -static const struct qca_device_data qca_soc_data_wcn3991 = { +static const struct qca_device_data qca_soc_data_wcn3991 __maybe_unused = { .soc_type = QCA_WCN3991, .vregs = (struct qca_vreg []) { { "vddio", 15000 }, @@ -1850,7 +1850,7 @@ static const struct qca_device_data qca_soc_data_wcn3991 = { .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES, }; -static const struct qca_device_data qca_soc_data_wcn3998 = { +static const struct qca_device_data qca_soc_data_wcn3998 __maybe_unused = { .soc_type = QCA_WCN3998, .vregs = (struct qca_vreg []) { { "vddio", 10000 }, @@ -1861,12 +1861,12 @@ static const struct qca_device_data qca_soc_data_wcn3998 = { .num_vregs = 4, }; -static const struct qca_device_data qca_soc_data_qca6390 = { +static const struct qca_device_data qca_soc_data_qca6390 __maybe_unused = { .soc_type = QCA_QCA6390, .num_vregs = 0, }; -static const struct qca_device_data qca_soc_data_wcn6750 = { +static const struct qca_device_data qca_soc_data_wcn6750 __maybe_unused = { .soc_type = QCA_WCN6750, .vregs = (struct qca_vreg []) { { "vddio", 5000 }, From a88ea43b73f109e446f6027df237c8f868127a78 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 12:13:54 +0100 Subject: [PATCH 22/72] Bluetooth: btmtkuart: mark OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF making certain data unused: drivers/bluetooth/btmtkuart.c:971:36: error: ‘mt7668_data’ defined but not used [-Werror=unused-const-variable=] drivers/bluetooth/btmtkuart.c:966:36: error: ‘mt7663_data’ defined but not used [-Werror=unused-const-variable=] drivers/bluetooth/btmtkuart.c:962:36: error: ‘mt7622_data’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmtkuart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index c98691cdbbd55..7680c67cdb35c 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -959,16 +959,16 @@ static void btmtkuart_remove(struct serdev_device *serdev) hci_free_dev(hdev); } -static const struct btmtkuart_data mt7622_data = { +static const struct btmtkuart_data mt7622_data __maybe_unused = { .fwname = FIRMWARE_MT7622, }; -static const struct btmtkuart_data mt7663_data = { +static const struct btmtkuart_data mt7663_data __maybe_unused = { .flags = BTMTKUART_FLAG_STANDALONE_HW, .fwname = FIRMWARE_MT7663, }; -static const struct btmtkuart_data mt7668_data = { +static const struct btmtkuart_data mt7668_data __maybe_unused = { .flags = BTMTKUART_FLAG_STANDALONE_HW, .fwname = FIRMWARE_MT7668, }; From 8194f1ef5a815aea815a91daf2c721eab2674f1f Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 7 Mar 2023 23:17:30 +0100 Subject: [PATCH 23/72] Bluetooth: Add new quirk for broken local ext features page 2 Some adapters (e.g. RTL8723CS) advertise that they have more than 2 pages for local ext features, but they don't support any features declared in these pages. RTL8723CS reports max_page = 2 and declares support for sync train and secure connection, but it responds with either garbage or with error in status on corresponding commands. Signed-off-by: Vasily Khoruzhick Signed-off-by: Bastian Germann Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 7 +++++++ net/bluetooth/hci_event.c | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 400f8a7d0c3fe..997107bfc0b12 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -294,6 +294,13 @@ enum { * during the hdev->setup vendor callback. */ HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG, + + /* When this quirk is set, max_page for local extended features + * is set to 1, even if controller reports higher number. Some + * controllers (e.g. RTL8723CS) report more pages, but they + * don't actually support features declared there. + */ + HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, }; /* HCI device flags */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e87c928c9e17a..51f13518dba9b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -886,8 +886,13 @@ static u8 hci_cc_read_local_ext_features(struct hci_dev *hdev, void *data, if (rp->status) return rp->status; - if (hdev->max_page < rp->max_page) - hdev->max_page = rp->max_page; + if (hdev->max_page < rp->max_page) { + if (test_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, + &hdev->quirks)) + bt_dev_warn(hdev, "broken local ext features page 2"); + else + hdev->max_page = rp->max_page; + } if (rp->page < HCI_MAX_PAGES) memcpy(hdev->features[rp->page], rp->features, 8); From c0123cb6c4c7fc2a42ead6cd7d3e82b8e1c25c6f Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 7 Mar 2023 23:17:31 +0100 Subject: [PATCH 24/72] Bluetooth: btrtl: add support for the RTL8723CS The Realtek RTL8723CS is a SDIO WiFi chip. It also contains a Bluetooth module which is connected via UART to the host. It shares lmp subversion with 8703B, so Realtek's userspace initialization tool (rtk_hciattach) differentiates varieties of RTL8723CS (CG, VF, XX) with RTL8703B using vendor's command to read chip type. Also this chip declares support for some features it doesn't support so add a quirk to indicate that these features are broken. Signed-off-by: Vasily Khoruzhick Signed-off-by: Bastian Germann Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 120 +++++++++++++++++++++++++++++++++++-- drivers/bluetooth/btrtl.h | 5 ++ drivers/bluetooth/hci_h5.c | 4 ++ 3 files changed, 125 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 0931f27d2b834..8fa7733812adf 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -17,7 +17,11 @@ #define VERSION "0.1" +#define RTL_CHIP_8723CS_CG 3 +#define RTL_CHIP_8723CS_VF 4 +#define RTL_CHIP_8723CS_XX 5 #define RTL_EPATCH_SIGNATURE "Realtech" +#define RTL_ROM_LMP_8703B 0x8703 #define RTL_ROM_LMP_8723A 0x1200 #define RTL_ROM_LMP_8723B 0x8723 #define RTL_ROM_LMP_8821A 0x8821 @@ -30,6 +34,7 @@ #define IC_MATCH_FL_HCIREV (1 << 1) #define IC_MATCH_FL_HCIVER (1 << 2) #define IC_MATCH_FL_HCIBUS (1 << 3) +#define IC_MATCH_FL_CHIP_TYPE (1 << 4) #define IC_INFO(lmps, hcir, hciv, bus) \ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \ IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \ @@ -59,6 +64,7 @@ struct id_table { __u16 hci_rev; __u8 hci_ver; __u8 hci_bus; + __u8 chip_type; bool config_needed; bool has_rom_version; bool has_msft_ext; @@ -99,6 +105,39 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8723b_fw.bin", .cfg_name = "rtl_bt/rtl8723b_config" }, + /* 8723CS-CG */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_CG, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_cg_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_cg_config" }, + + /* 8723CS-VF */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_VF, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_vf_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_vf_config" }, + + /* 8723CS-XX */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_XX, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_xx_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_xx_config" }, + /* 8723D */ { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB), .config_needed = true, @@ -224,7 +263,8 @@ static const struct id_table ic_id_table[] = { }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, - u8 hci_ver, u8 hci_bus) + u8 hci_ver, u8 hci_bus, + u8 chip_type) { int i; @@ -241,6 +281,9 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) && (ic_id_table[i].hci_bus != hci_bus)) continue; + if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) && + (ic_id_table[i].chip_type != chip_type)) + continue; break; } @@ -323,6 +366,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8723B, 1 }, { RTL_ROM_LMP_8821A, 2 }, { RTL_ROM_LMP_8761A, 3 }, + { RTL_ROM_LMP_8703B, 7 }, { RTL_ROM_LMP_8822B, 8 }, { RTL_ROM_LMP_8723B, 9 }, /* 8723D */ { RTL_ROM_LMP_8821A, 10 }, /* 8821C */ @@ -603,6 +647,48 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, return ret; } +static bool rtl_has_chip_type(u16 lmp_subver) +{ + switch (lmp_subver) { + case RTL_ROM_LMP_8703B: + return true; + default: + break; + } + + return false; +} + +static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type) +{ + struct rtl_chip_type_evt *chip_type; + struct sk_buff *skb; + const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0}; + + /* Read RTL chip type command */ + skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + rtl_dev_err(hdev, "Read chip type failed (%ld)", + PTR_ERR(skb)); + return PTR_ERR(skb); + } + + chip_type = skb_pull_data(skb, sizeof(*chip_type)); + if (!chip_type) { + rtl_dev_err(hdev, "RTL chip type event length mismatch"); + kfree_skb(skb); + return -EIO; + } + + rtl_dev_info(hdev, "chip_type status=%x type=%x", + chip_type->status, chip_type->type); + + *type = chip_type->type & 0x0f; + + kfree_skb(skb); + return 0; +} + void btrtl_free(struct btrtl_device_info *btrtl_dev) { kvfree(btrtl_dev->fw_data); @@ -619,7 +705,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, struct hci_rp_read_local_version *resp; char cfg_name[40]; u16 hci_rev, lmp_subver; - u8 hci_ver; + u8 hci_ver, chip_type = 0; int ret; u16 opcode; u8 cmd[2]; @@ -645,8 +731,14 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, hci_rev = le16_to_cpu(resp->hci_rev); lmp_subver = le16_to_cpu(resp->lmp_subver); + if (rtl_has_chip_type(lmp_subver)) { + ret = rtl_read_chip_type(hdev, &chip_type); + if (ret) + goto err_free; + } + btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, - hdev->bus); + hdev->bus, chip_type); if (!btrtl_dev->ic_info) btrtl_dev->drop_fw = true; @@ -689,7 +781,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, lmp_subver = le16_to_cpu(resp->lmp_subver); btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, - hdev->bus); + hdev->bus, chip_type); } out_free: kfree_skb(skb); @@ -771,6 +863,7 @@ int btrtl_download_firmware(struct hci_dev *hdev, case RTL_ROM_LMP_8761A: case RTL_ROM_LMP_8822B: case RTL_ROM_LMP_8852A: + case RTL_ROM_LMP_8703B: return btrtl_setup_rtl8723b(hdev, btrtl_dev); default: rtl_dev_info(hdev, "assuming no firmware upload needed"); @@ -811,6 +904,19 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) rtl_dev_dbg(hdev, "WBS supported not enabled."); break; } + + switch (btrtl_dev->ic_info->lmp_subver) { + case RTL_ROM_LMP_8703B: + /* 8723CS reports two pages for local ext features, + * but it doesn't support any features from page 2 - + * it either responds with garbage or with error status + */ + set_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, + &hdev->quirks); + break; + default: + break; + } } EXPORT_SYMBOL_GPL(btrtl_set_quirks); @@ -969,6 +1075,12 @@ MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin"); diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h index ebf0101c959b0..349d72ee571b6 100644 --- a/drivers/bluetooth/btrtl.h +++ b/drivers/bluetooth/btrtl.h @@ -14,6 +14,11 @@ struct btrtl_device_info; +struct rtl_chip_type_evt { + __u8 status; + __u8 type; +} __packed; + struct rtl_download_cmd { __u8 index; __u8 data[RTL_FRAG_LEN]; diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 6455bc4fb5bb3..e90670955df2c 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -936,6 +936,8 @@ static int h5_btrtl_setup(struct h5 *h5) err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev); /* Give the device some time before the hci-core sends it a reset */ usleep_range(10000, 20000); + if (err) + goto out_free; btrtl_set_quirks(h5->hu->hdev, btrtl_dev); @@ -1100,6 +1102,8 @@ static const struct of_device_id rtl_bluetooth_of_match[] = { .data = (const void *)&h5_data_rtl8822cs }, { .compatible = "realtek,rtl8723bs-bt", .data = (const void *)&h5_data_rtl8723bs }, + { .compatible = "realtek,rtl8723cs-bt", + .data = (const void *)&h5_data_rtl8723bs }, { .compatible = "realtek,rtl8723ds-bt", .data = (const void *)&h5_data_rtl8723bs }, #endif From 7c2b2d2d0cb658aa543e11e90ae95621d3cb5fe6 Mon Sep 17 00:00:00 2001 From: Raul Cheleguini Date: Fri, 10 Mar 2023 15:14:10 +0000 Subject: [PATCH 25/72] Bluetooth: Improve support for Actions Semi ATS2851 based devices Add two more quirks to resume the device initialization and basic operation as the device seems not to support "Read Transmit Power" and "Set Extended Scan Parameters". < HCI Command: LE Read Transmit Power (0x08|0x004b) plen 0 > HCI Event: Command Status (0x0f) plen 4 LE Read Transmit Power (0x08|0x004b) ncmd 1 Status: Unknown HCI Command (0x01) < HCI Command: LE Set Extended Scan Parameters (0x08|0x0041) plen 8 Own address type: Random (0x01) Filter policy: Accept all advertisement (0x00) PHYs: 0x01 Entry 0: LE 1M Type: Active (0x01) Interval: 11.250 msec (0x0012) Window: 11.250 msec (0x0012) > HCI Event: Command Status (0x0f) plen 4 LE Set Extended Scan Parameters (0x08|0x0041) ncmd 1 Status: Unknown HCI Command (0x01) Signed-off-by: Raul Cheleguini 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 0c94cd7f4af31..7382b021f3dfb 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4104,6 +4104,8 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_ACTIONS_SEMI) { /* Support is advertised, but not implemented */ set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks); } if (!reset) From 586e154f98ac826233c2be5dc80e101303b4b2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Thu, 9 Mar 2023 10:20:30 +0200 Subject: [PATCH 26/72] Bluetooth: hci_ldisc: Fix tty_set_termios() return value assumptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tty_set_termios() never returns anything else than 0. Make the debug prints to look directly into the new termios instead to check CRTSCTS state. Signed-off-by: Ilpo Järvinen Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_ldisc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 865112e96ff9f..efdda2c3fce8c 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -323,9 +323,9 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable) /* Disable hardware flow control */ ktermios = tty->termios; ktermios.c_cflag &= ~CRTSCTS; - status = tty_set_termios(tty, &ktermios); + tty_set_termios(tty, &ktermios); BT_DBG("Disabling hardware flow control: %s", - status ? "failed" : "success"); + (tty->termios.c_cflag & CRTSCTS) ? "failed" : "success"); /* Clear RTS to prevent the device from sending */ /* Most UARTs need OUT2 to enable interrupts */ @@ -357,9 +357,9 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable) /* Re-enable hardware flow control */ ktermios = tty->termios; ktermios.c_cflag |= CRTSCTS; - status = tty_set_termios(tty, &ktermios); + tty_set_termios(tty, &ktermios); BT_DBG("Enabling hardware flow control: %s", - status ? "failed" : "success"); + !(tty->termios.c_cflag & CRTSCTS) ? "failed" : "success"); } } From 29f93a687f3c435e94d026ee1fc8ad18ce56b7fb Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Thu, 16 Mar 2023 22:52:11 +0530 Subject: [PATCH 27/72] serdev: Replace all instances of ENOTSUPP with EOPNOTSUPP This replaces all instances of ENOTSUPP with EOPNOTSUPP since ENOTSUPP is not a standard error code. This will help maintain consistency in error codes when new serdev API's are added. Signed-off-by: Neeraj Sanjay Kale Reviewed-by: Simon Horman Signed-off-by: Luiz Augusto von Dentz --- drivers/tty/serdev/core.c | 6 +++--- drivers/tty/serdev/serdev-ttyport.c | 4 ++-- include/linux/serdev.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 678014253b7b2..078fe64625cb6 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -366,7 +366,7 @@ int serdev_device_set_parity(struct serdev_device *serdev, struct serdev_controller *ctrl = serdev->ctrl; if (!ctrl || !ctrl->ops->set_parity) - return -ENOTSUPP; + return -EOPNOTSUPP; return ctrl->ops->set_parity(ctrl, parity); } @@ -388,7 +388,7 @@ int serdev_device_get_tiocm(struct serdev_device *serdev) struct serdev_controller *ctrl = serdev->ctrl; if (!ctrl || !ctrl->ops->get_tiocm) - return -ENOTSUPP; + return -EOPNOTSUPP; return ctrl->ops->get_tiocm(ctrl); } @@ -399,7 +399,7 @@ int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) struct serdev_controller *ctrl = serdev->ctrl; if (!ctrl || !ctrl->ops->set_tiocm) - return -ENOTSUPP; + return -EOPNOTSUPP; return ctrl->ops->set_tiocm(ctrl, set, clear); } diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index d367803e2044f..f26ff82723f10 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -231,7 +231,7 @@ static int ttyport_get_tiocm(struct serdev_controller *ctrl) struct tty_struct *tty = serport->tty; if (!tty->ops->tiocmget) - return -ENOTSUPP; + return -EOPNOTSUPP; return tty->ops->tiocmget(tty); } @@ -242,7 +242,7 @@ static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, u struct tty_struct *tty = serport->tty; if (!tty->ops->tiocmset) - return -ENOTSUPP; + return -EOPNOTSUPP; return tty->ops->tiocmset(tty, set, clear); } diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 5f6bfe4f6d955..babedb13785bd 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -250,11 +250,11 @@ static inline int serdev_device_write_buf(struct serdev_device *serdev, static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {} static inline int serdev_device_get_tiocm(struct serdev_device *serdev) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int serdev_device_write(struct serdev_device *sdev, const unsigned char *buf, size_t count, unsigned long timeout) From 8eaf839e4ac4feadf06e03eeff34059795450712 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Thu, 16 Mar 2023 22:52:12 +0530 Subject: [PATCH 28/72] serdev: Add method to assert break signal over tty UART port Adds serdev_device_break_ctl() and an implementation for ttyport. This function simply calls the break_ctl in tty layer, which can assert a break signal over UART-TX line, if the tty and the underlying platform and UART peripheral supports this operation. Signed-off-by: Neeraj Sanjay Kale Reviewed-by: Simon Horman Signed-off-by: Luiz Augusto von Dentz --- drivers/tty/serdev/core.c | 11 +++++++++++ drivers/tty/serdev/serdev-ttyport.c | 12 ++++++++++++ include/linux/serdev.h | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 078fe64625cb6..e7d663901c075 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -405,6 +405,17 @@ int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) } EXPORT_SYMBOL_GPL(serdev_device_set_tiocm); +int serdev_device_break_ctl(struct serdev_device *serdev, int break_state) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->break_ctl) + return -EOPNOTSUPP; + + return ctrl->ops->break_ctl(ctrl, break_state); +} +EXPORT_SYMBOL_GPL(serdev_device_break_ctl); + static int serdev_drv_probe(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index f26ff82723f10..8033ef19669c2 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -247,6 +247,17 @@ static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, u return tty->ops->tiocmset(tty, set, clear); } +static int ttyport_break_ctl(struct serdev_controller *ctrl, unsigned int break_state) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + if (!tty->ops->break_ctl) + return -EOPNOTSUPP; + + return tty->ops->break_ctl(tty, break_state); +} + static const struct serdev_controller_ops ctrl_ops = { .write_buf = ttyport_write_buf, .write_flush = ttyport_write_flush, @@ -259,6 +270,7 @@ static const struct serdev_controller_ops ctrl_ops = { .wait_until_sent = ttyport_wait_until_sent, .get_tiocm = ttyport_get_tiocm, .set_tiocm = ttyport_set_tiocm, + .break_ctl = ttyport_break_ctl, }; struct device *serdev_tty_port_register(struct tty_port *port, diff --git a/include/linux/serdev.h b/include/linux/serdev.h index babedb13785bd..f5f97fa25e8ad 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -93,6 +93,7 @@ struct serdev_controller_ops { void (*wait_until_sent)(struct serdev_controller *, long); int (*get_tiocm)(struct serdev_controller *); int (*set_tiocm)(struct serdev_controller *, unsigned int, unsigned int); + int (*break_ctl)(struct serdev_controller *ctrl, unsigned int break_state); }; /** @@ -203,6 +204,7 @@ int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_ void serdev_device_wait_until_sent(struct serdev_device *, long); int serdev_device_get_tiocm(struct serdev_device *); int serdev_device_set_tiocm(struct serdev_device *, int, int); +int serdev_device_break_ctl(struct serdev_device *serdev, int break_state); void serdev_device_write_wakeup(struct serdev_device *); int serdev_device_write(struct serdev_device *, const unsigned char *, size_t, long); void serdev_device_write_flush(struct serdev_device *); @@ -256,6 +258,10 @@ static inline int serdev_device_set_tiocm(struct serdev_device *serdev, int set, { return -EOPNOTSUPP; } +static inline int serdev_device_break_ctl(struct serdev_device *serdev, int break_state) +{ + return -EOPNOTSUPP; +} static inline int serdev_device_write(struct serdev_device *sdev, const unsigned char *buf, size_t count, unsigned long timeout) { From ab3a769b4dccec2cf60f0a0700b140991bf9afc8 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Thu, 16 Mar 2023 22:52:13 +0530 Subject: [PATCH 29/72] dt-bindings: net: bluetooth: Add NXP bluetooth support Add binding document for NXP bluetooth chipsets attached over UART. Signed-off-by: Neeraj Sanjay Kale Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/nxp,88w8987-bt.yaml | 45 +++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml new file mode 100644 index 0000000000000..57e4c87cb00b4 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/nxp,88w8987-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP Bluetooth chips + +description: + This binding describes UART-attached NXP bluetooth chips. These chips + are dual-radio chips supporting WiFi and Bluetooth. The bluetooth + works on standard H4 protocol over 4-wire UART. The RTS and CTS lines + are used during FW download. To enable power save mode, the host + asserts break signal over UART-TX line to put the chip into power save + state. De-asserting break wakes up the BT chip. + +maintainers: + - Neeraj Sanjay Kale + +properties: + compatible: + enum: + - nxp,88w8987-bt + - nxp,88w8997-bt + + fw-init-baudrate: + description: + Chip baudrate after FW is downloaded and initialized. + This property depends on the module vendor's + configuration. If this property is not specified, + 115200 is set as default. + +required: + - compatible + +additionalProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "nxp,88w8987-bt"; + fw-init-baudrate = <3000000>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 6ac562e0381e0..ee743e25b096d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23237,6 +23237,12 @@ L: linux-mm@kvack.org S: Maintained F: mm/zswap.c +NXP BLUETOOTH WIRELESS DRIVERS +M: Amitkumar Karwar +M: Neeraj Kale +S: Maintained +F: Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml + THE REST M: Linus Torvalds L: linux-kernel@vger.kernel.org From 689ca16e523278470c38832a3010645a78c544d8 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Thu, 16 Mar 2023 22:52:14 +0530 Subject: [PATCH 30/72] Bluetooth: NXP: Add protocol support for NXP Bluetooth chipsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a driver based on serdev driver for the NXP BT serial protocol based on running H:4, which can enable the built-in Bluetooth device inside an NXP BT chip. This driver has Power Save feature that will put the chip into sleep state whenever there is no activity for 2000ms, and will be woken up when any activity is to be initiated over UART. This driver enables the power save feature by default by sending the vendor specific commands to the chip during setup. During setup, the driver checks if a FW is already running on the chip by waiting for the bootloader signature, and downloads device specific FW file into the chip over UART if bootloader signature is received.. Signed-off-by: Neeraj Sanjay Kale Reviewed-by: Ilpo Järvinen Signed-off-by: Luiz Augusto von Dentz --- MAINTAINERS | 1 + drivers/bluetooth/Kconfig | 12 + drivers/bluetooth/Makefile | 1 + drivers/bluetooth/btnxpuart.c | 1297 +++++++++++++++++++++++++++++++++ 4 files changed, 1311 insertions(+) create mode 100644 drivers/bluetooth/btnxpuart.c diff --git a/MAINTAINERS b/MAINTAINERS index ee743e25b096d..c2f3ecdb47e06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23242,6 +23242,7 @@ M: Amitkumar Karwar M: Neeraj Kale S: Maintained F: Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml +F: drivers/bluetooth/btnxpuart.c THE REST M: Linus Torvalds diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 5a1a7bec3c422..0703bdd441407 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -465,4 +465,16 @@ config BT_VIRTIO Say Y here to compile support for HCI over Virtio into the kernel or say M to compile as a module. +config BT_NXPUART + tristate "NXP protocol support" + depends on SERIAL_DEV_BUS + select CRC32 + help + NXP is serial driver required for NXP Bluetooth + devices with UART interface. + + Say Y here to compile support for NXP Bluetooth UART device into + the kernel, or say M here to compile as a module (btnxpuart). + + endmenu diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index e0b261f24fc98..7a5967e9ac482 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_BT_QCA) += btqca.o obj-$(CONFIG_BT_MTK) += btmtk.o obj-$(CONFIG_BT_VIRTIO) += virtio_bt.o +obj-$(CONFIG_BT_NXPUART) += btnxpuart.o obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c new file mode 100644 index 0000000000000..5f641466d695d --- /dev/null +++ b/drivers/bluetooth/btnxpuart.c @@ -0,0 +1,1297 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP Bluetooth driver + * Copyright 2023 NXP + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "h4_recv.h" + +#define MANUFACTURER_NXP 37 + +#define BTNXPUART_TX_STATE_ACTIVE 1 +#define BTNXPUART_FW_DOWNLOADING 2 +#define BTNXPUART_CHECK_BOOT_SIGNATURE 3 +#define BTNXPUART_SERDEV_OPEN 4 + +#define FIRMWARE_W8987 "nxp/uartuart8987_bt.bin" +#define FIRMWARE_W8997 "nxp/uartuart8997_bt_v4.bin" +#define FIRMWARE_W9098 "nxp/uartuart9098_bt_v1.bin" +#define FIRMWARE_IW416 "nxp/uartiw416_bt_v0.bin" +#define FIRMWARE_IW612 "nxp/uartspi_n61x_v1.bin.se" + +#define CHIP_ID_W9098 0x5c03 +#define CHIP_ID_IW416 0x7201 +#define CHIP_ID_IW612 0x7601 + +#define HCI_NXP_PRI_BAUDRATE 115200 +#define HCI_NXP_SEC_BAUDRATE 3000000 + +#define MAX_FW_FILE_NAME_LEN 50 + +/* Default ps timeout period in milliseconds */ +#define PS_DEFAULT_TIMEOUT_PERIOD_MS 2000 + +/* wakeup methods */ +#define WAKEUP_METHOD_DTR 0 +#define WAKEUP_METHOD_BREAK 1 +#define WAKEUP_METHOD_EXT_BREAK 2 +#define WAKEUP_METHOD_RTS 3 +#define WAKEUP_METHOD_INVALID 0xff + +/* power save mode status */ +#define PS_MODE_DISABLE 0 +#define PS_MODE_ENABLE 1 + +/* Power Save Commands to ps_work_func */ +#define PS_CMD_EXIT_PS 1 +#define PS_CMD_ENTER_PS 2 + +/* power save state */ +#define PS_STATE_AWAKE 0 +#define PS_STATE_SLEEP 1 + +/* Bluetooth vendor command : Sleep mode */ +#define HCI_NXP_AUTO_SLEEP_MODE 0xfc23 +/* Bluetooth vendor command : Wakeup method */ +#define HCI_NXP_WAKEUP_METHOD 0xfc53 +/* Bluetooth vendor command : Set operational baudrate */ +#define HCI_NXP_SET_OPER_SPEED 0xfc09 +/* Bluetooth vendor command: Independent Reset */ +#define HCI_NXP_IND_RESET 0xfcfc + +/* Bluetooth Power State : Vendor cmd params */ +#define BT_PS_ENABLE 0x02 +#define BT_PS_DISABLE 0x03 + +/* Bluetooth Host Wakeup Methods */ +#define BT_HOST_WAKEUP_METHOD_NONE 0x00 +#define BT_HOST_WAKEUP_METHOD_DTR 0x01 +#define BT_HOST_WAKEUP_METHOD_BREAK 0x02 +#define BT_HOST_WAKEUP_METHOD_GPIO 0x03 + +/* Bluetooth Chip Wakeup Methods */ +#define BT_CTRL_WAKEUP_METHOD_DSR 0x00 +#define BT_CTRL_WAKEUP_METHOD_BREAK 0x01 +#define BT_CTRL_WAKEUP_METHOD_GPIO 0x02 +#define BT_CTRL_WAKEUP_METHOD_EXT_BREAK 0x04 +#define BT_CTRL_WAKEUP_METHOD_RTS 0x05 + +struct ps_data { + u8 target_ps_mode; /* ps mode to be set */ + u8 cur_psmode; /* current ps_mode */ + u8 ps_state; /* controller's power save state */ + u8 ps_cmd; + u8 h2c_wakeupmode; + u8 cur_h2c_wakeupmode; + u8 c2h_wakeupmode; + u8 c2h_wakeup_gpio; + u8 h2c_wakeup_gpio; + bool driver_sent_cmd; + u16 h2c_ps_interval; + u16 c2h_ps_interval; + struct hci_dev *hdev; + struct work_struct work; + struct timer_list ps_timer; +}; + +struct wakeup_cmd_payload { + u8 c2h_wakeupmode; + u8 c2h_wakeup_gpio; + u8 h2c_wakeupmode; + u8 h2c_wakeup_gpio; +} __packed; + +struct psmode_cmd_payload { + u8 ps_cmd; + __le16 c2h_ps_interval; +} __packed; + +struct btnxpuart_data { + bool fw_download_3M_baudrate; + const char *fw_name; +}; + +struct btnxpuart_dev { + struct hci_dev *hdev; + struct serdev_device *serdev; + + struct work_struct tx_work; + unsigned long tx_state; + struct sk_buff_head txq; + struct sk_buff *rx_skb; + + const struct firmware *fw; + u8 fw_name[MAX_FW_FILE_NAME_LEN]; + u32 fw_dnld_v1_offset; + u32 fw_v1_sent_bytes; + u32 fw_v3_offset_correction; + u32 fw_v1_expected_len; + wait_queue_head_t fw_dnld_done_wait_q; + wait_queue_head_t check_boot_sign_wait_q; + + u32 new_baudrate; + u32 current_baudrate; + u32 fw_init_baudrate; + bool timeout_changed; + bool baudrate_changed; + + struct ps_data psdata; + struct btnxpuart_data *nxp_data; +}; + +#define NXP_V1_FW_REQ_PKT 0xa5 +#define NXP_V1_CHIP_VER_PKT 0xaa +#define NXP_V3_FW_REQ_PKT 0xa7 +#define NXP_V3_CHIP_VER_PKT 0xab + +#define NXP_ACK_V1 0x5a +#define NXP_NAK_V1 0xbf +#define NXP_ACK_V3 0x7a +#define NXP_NAK_V3 0x7b +#define NXP_CRC_ERROR_V3 0x7c + +#define HDR_LEN 16 + +#define NXP_RECV_FW_REQ_V1 \ + .type = NXP_V1_FW_REQ_PKT, \ + .hlen = 4, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = 4 + +#define NXP_RECV_CHIP_VER_V3 \ + .type = NXP_V3_CHIP_VER_PKT, \ + .hlen = 4, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = 4 + +#define NXP_RECV_FW_REQ_V3 \ + .type = NXP_V3_FW_REQ_PKT, \ + .hlen = 9, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = 9 + +struct v1_data_req { + __le16 len; + __le16 len_comp; +} __packed; + +struct v3_data_req { + __le16 len; + __le32 offset; + __le16 error; + u8 crc; +} __packed; + +struct v3_start_ind { + __le16 chip_id; + u8 loader_ver; + u8 crc; +} __packed; + +/* UART register addresses of BT chip */ +#define CLKDIVADDR 0x7f00008f +#define UARTDIVADDR 0x7f000090 +#define UARTMCRADDR 0x7f000091 +#define UARTREINITADDR 0x7f000092 +#define UARTICRADDR 0x7f000093 +#define UARTFCRADDR 0x7f000094 + +#define MCR 0x00000022 +#define INIT 0x00000001 +#define ICR 0x000000c7 +#define FCR 0x000000c7 + +#define POLYNOMIAL8 0x07 + +struct uart_reg { + __le32 address; + __le32 value; +} __packed; + +struct uart_config { + struct uart_reg clkdiv; + struct uart_reg uartdiv; + struct uart_reg mcr; + struct uart_reg re_init; + struct uart_reg icr; + struct uart_reg fcr; + __be32 crc; +} __packed; + +struct nxp_bootloader_cmd { + __le32 header; + __le32 arg; + __le32 payload_len; + __be32 crc; +} __packed; + +static u8 crc8_table[CRC8_TABLE_SIZE]; + +/* Default configurations */ +#define DEFAULT_H2C_WAKEUP_MODE WAKEUP_METHOD_BREAK +#define DEFAULT_PS_MODE PS_MODE_ENABLE +#define FW_INIT_BAUDRATE HCI_NXP_PRI_BAUDRATE + +static struct sk_buff *nxp_drv_send_cmd(struct hci_dev *hdev, u16 opcode, + u32 plen, + void *param) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + struct sk_buff *skb; + + /* set flag to prevent nxp_enqueue from parsing values from this command and + * calling hci_cmd_sync_queue() again. + */ + psdata->driver_sent_cmd = true; + skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT); + psdata->driver_sent_cmd = false; + + return skb; +} + +static void btnxpuart_tx_wakeup(struct btnxpuart_dev *nxpdev) +{ + if (schedule_work(&nxpdev->tx_work)) + set_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state); +} + +/* NXP Power Save Feature */ +static void ps_start_timer(struct btnxpuart_dev *nxpdev) +{ + struct ps_data *psdata = &nxpdev->psdata; + + if (!psdata) + return; + + if (psdata->cur_psmode == PS_MODE_ENABLE) + mod_timer(&psdata->ps_timer, jiffies + msecs_to_jiffies(psdata->h2c_ps_interval)); +} + +static void ps_cancel_timer(struct btnxpuart_dev *nxpdev) +{ + struct ps_data *psdata = &nxpdev->psdata; + + flush_work(&psdata->work); + del_timer_sync(&psdata->ps_timer); +} + +static void ps_control(struct hci_dev *hdev, u8 ps_state) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + int status; + + if (psdata->ps_state == ps_state || + !test_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state)) + return; + + switch (psdata->cur_h2c_wakeupmode) { + case WAKEUP_METHOD_DTR: + if (ps_state == PS_STATE_AWAKE) + status = serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0); + else + status = serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR); + break; + case WAKEUP_METHOD_BREAK: + default: + if (ps_state == PS_STATE_AWAKE) + status = serdev_device_break_ctl(nxpdev->serdev, 0); + else + status = serdev_device_break_ctl(nxpdev->serdev, -1); + bt_dev_dbg(hdev, "Set UART break: %s, status=%d", + str_on_off(ps_state == PS_STATE_SLEEP), status); + break; + } + if (!status) + psdata->ps_state = ps_state; + if (ps_state == PS_STATE_AWAKE) + btnxpuart_tx_wakeup(nxpdev); +} + +static void ps_work_func(struct work_struct *work) +{ + struct ps_data *data = container_of(work, struct ps_data, work); + + if (data->ps_cmd == PS_CMD_ENTER_PS && data->cur_psmode == PS_MODE_ENABLE) + ps_control(data->hdev, PS_STATE_SLEEP); + else if (data->ps_cmd == PS_CMD_EXIT_PS) + ps_control(data->hdev, PS_STATE_AWAKE); +} + +static void ps_timeout_func(struct timer_list *t) +{ + struct ps_data *data = from_timer(data, t, ps_timer); + struct hci_dev *hdev = data->hdev; + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + if (test_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state)) { + ps_start_timer(nxpdev); + } else { + data->ps_cmd = PS_CMD_ENTER_PS; + schedule_work(&data->work); + } +} + +static int ps_init_work(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + + psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS; + psdata->ps_state = PS_STATE_AWAKE; + psdata->target_ps_mode = DEFAULT_PS_MODE; + psdata->hdev = hdev; + psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE; + psdata->c2h_wakeup_gpio = 0xff; + + switch (DEFAULT_H2C_WAKEUP_MODE) { + case WAKEUP_METHOD_DTR: + psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR; + break; + case WAKEUP_METHOD_BREAK: + default: + psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK; + break; + } + psdata->cur_psmode = PS_MODE_DISABLE; + psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID; + INIT_WORK(&psdata->work, ps_work_func); + + return 0; +} + +static void ps_init_timer(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + + timer_setup(&psdata->ps_timer, ps_timeout_func, 0); +} + +static void ps_wakeup(struct btnxpuart_dev *nxpdev) +{ + struct ps_data *psdata = &nxpdev->psdata; + + if (psdata->ps_state != PS_STATE_AWAKE) { + psdata->ps_cmd = PS_CMD_EXIT_PS; + schedule_work(&psdata->work); + } +} + +static int send_ps_cmd(struct hci_dev *hdev, void *data) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + struct psmode_cmd_payload pcmd; + struct sk_buff *skb; + u8 *status; + + if (psdata->target_ps_mode == PS_MODE_ENABLE) + pcmd.ps_cmd = BT_PS_ENABLE; + else + pcmd.ps_cmd = BT_PS_DISABLE; + pcmd.c2h_ps_interval = __cpu_to_le16(psdata->c2h_ps_interval); + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd), &pcmd); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Setting Power Save mode failed (%ld)", PTR_ERR(skb)); + return PTR_ERR(skb); + } + + status = skb_pull_data(skb, 1); + if (status) { + if (!*status) + psdata->cur_psmode = psdata->target_ps_mode; + else + psdata->target_ps_mode = psdata->cur_psmode; + if (psdata->cur_psmode == PS_MODE_ENABLE) + ps_start_timer(nxpdev); + else + ps_wakeup(nxpdev); + bt_dev_dbg(hdev, "Power Save mode response: status=%d, ps_mode=%d", + *status, psdata->cur_psmode); + } + kfree_skb(skb); + + return 0; +} + +static int send_wakeup_method_cmd(struct hci_dev *hdev, void *data) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + struct wakeup_cmd_payload pcmd; + struct sk_buff *skb; + u8 *status; + + pcmd.c2h_wakeupmode = psdata->c2h_wakeupmode; + pcmd.c2h_wakeup_gpio = psdata->c2h_wakeup_gpio; + switch (psdata->h2c_wakeupmode) { + case WAKEUP_METHOD_DTR: + pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_DSR; + break; + case WAKEUP_METHOD_BREAK: + default: + pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_BREAK; + break; + } + pcmd.h2c_wakeup_gpio = 0xff; + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd), &pcmd); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Setting wake-up method failed (%ld)", PTR_ERR(skb)); + return PTR_ERR(skb); + } + + status = skb_pull_data(skb, 1); + if (status) { + if (*status == 0) + psdata->cur_h2c_wakeupmode = psdata->h2c_wakeupmode; + else + psdata->h2c_wakeupmode = psdata->cur_h2c_wakeupmode; + bt_dev_dbg(hdev, "Set Wakeup Method response: status=%d, h2c_wakeupmode=%d", + *status, psdata->cur_h2c_wakeupmode); + } + kfree_skb(skb); + + return 0; +} + +static void ps_init(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + + serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_RTS); + usleep_range(5000, 10000); + serdev_device_set_tiocm(nxpdev->serdev, TIOCM_RTS, 0); + usleep_range(5000, 10000); + + switch (psdata->h2c_wakeupmode) { + case WAKEUP_METHOD_DTR: + serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR); + serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0); + break; + case WAKEUP_METHOD_BREAK: + default: + serdev_device_break_ctl(nxpdev->serdev, -1); + usleep_range(5000, 10000); + serdev_device_break_ctl(nxpdev->serdev, 0); + usleep_range(5000, 10000); + break; + } + if (psdata->cur_h2c_wakeupmode != psdata->h2c_wakeupmode) + hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL); + if (psdata->cur_psmode != psdata->target_ps_mode) + hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL); +} + +/* NXP Firmware Download Feature */ +static int nxp_download_firmware(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int err = 0; + + nxpdev->fw_dnld_v1_offset = 0; + nxpdev->fw_v1_sent_bytes = 0; + nxpdev->fw_v1_expected_len = HDR_LEN; + nxpdev->fw_v3_offset_correction = 0; + nxpdev->baudrate_changed = false; + nxpdev->timeout_changed = false; + + serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, 0); + nxpdev->current_baudrate = HCI_NXP_PRI_BAUDRATE; + + /* Wait till FW is downloaded and CTS becomes low */ + err = wait_event_interruptible_timeout(nxpdev->fw_dnld_done_wait_q, + !test_bit(BTNXPUART_FW_DOWNLOADING, + &nxpdev->tx_state), + msecs_to_jiffies(60000)); + if (err == 0) { + bt_dev_err(hdev, "FW Download Timeout."); + return -ETIMEDOUT; + } + + serdev_device_set_flow_control(nxpdev->serdev, 1); + err = serdev_device_wait_for_cts(nxpdev->serdev, 1, 60000); + if (err < 0) { + bt_dev_err(hdev, "CTS is still high. FW Download failed."); + return err; + } + release_firmware(nxpdev->fw); + memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name)); + + /* Allow the downloaded FW to initialize */ + usleep_range(800 * USEC_PER_MSEC, 1 * USEC_PER_SEC); + + return 0; +} + +static void nxp_send_ack(u8 ack, struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + u8 ack_nak[2]; + int len = 1; + + ack_nak[0] = ack; + if (ack == NXP_ACK_V3) { + ack_nak[1] = crc8(crc8_table, ack_nak, 1, 0xff); + len = 2; + } + serdev_device_write_buf(nxpdev->serdev, ack_nak, len); +} + +static bool nxp_fw_change_baudrate(struct hci_dev *hdev, u16 req_len) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_bootloader_cmd nxp_cmd5; + struct uart_config uart_config; + + if (req_len == sizeof(nxp_cmd5)) { + nxp_cmd5.header = __cpu_to_le32(5); + nxp_cmd5.arg = 0; + nxp_cmd5.payload_len = __cpu_to_le32(sizeof(uart_config)); + /* FW expects swapped CRC bytes */ + nxp_cmd5.crc = __cpu_to_be32(crc32_be(0UL, (char *)&nxp_cmd5, + sizeof(nxp_cmd5) - 4)); + + serdev_device_write_buf(nxpdev->serdev, (u8 *)&nxp_cmd5, sizeof(nxp_cmd5)); + nxpdev->fw_v3_offset_correction += req_len; + } else if (req_len == sizeof(uart_config)) { + uart_config.clkdiv.address = __cpu_to_le32(CLKDIVADDR); + uart_config.clkdiv.value = __cpu_to_le32(0x00c00000); + uart_config.uartdiv.address = __cpu_to_le32(UARTDIVADDR); + uart_config.uartdiv.value = __cpu_to_le32(1); + uart_config.mcr.address = __cpu_to_le32(UARTMCRADDR); + uart_config.mcr.value = __cpu_to_le32(MCR); + uart_config.re_init.address = __cpu_to_le32(UARTREINITADDR); + uart_config.re_init.value = __cpu_to_le32(INIT); + uart_config.icr.address = __cpu_to_le32(UARTICRADDR); + uart_config.icr.value = __cpu_to_le32(ICR); + uart_config.fcr.address = __cpu_to_le32(UARTFCRADDR); + uart_config.fcr.value = __cpu_to_le32(FCR); + /* FW expects swapped CRC bytes */ + uart_config.crc = __cpu_to_be32(crc32_be(0UL, (char *)&uart_config, + sizeof(uart_config) - 4)); + + serdev_device_write_buf(nxpdev->serdev, (u8 *)&uart_config, sizeof(uart_config)); + serdev_device_wait_until_sent(nxpdev->serdev, 0); + nxpdev->fw_v3_offset_correction += req_len; + return true; + } + return false; +} + +static bool nxp_fw_change_timeout(struct hci_dev *hdev, u16 req_len) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_bootloader_cmd nxp_cmd7; + + if (req_len != sizeof(nxp_cmd7)) + return false; + + nxp_cmd7.header = __cpu_to_le32(7); + nxp_cmd7.arg = __cpu_to_le32(0x70); + nxp_cmd7.payload_len = 0; + /* FW expects swapped CRC bytes */ + nxp_cmd7.crc = __cpu_to_be32(crc32_be(0UL, (char *)&nxp_cmd7, + sizeof(nxp_cmd7) - 4)); + serdev_device_write_buf(nxpdev->serdev, (u8 *)&nxp_cmd7, sizeof(nxp_cmd7)); + serdev_device_wait_until_sent(nxpdev->serdev, 0); + nxpdev->fw_v3_offset_correction += req_len; + return true; +} + +static u32 nxp_get_data_len(const u8 *buf) +{ + struct nxp_bootloader_cmd *hdr = (struct nxp_bootloader_cmd *)buf; + + return __le32_to_cpu(hdr->payload_len); +} + +static bool is_fw_downloading(struct btnxpuart_dev *nxpdev) +{ + return test_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); +} + +static bool process_boot_signature(struct btnxpuart_dev *nxpdev) +{ + if (test_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state)) { + clear_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state); + wake_up_interruptible(&nxpdev->check_boot_sign_wait_q); + return false; + } + return is_fw_downloading(nxpdev); +} + +static int nxp_request_firmware(struct hci_dev *hdev, const char *fw_name) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int err = 0; + + if (!strlen(nxpdev->fw_name)) { + snprintf(nxpdev->fw_name, MAX_FW_FILE_NAME_LEN, "%s", fw_name); + + bt_dev_dbg(hdev, "Request Firmware: %s", nxpdev->fw_name); + err = request_firmware(&nxpdev->fw, nxpdev->fw_name, &hdev->dev); + if (err < 0) { + bt_dev_err(hdev, "Firmware file %s not found", nxpdev->fw_name); + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + } + } + return err; +} + +/* for legacy chipsets with V1 bootloader */ +static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct btnxpuart_data *nxp_data = nxpdev->nxp_data; + struct v1_data_req *req; + u32 requested_len; + + if (!process_boot_signature(nxpdev)) + goto free_skb; + + req = (struct v1_data_req *)skb_pull_data(skb, sizeof(struct v1_data_req)); + if (!req) + goto free_skb; + + if ((req->len ^ req->len_comp) != 0xffff) { + bt_dev_dbg(hdev, "ERR: Send NAK"); + nxp_send_ack(NXP_NAK_V1, hdev); + goto free_skb; + } + nxp_send_ack(NXP_ACK_V1, hdev); + + if (nxp_data->fw_download_3M_baudrate) { + if (!nxpdev->timeout_changed) { + nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, req->len); + goto free_skb; + } + if (!nxpdev->baudrate_changed) { + nxpdev->baudrate_changed = nxp_fw_change_baudrate(hdev, req->len); + if (nxpdev->baudrate_changed) { + serdev_device_set_baudrate(nxpdev->serdev, + HCI_NXP_SEC_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, 1); + nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; + } + goto free_skb; + } + } + + if (nxp_request_firmware(hdev, nxp_data->fw_name)) + goto free_skb; + + requested_len = req->len; + if (requested_len == 0) { + bt_dev_dbg(hdev, "FW Downloaded Successfully: %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; + } + if (requested_len & 0x01) { + /* The CRC did not match at the other end. + * Simply send the same bytes again. + */ + requested_len = nxpdev->fw_v1_sent_bytes; + bt_dev_dbg(hdev, "CRC error. Resend %d bytes of FW.", requested_len); + } else { + nxpdev->fw_dnld_v1_offset += nxpdev->fw_v1_sent_bytes; + + /* The FW bin file is made up of many blocks of + * 16 byte header and payload data chunks. If the + * FW has requested a header, read the payload length + * info from the header, before sending the header. + * In the next iteration, the FW should request the + * payload data chunk, which should be equal to the + * payload length read from header. If there is a + * mismatch, clearly the driver and FW are out of sync, + * and we need to re-send the previous header again. + */ + if (requested_len == nxpdev->fw_v1_expected_len) { + if (requested_len == HDR_LEN) + nxpdev->fw_v1_expected_len = nxp_get_data_len(nxpdev->fw->data + + nxpdev->fw_dnld_v1_offset); + else + nxpdev->fw_v1_expected_len = HDR_LEN; + } else if (requested_len == HDR_LEN) { + /* FW download out of sync. Send previous chunk again */ + nxpdev->fw_dnld_v1_offset -= nxpdev->fw_v1_sent_bytes; + nxpdev->fw_v1_expected_len = HDR_LEN; + } + } + + if (nxpdev->fw_dnld_v1_offset + requested_len <= nxpdev->fw->size) + serdev_device_write_buf(nxpdev->serdev, + nxpdev->fw->data + nxpdev->fw_dnld_v1_offset, + requested_len); + nxpdev->fw_v1_sent_bytes = requested_len; + +free_skb: + kfree_skb(skb); + return 0; +} + +static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid) +{ + char *fw_name = NULL; + + switch (chipid) { + case CHIP_ID_W9098: + fw_name = FIRMWARE_W9098; + break; + case CHIP_ID_IW416: + fw_name = FIRMWARE_IW416; + break; + case CHIP_ID_IW612: + fw_name = FIRMWARE_IW612; + break; + default: + bt_dev_err(hdev, "Unknown chip signature %04x", chipid); + break; + } + return fw_name; +} + +static int nxp_recv_chip_ver_v3(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct v3_start_ind *req = skb_pull_data(skb, sizeof(struct v3_start_ind)); + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + if (!process_boot_signature(nxpdev)) + goto free_skb; + + if (!nxp_request_firmware(hdev, nxp_get_fw_name_from_chipid(hdev, req->chip_id))) + nxp_send_ack(NXP_ACK_V3, hdev); + +free_skb: + kfree_skb(skb); + return 0; +} + +static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct v3_data_req *req; + + if (!process_boot_signature(nxpdev)) + goto free_skb; + + req = (struct v3_data_req *)skb_pull_data(skb, sizeof(struct v3_data_req)); + if (!req || !nxpdev->fw) + goto free_skb; + + nxp_send_ack(NXP_ACK_V3, hdev); + + if (!nxpdev->timeout_changed) { + nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, req->len); + goto free_skb; + } + + if (!nxpdev->baudrate_changed) { + nxpdev->baudrate_changed = nxp_fw_change_baudrate(hdev, req->len); + if (nxpdev->baudrate_changed) { + serdev_device_set_baudrate(nxpdev->serdev, + HCI_NXP_SEC_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, 1); + nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; + } + goto free_skb; + } + + if (req->len == 0) { + bt_dev_dbg(hdev, "FW Downloaded Successfully: %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; + } + if (req->error) + bt_dev_dbg(hdev, "FW Download received err 0x%02x from chip", req->error); + + if (req->offset < nxpdev->fw_v3_offset_correction) { + /* This scenario should ideally never occur. But if it ever does, + * FW is out of sync and needs a power cycle. + */ + bt_dev_err(hdev, "Something went wrong during FW download. Please power cycle and try again"); + goto free_skb; + } + + serdev_device_write_buf(nxpdev->serdev, + nxpdev->fw->data + req->offset - nxpdev->fw_v3_offset_correction, + req->len); + +free_skb: + kfree_skb(skb); + return 0; +} + +static int nxp_set_baudrate_cmd(struct hci_dev *hdev, void *data) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + __le32 new_baudrate = __cpu_to_le32(nxpdev->new_baudrate); + struct ps_data *psdata = &nxpdev->psdata; + struct sk_buff *skb; + u8 *status; + + if (!psdata) + return 0; + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4, (u8 *)&new_baudrate); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Setting baudrate failed (%ld)", PTR_ERR(skb)); + return PTR_ERR(skb); + } + + status = (u8 *)skb_pull_data(skb, 1); + if (status) { + if (*status == 0) { + serdev_device_set_baudrate(nxpdev->serdev, nxpdev->new_baudrate); + nxpdev->current_baudrate = nxpdev->new_baudrate; + } + bt_dev_dbg(hdev, "Set baudrate response: status=%d, baudrate=%d", + *status, nxpdev->new_baudrate); + } + kfree_skb(skb); + + return 0; +} + +static int nxp_set_ind_reset(struct hci_dev *hdev, void *data) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct sk_buff *skb; + u8 *status; + u8 pcmd = 0; + int err = 0; + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + status = skb_pull_data(skb, 1); + if (!status || *status) + goto free_skb; + + set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + err = nxp_download_firmware(hdev); + if (err < 0) + goto free_skb; + serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate); + nxpdev->current_baudrate = nxpdev->fw_init_baudrate; + if (nxpdev->current_baudrate != HCI_NXP_SEC_BAUDRATE) { + nxpdev->new_baudrate = HCI_NXP_SEC_BAUDRATE; + nxp_set_baudrate_cmd(hdev, NULL); + } + hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL); + hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL); + +free_skb: + kfree_skb(skb); + return err; +} + +/* NXP protocol */ +static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev) +{ + serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, 0); + set_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state); + + return wait_event_interruptible_timeout(nxpdev->check_boot_sign_wait_q, + !test_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, + &nxpdev->tx_state), + msecs_to_jiffies(1000)); +} + +static int nxp_setup(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int err = 0; + + set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + init_waitqueue_head(&nxpdev->fw_dnld_done_wait_q); + init_waitqueue_head(&nxpdev->check_boot_sign_wait_q); + + if (nxp_check_boot_sign(nxpdev)) { + bt_dev_dbg(hdev, "Need FW Download."); + err = nxp_download_firmware(hdev); + if (err < 0) + return err; + } else { + bt_dev_dbg(hdev, "FW already running."); + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + } + + serdev_device_set_flow_control(nxpdev->serdev, 1); + device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate", + &nxpdev->fw_init_baudrate); + if (!nxpdev->fw_init_baudrate) + nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE; + serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate); + nxpdev->current_baudrate = nxpdev->fw_init_baudrate; + + if (nxpdev->current_baudrate != HCI_NXP_SEC_BAUDRATE) { + nxpdev->new_baudrate = HCI_NXP_SEC_BAUDRATE; + hci_cmd_sync_queue(hdev, nxp_set_baudrate_cmd, NULL, NULL); + } + + ps_init(hdev); + + return 0; +} + +static int btnxpuart_queue_skb(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); + skb_queue_tail(&nxpdev->txq, skb); + btnxpuart_tx_wakeup(nxpdev); + return 0; +} + +static int nxp_enqueue(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + struct hci_command_hdr *hdr; + struct psmode_cmd_payload ps_parm; + struct wakeup_cmd_payload wakeup_parm; + __le32 baudrate_parm; + + /* if vendor commands are received from user space (e.g. hcitool), update + * driver flags accordingly and ask driver to re-send the command to FW. + * In case the payload for any command does not match expected payload + * length, let the firmware and user space program handle it, or throw + * an error. + */ + if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT && !psdata->driver_sent_cmd) { + hdr = (struct hci_command_hdr *)skb->data; + if (hdr->plen != (skb->len - HCI_COMMAND_HDR_SIZE)) + return btnxpuart_queue_skb(hdev, skb); + + switch (__le16_to_cpu(hdr->opcode)) { + case HCI_NXP_AUTO_SLEEP_MODE: + if (hdr->plen == sizeof(ps_parm)) { + memcpy(&ps_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen); + if (ps_parm.ps_cmd == BT_PS_ENABLE) + psdata->target_ps_mode = PS_MODE_ENABLE; + else if (ps_parm.ps_cmd == BT_PS_DISABLE) + psdata->target_ps_mode = PS_MODE_DISABLE; + psdata->c2h_ps_interval = __le16_to_cpu(ps_parm.c2h_ps_interval); + hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL); + goto free_skb; + } + break; + case HCI_NXP_WAKEUP_METHOD: + if (hdr->plen == sizeof(wakeup_parm)) { + memcpy(&wakeup_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen); + psdata->c2h_wakeupmode = wakeup_parm.c2h_wakeupmode; + psdata->c2h_wakeup_gpio = wakeup_parm.c2h_wakeup_gpio; + psdata->h2c_wakeup_gpio = wakeup_parm.h2c_wakeup_gpio; + switch (wakeup_parm.h2c_wakeupmode) { + case BT_CTRL_WAKEUP_METHOD_DSR: + psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR; + break; + case BT_CTRL_WAKEUP_METHOD_BREAK: + default: + psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK; + break; + } + hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL); + goto free_skb; + } + break; + case HCI_NXP_SET_OPER_SPEED: + if (hdr->plen == sizeof(baudrate_parm)) { + memcpy(&baudrate_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen); + nxpdev->new_baudrate = __le32_to_cpu(baudrate_parm); + hci_cmd_sync_queue(hdev, nxp_set_baudrate_cmd, NULL, NULL); + goto free_skb; + } + break; + case HCI_NXP_IND_RESET: + if (hdr->plen == 1) { + hci_cmd_sync_queue(hdev, nxp_set_ind_reset, NULL, NULL); + goto free_skb; + } + break; + default: + break; + } + } + + return btnxpuart_queue_skb(hdev, skb); + +free_skb: + kfree_skb(skb); + return 0; +} + +static struct sk_buff *nxp_dequeue(void *data) +{ + struct btnxpuart_dev *nxpdev = (struct btnxpuart_dev *)data; + + ps_wakeup(nxpdev); + ps_start_timer(nxpdev); + return skb_dequeue(&nxpdev->txq); +} + +/* btnxpuart based on serdev */ +static void btnxpuart_tx_work(struct work_struct *work) +{ + struct btnxpuart_dev *nxpdev = container_of(work, struct btnxpuart_dev, + tx_work); + struct serdev_device *serdev = nxpdev->serdev; + struct hci_dev *hdev = nxpdev->hdev; + struct sk_buff *skb; + int len; + + while ((skb = nxp_dequeue(nxpdev))) { + len = serdev_device_write_buf(serdev, skb->data, skb->len); + hdev->stat.byte_tx += len; + + skb_pull(skb, len); + if (skb->len > 0) { + skb_queue_head(&nxpdev->txq, skb); + break; + } + + switch (hci_skb_pkt_type(skb)) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + } + + kfree_skb(skb); + } + clear_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state); +} + +static int btnxpuart_open(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int err = 0; + + err = serdev_device_open(nxpdev->serdev); + if (err) { + bt_dev_err(hdev, "Unable to open UART device %s", + dev_name(&nxpdev->serdev->dev)); + } else { + set_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state); + } + return err; +} + +static int btnxpuart_close(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + serdev_device_close(nxpdev->serdev); + clear_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state); + return 0; +} + +static int btnxpuart_flush(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + /* Flush any pending characters */ + serdev_device_write_flush(nxpdev->serdev); + skb_queue_purge(&nxpdev->txq); + + cancel_work_sync(&nxpdev->tx_work); + + kfree_skb(nxpdev->rx_skb); + nxpdev->rx_skb = NULL; + + return 0; +} + +static const struct h4_recv_pkt nxp_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { NXP_RECV_FW_REQ_V1, .recv = nxp_recv_fw_req_v1 }, + { NXP_RECV_CHIP_VER_V3, .recv = nxp_recv_chip_ver_v3 }, + { NXP_RECV_FW_REQ_V3, .recv = nxp_recv_fw_req_v3 }, +}; + +static bool is_valid_bootloader_signature(const u8 *data, size_t count) +{ + if ((*data == NXP_V1_FW_REQ_PKT && count == sizeof(struct v1_data_req) + 1) || + (*data == NXP_V3_FW_REQ_PKT && count == sizeof(struct v3_data_req) + 1) || + (*data == NXP_V3_CHIP_VER_PKT && count == sizeof(struct v3_start_ind) + 1)) + return true; + else + return false; +} + +static int btnxpuart_receive_buf(struct serdev_device *serdev, const u8 *data, + size_t count) +{ + struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev); + + if (is_fw_downloading(nxpdev) && !is_valid_bootloader_signature(data, count)) { + /* Unknown bootloader signature, skip without returning error */ + return count; + } + + ps_start_timer(nxpdev); + + nxpdev->rx_skb = h4_recv_buf(nxpdev->hdev, nxpdev->rx_skb, data, count, + nxp_recv_pkts, ARRAY_SIZE(nxp_recv_pkts)); + if (IS_ERR(nxpdev->rx_skb)) { + int err = PTR_ERR(nxpdev->rx_skb); + + bt_dev_err(nxpdev->hdev, "Frame reassembly failed (%d)", err); + nxpdev->rx_skb = NULL; + return err; + } + nxpdev->hdev->stat.byte_rx += count; + return count; +} + +static void btnxpuart_write_wakeup(struct serdev_device *serdev) +{ + serdev_device_write_wakeup(serdev); +} + +static const struct serdev_device_ops btnxpuart_client_ops = { + .receive_buf = btnxpuart_receive_buf, + .write_wakeup = btnxpuart_write_wakeup, +}; + +static int nxp_serdev_probe(struct serdev_device *serdev) +{ + struct hci_dev *hdev; + struct btnxpuart_dev *nxpdev; + + nxpdev = devm_kzalloc(&serdev->dev, sizeof(*nxpdev), GFP_KERNEL); + if (!nxpdev) + return -ENOMEM; + + nxpdev->nxp_data = (struct btnxpuart_data *)device_get_match_data(&serdev->dev); + + nxpdev->serdev = serdev; + serdev_device_set_drvdata(serdev, nxpdev); + + serdev_device_set_client_ops(serdev, &btnxpuart_client_ops); + + INIT_WORK(&nxpdev->tx_work, btnxpuart_tx_work); + skb_queue_head_init(&nxpdev->txq); + + crc8_populate_msb(crc8_table, POLYNOMIAL8); + + /* Initialize and register HCI device */ + hdev = hci_alloc_dev(); + if (!hdev) { + dev_err(&serdev->dev, "Can't allocate HCI device\n"); + return -ENOMEM; + } + + nxpdev->hdev = hdev; + + hdev->bus = HCI_UART; + hci_set_drvdata(hdev, nxpdev); + + hdev->manufacturer = MANUFACTURER_NXP; + hdev->open = btnxpuart_open; + hdev->close = btnxpuart_close; + hdev->flush = btnxpuart_flush; + hdev->setup = nxp_setup; + hdev->send = nxp_enqueue; + SET_HCIDEV_DEV(hdev, &serdev->dev); + + if (hci_register_dev(hdev) < 0) { + dev_err(&serdev->dev, "Can't register HCI device\n"); + hci_free_dev(hdev); + return -ENODEV; + } + + ps_init_work(hdev); + ps_init_timer(hdev); + + return 0; +} + +static void nxp_serdev_remove(struct serdev_device *serdev) +{ + struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev); + struct hci_dev *hdev = nxpdev->hdev; + + /* Restore FW baudrate to fw_init_baudrate if changed. + * This will ensure FW baudrate is in sync with + * driver baudrate in case this driver is re-inserted. + */ + if (nxpdev->current_baudrate != nxpdev->fw_init_baudrate) { + nxpdev->new_baudrate = nxpdev->fw_init_baudrate; + nxp_set_baudrate_cmd(hdev, NULL); + } + + ps_cancel_timer(nxpdev); + hci_unregister_dev(hdev); + hci_free_dev(hdev); +} + +static struct btnxpuart_data w8987_data = { + .fw_download_3M_baudrate = true, + .fw_name = FIRMWARE_W8987, +}; + +static struct btnxpuart_data w8997_data = { + .fw_download_3M_baudrate = false, + .fw_name = FIRMWARE_W8997, +}; + +static const struct of_device_id nxpuart_of_match_table[] = { + { .compatible = "nxp,88w8987-bt", .data = &w8987_data }, + { .compatible = "nxp,88w8997-bt", .data = &w8997_data }, + { } +}; +MODULE_DEVICE_TABLE(of, nxpuart_of_match_table); + +static struct serdev_device_driver nxp_serdev_driver = { + .probe = nxp_serdev_probe, + .remove = nxp_serdev_remove, + .driver = { + .name = "btnxpuart", + .of_match_table = of_match_ptr(nxpuart_of_match_table), + }, +}; + +module_serdev_device_driver(nxp_serdev_driver); + +MODULE_AUTHOR("Neeraj Sanjay Kale "); +MODULE_DESCRIPTION("NXP Bluetooth Serial driver"); +MODULE_LICENSE("GPL"); From 11e29b4a3fda975f4427800285b7e4544f4e9152 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 22 Mar 2023 12:16:27 +0100 Subject: [PATCH 31/72] Bluetooth: NXP: select CONFIG_CRC8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver needs both CRC32 and CRC8 to link correctly: ld: drivers/bluetooth/btnxpuart.o: in function `nxp_serdev_probe': drivers/bluetooth/btnxpuart.c:1214: undefined reference to `crc8_populate_msb' ld: drivers/bluetooth/btnxpuart.o: in function `nxp_send_ack': drivers/bluetooth/btnxpuart.c:559: undefined reference to `crc8' ld: drivers/bluetooth/btnxpuart.c:559: undefined reference to `crc8' Fixes: 3e662aa4453a ("Bluetooth: NXP: Add protocol support for NXP Bluetooth chipsets") Signed-off-by: Arnd Bergmann Reviewed-by: Ilpo Järvinen Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 0703bdd441407..f84322d375de3 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -469,6 +469,7 @@ config BT_NXPUART tristate "NXP protocol support" depends on SERIAL_DEV_BUS select CRC32 + select CRC8 help NXP is serial driver required for NXP Bluetooth devices with UART interface. From 730a1d1a93a3e30c3723f87af97a8517334b2203 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 22 Mar 2023 19:52:02 -0500 Subject: [PATCH 32/72] bluetooth: Add device 0bda:887b to device tables This device is part of a Realtek RTW8852BE chip. The device table entry is as follows: T: Bus=03 Lev=01 Prnt=01 Port=12 Cnt=02 Dev#= 3 Spd=12 MxCh= 0 D: Ver= 1.00 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0bda ProdID=887b Rev= 0.00 S: Manufacturer=Realtek S: Product=Bluetooth Radio S: SerialNumber=00e04c000001 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=500mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Larry Finger 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 7382b021f3dfb..128deab609e46 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -540,6 +540,8 @@ static const struct usb_device_id blacklist_table[] = { /* Realtek 8852BE Bluetooth devices */ { USB_DEVICE(0x0cb8, 0xc559), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0bda, 0x887b), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek Bluetooth devices */ { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), From 069f534247bb6db4f8c2c2ea8e9155abf495c37e Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 22 Mar 2023 19:52:03 -0500 Subject: [PATCH 33/72] bluetooth: Add device 13d3:3571 to device tables This device is part of a Realtek RTW8852BE chip. The device table is as follows: T: Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 D: Ver= 1.00 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=13d3 ProdID=3571 Rev= 0.00 S: Manufacturer=Realtek S: Product=Bluetooth Radio S: SerialNumber=00e04c000001 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=500mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Larry Finger 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 128deab609e46..9bf1d4ee74c23 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -542,6 +542,8 @@ static const struct usb_device_id blacklist_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0bda, 0x887b), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3571), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek Bluetooth devices */ { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), From 253cf30e8d3d001850a95c4729d668f916b037ab Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 21 Mar 2023 19:48:26 +0800 Subject: [PATCH 34/72] Bluetooth: btrtl: check for NULL in btrtl_set_quirks() The btrtl_set_quirks() has accessed btrtl_dev->ic_info->lmp_subver since b8e482d02513. However, if installing a Realtek Bluetooth controller without the driver supported, it will hit the NULL point accessed. Add a check for NULL to avoid the Kernel Oops. Signed-off-by: Max Chou Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 8fa7733812adf..0f256a8abef42 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -905,6 +905,9 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) break; } + if (!btrtl_dev->ic_info) + return; + switch (btrtl_dev->ic_info->lmp_subver) { case RTL_ROM_LMP_8703B: /* 8723CS reports two pages for local ext features, From b1d00baaa0298a5e033bc00a92522e4fd07900a8 Mon Sep 17 00:00:00 2001 From: Steev Klimaszewski Date: Sun, 26 Mar 2023 18:38:09 -0500 Subject: [PATCH 35/72] dt-bindings: net: Add WCN6855 Bluetooth Add bindings for the QTI WCN6855 chipset. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Steev Klimaszewski Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/qualcomm-bluetooth.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index a6a6b0e4df7a4..68f78b90d23a3 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -23,6 +23,7 @@ properties: - qcom,wcn3998-bt - qcom,qca6390-bt - qcom,wcn6750-bt + - qcom,wcn6855-bt enable-gpios: maxItems: 1 @@ -133,6 +134,22 @@ allOf: - vddrfa1p7-supply - vddrfa1p2-supply - vddasd-supply + - if: + properties: + compatible: + contains: + enum: + - qcom,wcn6855-bt + then: + required: + - enable-gpios + - swctrl-gpios + - vddio-supply + - vddbtcxmx-supply + - vddrfacmn-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p7-supply examples: - | From 095327fede005f4b14d40b2183b2f7965c739dbd Mon Sep 17 00:00:00 2001 From: Steev Klimaszewski Date: Sun, 26 Mar 2023 18:38:10 -0500 Subject: [PATCH 36/72] Bluetooth: hci_qca: Add support for QTI Bluetooth chip wcn6855 Add regulators, GPIOs and changes required to power on/off wcn6855. Add support for firmware download for wcn6855 which is in the linux-firmware repository as hpbtfw21.tlv and hpnv21.bin. Based on the assumption that this is similar to the wcn6750 Tested-on: BTFW.HSP.2.1.0-00538-VER_PATCHZ-1 Signed-off-by: Steev Klimaszewski Reviewed-by: Bjorn Andersson Tested-by: Bjorn Andersson Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btqca.c | 14 ++++++++- drivers/bluetooth/btqca.h | 10 +++++++ drivers/bluetooth/hci_qca.c | 57 ++++++++++++++++++++++++++++--------- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index c9064d34d8308..fd0941fe86081 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -614,6 +614,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, config.type = ELF_TYPE_PATCH; snprintf(config.fwname, sizeof(config.fwname), "qca/msbtfw%02x.mbn", rom_ver); + } else if (soc_type == QCA_WCN6855) { + snprintf(config.fwname, sizeof(config.fwname), + "qca/hpbtfw%02x.tlv", rom_ver); } else { snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.bin", soc_ver); @@ -648,6 +651,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, else if (soc_type == QCA_WCN6750) snprintf(config.fwname, sizeof(config.fwname), "qca/msnv%02x.bin", rom_ver); + else if (soc_type == QCA_WCN6855) + snprintf(config.fwname, sizeof(config.fwname), + "qca/hpnv%02x.bin", rom_ver); else snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin", soc_ver); @@ -685,11 +691,17 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, return err; } - if (soc_type == QCA_WCN3991 || soc_type == QCA_WCN6750) { + switch (soc_type) { + case QCA_WCN3991: + case QCA_WCN6750: + case QCA_WCN6855: /* get fw build info */ err = qca_read_fw_build_info(hdev); if (err < 0) return err; + break; + default: + break; } bt_dev_info(hdev, "QCA setup on UART is completed"); diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index 61e9a50e66ae1..b884095bcd9d0 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -147,6 +147,7 @@ enum qca_btsoc_type { QCA_WCN3991, QCA_QCA6390, QCA_WCN6750, + QCA_WCN6855, }; #if IS_ENABLED(CONFIG_BT_QCA) @@ -168,6 +169,10 @@ static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type) { return soc_type == QCA_WCN6750; } +static inline bool qca_is_wcn6855(enum qca_btsoc_type soc_type) +{ + return soc_type == QCA_WCN6855; +} #else @@ -206,6 +211,11 @@ static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type) return false; } +static inline bool qca_is_wcn6855(enum qca_btsoc_type soc_type) +{ + return false; +} + static inline int qca_send_pre_shutdown_cmd(struct hci_dev *hdev) { return -EOPNOTSUPP; diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index a199ed52d76dc..1597797ff1693 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1317,7 +1317,8 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) /* Give the controller time to process the request */ if (qca_is_wcn399x(qca_soc_type(hu)) || - qca_is_wcn6750(qca_soc_type(hu))) + qca_is_wcn6750(qca_soc_type(hu)) || + qca_is_wcn6855(qca_soc_type(hu))) usleep_range(1000, 10000); else msleep(300); @@ -1394,7 +1395,8 @@ static unsigned int qca_get_speed(struct hci_uart *hu, static int qca_check_speeds(struct hci_uart *hu) { if (qca_is_wcn399x(qca_soc_type(hu)) || - qca_is_wcn6750(qca_soc_type(hu))) { + qca_is_wcn6750(qca_soc_type(hu)) || + qca_is_wcn6855(qca_soc_type(hu))) { if (!qca_get_speed(hu, QCA_INIT_SPEED) && !qca_get_speed(hu, QCA_OPER_SPEED)) return -EINVAL; @@ -1428,7 +1430,8 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) * changing the baudrate of chip and host. */ if (qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type)) + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type)) hci_uart_set_flow_control(hu, true); if (soc_type == QCA_WCN3990) { @@ -1446,7 +1449,8 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) error: if (qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type)) + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type)) hci_uart_set_flow_control(hu, false); if (soc_type == QCA_WCN3990) { @@ -1682,7 +1686,8 @@ static int qca_power_on(struct hci_dev *hdev) return 0; if (qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type)) { + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type)) { ret = qca_regulator_init(hu); } else { qcadev = serdev_device_get_drvdata(hu->serdev); @@ -1723,7 +1728,8 @@ static int qca_setup(struct hci_uart *hu) bt_dev_info(hdev, "setting up %s", qca_is_wcn399x(soc_type) ? "wcn399x" : - (soc_type == QCA_WCN6750) ? "wcn6750" : "ROME/QCA6390"); + (soc_type == QCA_WCN6750) ? "wcn6750" : + (soc_type == QCA_WCN6855) ? "wcn6855" : "ROME/QCA6390"); qca->memdump_state = QCA_MEMDUMP_IDLE; @@ -1735,7 +1741,8 @@ static int qca_setup(struct hci_uart *hu) clear_bit(QCA_SSR_TRIGGERED, &qca->flags); if (qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type)) { + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type)) { set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); hci_set_aosp_capable(hdev); @@ -1757,7 +1764,8 @@ static int qca_setup(struct hci_uart *hu) } if (!(qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type))) { + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type))) { /* Get QCA version information */ ret = qca_read_soc_version(hdev, &ver, soc_type); if (ret) @@ -1883,6 +1891,20 @@ static const struct qca_device_data qca_soc_data_wcn6750 __maybe_unused = { .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES, }; +static const struct qca_device_data qca_soc_data_wcn6855 = { + .soc_type = QCA_WCN6855, + .vregs = (struct qca_vreg []) { + { "vddio", 5000 }, + { "vddbtcxmx", 126000 }, + { "vddrfacmn", 12500 }, + { "vddrfa0p8", 102000 }, + { "vddrfa1p7", 302000 }, + { "vddrfa1p2", 257000 }, + }, + .num_vregs = 6, + .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES, +}; + static void qca_power_shutdown(struct hci_uart *hu) { struct qca_serdev *qcadev; @@ -1912,7 +1934,7 @@ static void qca_power_shutdown(struct hci_uart *hu) host_set_baudrate(hu, 2400); qca_send_power_pulse(hu, false); qca_regulator_disable(qcadev); - } else if (soc_type == QCA_WCN6750) { + } else if (soc_type == QCA_WCN6750 || soc_type == QCA_WCN6855) { gpiod_set_value_cansleep(qcadev->bt_en, 0); msleep(100); qca_regulator_disable(qcadev); @@ -2047,7 +2069,8 @@ static int qca_serdev_probe(struct serdev_device *serdev) if (data && (qca_is_wcn399x(data->soc_type) || - qca_is_wcn6750(data->soc_type))) { + qca_is_wcn6750(data->soc_type) || + qca_is_wcn6855(data->soc_type))) { qcadev->btsoc_type = data->soc_type; qcadev->bt_power = devm_kzalloc(&serdev->dev, sizeof(struct qca_power), @@ -2067,14 +2090,18 @@ static int qca_serdev_probe(struct serdev_device *serdev) qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR_OR_NULL(qcadev->bt_en) && data->soc_type == QCA_WCN6750) { + if (IS_ERR_OR_NULL(qcadev->bt_en) && + (data->soc_type == QCA_WCN6750 || + data->soc_type == QCA_WCN6855)) { dev_err(&serdev->dev, "failed to acquire BT_EN gpio\n"); power_ctrl_enabled = false; } qcadev->sw_ctrl = devm_gpiod_get_optional(&serdev->dev, "swctrl", GPIOD_IN); - if (IS_ERR_OR_NULL(qcadev->sw_ctrl) && data->soc_type == QCA_WCN6750) + if (IS_ERR_OR_NULL(qcadev->sw_ctrl) && + (data->soc_type == QCA_WCN6750 || + data->soc_type == QCA_WCN6855)) dev_warn(&serdev->dev, "failed to acquire SW_CTRL gpio\n"); qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL); @@ -2150,8 +2177,9 @@ static void qca_serdev_remove(struct serdev_device *serdev) struct qca_power *power = qcadev->bt_power; if ((qca_is_wcn399x(qcadev->btsoc_type) || - qca_is_wcn6750(qcadev->btsoc_type)) && - power->vregs_on) + qca_is_wcn6750(qcadev->btsoc_type) || + qca_is_wcn6855(qcadev->btsoc_type)) && + power->vregs_on) qca_power_shutdown(&qcadev->serdev_hu); else if (qcadev->susclk) clk_disable_unprepare(qcadev->susclk); @@ -2335,6 +2363,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = { { .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991}, { .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998}, { .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750}, + { .compatible = "qcom,wcn6855-bt", .data = &qca_soc_data_wcn6855}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match); From 77f542b10c535c9a93bf8afdd2665524935807c2 Mon Sep 17 00:00:00 2001 From: Chethan T N Date: Tue, 21 Mar 2023 10:03:10 +0530 Subject: [PATCH 37/72] Bluetooth: btintel: Add LE States quirk support Basically all Intel controllers support both Central/Peripheral LE states. This patch enables the LE States quirk by default on all Solar and Magnertor Intel controllers. Signed-off-by: Chethan T N Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index af774688f1c0d..7a6dc05553f13 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -2684,9 +2684,8 @@ static int btintel_setup_combined(struct hci_dev *hdev) */ set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); - /* Valid LE States quirk for GfP */ - if (INTEL_HW_VARIANT(ver_tlv.cnvi_bt) == 0x18) - set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); + /* Apply LE States quirk from solar onwards */ + set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); /* Setup MSFT Extension support */ btintel_set_msft_opcode(hdev, From 9695ef876fd122cb7bbc04a4a93b8727d2e36bda Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Thu, 30 Mar 2023 09:58:23 -0700 Subject: [PATCH 38/72] Bluetooth: Add support for hci devcoredump Add devcoredump APIs to hci core so that drivers only have to provide the dump skbs instead of managing the synchronization and timeouts. The devcoredump APIs should be used in the following manner: - hci_devcoredump_init is called to allocate the dump. - hci_devcoredump_append is called to append any skbs with dump data OR hci_devcoredump_append_pattern is called to insert a pattern. - hci_devcoredump_complete is called when all dump packets have been sent OR hci_devcoredump_abort is called to indicate an error and cancel an ongoing dump collection. The high level APIs just prepare some skbs with the appropriate data and queue it for the dump to process. Packets part of the crashdump can be intercepted in the driver in interrupt context and forwarded directly to the devcoredump APIs. Internally, there are 5 states for the dump: idle, active, complete, abort and timeout. A devcoredump will only be in active state after it has been initialized. Once active, it accepts data to be appended, patterns to be inserted (i.e. memset) and a completion event or an abort event to generate a devcoredump. The timeout is initialized at the same time the dump is initialized (defaulting to 10s) and will be cleared either when the timeout occurs or the dump is complete or aborted. Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Manish Mandlik Reviewed-by: Abhishek Pandit-Subedi Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/coredump.h | 116 +++++++ include/net/bluetooth/hci_core.h | 14 + net/bluetooth/Makefile | 2 + net/bluetooth/coredump.c | 535 +++++++++++++++++++++++++++++++ net/bluetooth/hci_core.c | 1 + net/bluetooth/hci_sync.c | 2 + 6 files changed, 670 insertions(+) create mode 100644 include/net/bluetooth/coredump.h create mode 100644 net/bluetooth/coredump.c diff --git a/include/net/bluetooth/coredump.h b/include/net/bluetooth/coredump.h new file mode 100644 index 0000000000000..72f51b587a046 --- /dev/null +++ b/include/net/bluetooth/coredump.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 Google Corporation + */ + +#ifndef __COREDUMP_H +#define __COREDUMP_H + +#define DEVCOREDUMP_TIMEOUT msecs_to_jiffies(10000) /* 10 sec */ + +typedef void (*coredump_t)(struct hci_dev *hdev); +typedef void (*dmp_hdr_t)(struct hci_dev *hdev, struct sk_buff *skb); +typedef void (*notify_change_t)(struct hci_dev *hdev, int state); + +/* struct hci_devcoredump - Devcoredump state + * + * @supported: Indicates if FW dump collection is supported by driver + * @state: Current state of dump collection + * @timeout: Indicates a timeout for collecting the devcoredump + * + * @alloc_size: Total size of the dump + * @head: Start of the dump + * @tail: Pointer to current end of dump + * @end: head + alloc_size for easy comparisons + * + * @dump_q: Dump queue for state machine to process + * @dump_rx: Devcoredump state machine work + * @dump_timeout: Devcoredump timeout work + * + * @coredump: Called from the driver's .coredump() function. + * @dmp_hdr: Create a dump header to identify controller/fw/driver info + * @notify_change: Notify driver when devcoredump state has changed + */ +struct hci_devcoredump { + bool supported; + + enum devcoredump_state { + HCI_DEVCOREDUMP_IDLE, + HCI_DEVCOREDUMP_ACTIVE, + HCI_DEVCOREDUMP_DONE, + HCI_DEVCOREDUMP_ABORT, + HCI_DEVCOREDUMP_TIMEOUT, + } state; + + unsigned long timeout; + + size_t alloc_size; + char *head; + char *tail; + char *end; + + struct sk_buff_head dump_q; + struct work_struct dump_rx; + struct delayed_work dump_timeout; + + coredump_t coredump; + dmp_hdr_t dmp_hdr; + notify_change_t notify_change; +}; + +#ifdef CONFIG_DEV_COREDUMP + +void hci_devcd_reset(struct hci_dev *hdev); +void hci_devcd_rx(struct work_struct *work); +void hci_devcd_timeout(struct work_struct *work); + +int hci_devcd_register(struct hci_dev *hdev, coredump_t coredump, + dmp_hdr_t dmp_hdr, notify_change_t notify_change); +int hci_devcd_init(struct hci_dev *hdev, u32 dump_size); +int hci_devcd_append(struct hci_dev *hdev, struct sk_buff *skb); +int hci_devcd_append_pattern(struct hci_dev *hdev, u8 pattern, u32 len); +int hci_devcd_complete(struct hci_dev *hdev); +int hci_devcd_abort(struct hci_dev *hdev); + +#else + +static inline void hci_devcd_reset(struct hci_dev *hdev) {} +static inline void hci_devcd_rx(struct work_struct *work) {} +static inline void hci_devcd_timeout(struct work_struct *work) {} + +static inline int hci_devcd_register(struct hci_dev *hdev, coredump_t coredump, + dmp_hdr_t dmp_hdr, + notify_change_t notify_change) +{ + return -EOPNOTSUPP; +} + +static inline int hci_devcd_init(struct hci_dev *hdev, u32 dump_size) +{ + return -EOPNOTSUPP; +} + +static inline int hci_devcd_append(struct hci_dev *hdev, struct sk_buff *skb) +{ + return -EOPNOTSUPP; +} + +static inline int hci_devcd_append_pattern(struct hci_dev *hdev, + u8 pattern, u32 len) +{ + return -EOPNOTSUPP; +} + +static inline int hci_devcd_complete(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} + +static inline int hci_devcd_abort(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} + +#endif /* CONFIG_DEV_COREDUMP */ + +#endif /* __COREDUMP_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e22e45fbe8dbb..a461335beacf0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -32,6 +32,7 @@ #include #include #include +#include /* HCI priority */ #define HCI_PRIO_MAX 7 @@ -590,6 +591,10 @@ struct hci_dev { const char *fw_info; struct dentry *debugfs; +#ifdef CONFIG_DEV_COREDUMP + struct hci_devcoredump dump; +#endif + struct device dev; struct rfkill *rfkill; @@ -1497,6 +1502,15 @@ static inline void hci_set_aosp_capable(struct hci_dev *hdev) #endif } +static inline void hci_devcd_setup(struct hci_dev *hdev) +{ +#ifdef CONFIG_DEV_COREDUMP + INIT_WORK(&hdev->dump.dump_rx, hci_devcd_rx); + INIT_DELAYED_WORK(&hdev->dump.dump_timeout, hci_devcd_timeout); + skb_queue_head_init(&hdev->dump.dump_q); +#endif +} + int hci_dev_open(__u16 dev); int hci_dev_close(__u16 dev); int hci_dev_do_close(struct hci_dev *hdev); diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 0e7b7db427501..141ac1fda0bfa 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -17,6 +17,8 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ ecdh_helper.o hci_request.o mgmt_util.o mgmt_config.o hci_codec.o \ eir.o hci_sync.o +bluetooth-$(CONFIG_DEV_COREDUMP) += coredump.o + bluetooth-$(CONFIG_BT_BREDR) += sco.o bluetooth-$(CONFIG_BT_LE) += iso.o bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o diff --git a/net/bluetooth/coredump.c b/net/bluetooth/coredump.c new file mode 100644 index 0000000000000..08fa98505454a --- /dev/null +++ b/net/bluetooth/coredump.c @@ -0,0 +1,535 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Google Corporation + */ + +#include + +#include +#include + +enum hci_devcoredump_pkt_type { + HCI_DEVCOREDUMP_PKT_INIT, + HCI_DEVCOREDUMP_PKT_SKB, + HCI_DEVCOREDUMP_PKT_PATTERN, + HCI_DEVCOREDUMP_PKT_COMPLETE, + HCI_DEVCOREDUMP_PKT_ABORT, +}; + +struct hci_devcoredump_skb_cb { + u16 pkt_type; +}; + +struct hci_devcoredump_skb_pattern { + u8 pattern; + u32 len; +} __packed; + +#define hci_dmp_cb(skb) ((struct hci_devcoredump_skb_cb *)((skb)->cb)) + +#define DBG_UNEXPECTED_STATE() \ + bt_dev_dbg(hdev, \ + "Unexpected packet (%d) for state (%d). ", \ + hci_dmp_cb(skb)->pkt_type, hdev->dump.state) + +#define MAX_DEVCOREDUMP_HDR_SIZE 512 /* bytes */ + +static int hci_devcd_update_hdr_state(char *buf, size_t size, int state) +{ + int len = 0; + + if (!buf) + return 0; + + len = scnprintf(buf, size, "Bluetooth devcoredump\nState: %d\n", state); + + return len + 1; /* scnprintf adds \0 at the end upon state rewrite */ +} + +/* Call with hci_dev_lock only. */ +static int hci_devcd_update_state(struct hci_dev *hdev, int state) +{ + bt_dev_dbg(hdev, "Updating devcoredump state from %d to %d.", + hdev->dump.state, state); + + hdev->dump.state = state; + + return hci_devcd_update_hdr_state(hdev->dump.head, + hdev->dump.alloc_size, state); +} + +static int hci_devcd_mkheader(struct hci_dev *hdev, struct sk_buff *skb) +{ + char dump_start[] = "--- Start dump ---\n"; + char hdr[80]; + int hdr_len; + + hdr_len = hci_devcd_update_hdr_state(hdr, sizeof(hdr), + HCI_DEVCOREDUMP_IDLE); + skb_put_data(skb, hdr, hdr_len); + + if (hdev->dump.dmp_hdr) + hdev->dump.dmp_hdr(hdev, skb); + + skb_put_data(skb, dump_start, strlen(dump_start)); + + return skb->len; +} + +/* Do not call with hci_dev_lock since this calls driver code. */ +static void hci_devcd_notify(struct hci_dev *hdev, int state) +{ + if (hdev->dump.notify_change) + hdev->dump.notify_change(hdev, state); +} + +/* Call with hci_dev_lock only. */ +void hci_devcd_reset(struct hci_dev *hdev) +{ + hdev->dump.head = NULL; + hdev->dump.tail = NULL; + hdev->dump.alloc_size = 0; + + hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_IDLE); + + cancel_delayed_work(&hdev->dump.dump_timeout); + skb_queue_purge(&hdev->dump.dump_q); +} + +/* Call with hci_dev_lock only. */ +static void hci_devcd_free(struct hci_dev *hdev) +{ + if (hdev->dump.head) + vfree(hdev->dump.head); + + hci_devcd_reset(hdev); +} + +/* Call with hci_dev_lock only. */ +static int hci_devcd_alloc(struct hci_dev *hdev, u32 size) +{ + hdev->dump.head = vmalloc(size); + if (!hdev->dump.head) + return -ENOMEM; + + hdev->dump.alloc_size = size; + hdev->dump.tail = hdev->dump.head; + hdev->dump.end = hdev->dump.head + size; + + hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_IDLE); + + return 0; +} + +/* Call with hci_dev_lock only. */ +static bool hci_devcd_copy(struct hci_dev *hdev, char *buf, u32 size) +{ + if (hdev->dump.tail + size > hdev->dump.end) + return false; + + memcpy(hdev->dump.tail, buf, size); + hdev->dump.tail += size; + + return true; +} + +/* Call with hci_dev_lock only. */ +static bool hci_devcd_memset(struct hci_dev *hdev, u8 pattern, u32 len) +{ + if (hdev->dump.tail + len > hdev->dump.end) + return false; + + memset(hdev->dump.tail, pattern, len); + hdev->dump.tail += len; + + return true; +} + +/* Call with hci_dev_lock only. */ +static int hci_devcd_prepare(struct hci_dev *hdev, u32 dump_size) +{ + struct sk_buff *skb; + int dump_hdr_size; + int err = 0; + + skb = alloc_skb(MAX_DEVCOREDUMP_HDR_SIZE, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + dump_hdr_size = hci_devcd_mkheader(hdev, skb); + + if (hci_devcd_alloc(hdev, dump_hdr_size + dump_size)) { + err = -ENOMEM; + goto hdr_free; + } + + /* Insert the device header */ + if (!hci_devcd_copy(hdev, skb->data, skb->len)) { + bt_dev_err(hdev, "Failed to insert header"); + hci_devcd_free(hdev); + + err = -ENOMEM; + goto hdr_free; + } + +hdr_free: + kfree_skb(skb); + + return err; +} + +static void hci_devcd_handle_pkt_init(struct hci_dev *hdev, struct sk_buff *skb) +{ + u32 *dump_size; + + if (hdev->dump.state != HCI_DEVCOREDUMP_IDLE) { + DBG_UNEXPECTED_STATE(); + return; + } + + if (skb->len != sizeof(*dump_size)) { + bt_dev_dbg(hdev, "Invalid dump init pkt"); + return; + } + + dump_size = skb_pull_data(skb, sizeof(*dump_size)); + if (!*dump_size) { + bt_dev_err(hdev, "Zero size dump init pkt"); + return; + } + + if (hci_devcd_prepare(hdev, *dump_size)) { + bt_dev_err(hdev, "Failed to prepare for dump"); + return; + } + + hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_ACTIVE); + queue_delayed_work(hdev->workqueue, &hdev->dump.dump_timeout, + hdev->dump.timeout); +} + +static void hci_devcd_handle_pkt_skb(struct hci_dev *hdev, struct sk_buff *skb) +{ + if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) { + DBG_UNEXPECTED_STATE(); + return; + } + + if (!hci_devcd_copy(hdev, skb->data, skb->len)) + bt_dev_dbg(hdev, "Failed to insert skb"); +} + +static void hci_devcd_handle_pkt_pattern(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_devcoredump_skb_pattern *pattern; + + if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) { + DBG_UNEXPECTED_STATE(); + return; + } + + if (skb->len != sizeof(*pattern)) { + bt_dev_dbg(hdev, "Invalid pattern skb"); + return; + } + + pattern = skb_pull_data(skb, sizeof(*pattern)); + + if (!hci_devcd_memset(hdev, pattern->pattern, pattern->len)) + bt_dev_dbg(hdev, "Failed to set pattern"); +} + +static void hci_devcd_handle_pkt_complete(struct hci_dev *hdev, + struct sk_buff *skb) +{ + u32 dump_size; + + if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) { + DBG_UNEXPECTED_STATE(); + return; + } + + hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_DONE); + dump_size = hdev->dump.tail - hdev->dump.head; + + bt_dev_dbg(hdev, "complete with size %u (expect %zu)", dump_size, + hdev->dump.alloc_size); + + dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size, GFP_KERNEL); +} + +static void hci_devcd_handle_pkt_abort(struct hci_dev *hdev, + struct sk_buff *skb) +{ + u32 dump_size; + + if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) { + DBG_UNEXPECTED_STATE(); + return; + } + + hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_ABORT); + dump_size = hdev->dump.tail - hdev->dump.head; + + bt_dev_dbg(hdev, "aborted with size %u (expect %zu)", dump_size, + hdev->dump.alloc_size); + + /* Emit a devcoredump with the available data */ + dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size, GFP_KERNEL); +} + +/* Bluetooth devcoredump state machine. + * + * Devcoredump states: + * + * HCI_DEVCOREDUMP_IDLE: The default state. + * + * HCI_DEVCOREDUMP_ACTIVE: A devcoredump will be in this state once it has + * been initialized using hci_devcd_init(). Once active, the driver + * can append data using hci_devcd_append() or insert a pattern + * using hci_devcd_append_pattern(). + * + * HCI_DEVCOREDUMP_DONE: Once the dump collection is complete, the drive + * can signal the completion using hci_devcd_complete(). A + * devcoredump is generated indicating the completion event and + * then the state machine is reset to the default state. + * + * HCI_DEVCOREDUMP_ABORT: The driver can cancel ongoing dump collection in + * case of any error using hci_devcd_abort(). A devcoredump is + * still generated with the available data indicating the abort + * event and then the state machine is reset to the default state. + * + * HCI_DEVCOREDUMP_TIMEOUT: A timeout timer for HCI_DEVCOREDUMP_TIMEOUT sec + * is started during devcoredump initialization. Once the timeout + * occurs, the driver is notified, a devcoredump is generated with + * the available data indicating the timeout event and then the + * state machine is reset to the default state. + * + * The driver must register using hci_devcd_register() before using the hci + * devcoredump APIs. + */ +void hci_devcd_rx(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, dump.dump_rx); + struct sk_buff *skb; + int start_state; + + while ((skb = skb_dequeue(&hdev->dump.dump_q))) { + /* Return if timeout occurs. The timeout handler function + * hci_devcd_timeout() will report the available dump data. + */ + if (hdev->dump.state == HCI_DEVCOREDUMP_TIMEOUT) { + kfree_skb(skb); + return; + } + + hci_dev_lock(hdev); + start_state = hdev->dump.state; + + switch (hci_dmp_cb(skb)->pkt_type) { + case HCI_DEVCOREDUMP_PKT_INIT: + hci_devcd_handle_pkt_init(hdev, skb); + break; + + case HCI_DEVCOREDUMP_PKT_SKB: + hci_devcd_handle_pkt_skb(hdev, skb); + break; + + case HCI_DEVCOREDUMP_PKT_PATTERN: + hci_devcd_handle_pkt_pattern(hdev, skb); + break; + + case HCI_DEVCOREDUMP_PKT_COMPLETE: + hci_devcd_handle_pkt_complete(hdev, skb); + break; + + case HCI_DEVCOREDUMP_PKT_ABORT: + hci_devcd_handle_pkt_abort(hdev, skb); + break; + + default: + bt_dev_dbg(hdev, "Unknown packet (%d) for state (%d). ", + hci_dmp_cb(skb)->pkt_type, hdev->dump.state); + break; + } + + hci_dev_unlock(hdev); + kfree_skb(skb); + + /* Notify the driver about any state changes before resetting + * the state machine + */ + if (start_state != hdev->dump.state) + hci_devcd_notify(hdev, hdev->dump.state); + + /* Reset the state machine if the devcoredump is complete */ + hci_dev_lock(hdev); + if (hdev->dump.state == HCI_DEVCOREDUMP_DONE || + hdev->dump.state == HCI_DEVCOREDUMP_ABORT) + hci_devcd_reset(hdev); + hci_dev_unlock(hdev); + } +} +EXPORT_SYMBOL(hci_devcd_rx); + +void hci_devcd_timeout(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, + dump.dump_timeout.work); + u32 dump_size; + + hci_devcd_notify(hdev, HCI_DEVCOREDUMP_TIMEOUT); + + hci_dev_lock(hdev); + + cancel_work(&hdev->dump.dump_rx); + + hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_TIMEOUT); + + dump_size = hdev->dump.tail - hdev->dump.head; + bt_dev_dbg(hdev, "timeout with size %u (expect %zu)", dump_size, + hdev->dump.alloc_size); + + /* Emit a devcoredump with the available data */ + dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size, GFP_KERNEL); + + hci_devcd_reset(hdev); + + hci_dev_unlock(hdev); +} +EXPORT_SYMBOL(hci_devcd_timeout); + +int hci_devcd_register(struct hci_dev *hdev, coredump_t coredump, + dmp_hdr_t dmp_hdr, notify_change_t notify_change) +{ + /* Driver must implement coredump() and dmp_hdr() functions for + * bluetooth devcoredump. The coredump() should trigger a coredump + * event on the controller when the device's coredump sysfs entry is + * written to. The dmp_hdr() should create a dump header to identify + * the controller/fw/driver info. + */ + if (!coredump || !dmp_hdr) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->dump.coredump = coredump; + hdev->dump.dmp_hdr = dmp_hdr; + hdev->dump.notify_change = notify_change; + hdev->dump.supported = true; + hdev->dump.timeout = DEVCOREDUMP_TIMEOUT; + hci_dev_unlock(hdev); + + return 0; +} +EXPORT_SYMBOL(hci_devcd_register); + +static inline bool hci_devcd_enabled(struct hci_dev *hdev) +{ + return hdev->dump.supported; +} + +int hci_devcd_init(struct hci_dev *hdev, u32 dump_size) +{ + struct sk_buff *skb; + + if (!hci_devcd_enabled(hdev)) + return -EOPNOTSUPP; + + skb = alloc_skb(sizeof(dump_size), GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_INIT; + skb_put_data(skb, &dump_size, sizeof(dump_size)); + + skb_queue_tail(&hdev->dump.dump_q, skb); + queue_work(hdev->workqueue, &hdev->dump.dump_rx); + + return 0; +} +EXPORT_SYMBOL(hci_devcd_init); + +int hci_devcd_append(struct hci_dev *hdev, struct sk_buff *skb) +{ + if (!skb) + return -ENOMEM; + + if (!hci_devcd_enabled(hdev)) { + kfree_skb(skb); + return -EOPNOTSUPP; + } + + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_SKB; + + skb_queue_tail(&hdev->dump.dump_q, skb); + queue_work(hdev->workqueue, &hdev->dump.dump_rx); + + return 0; +} +EXPORT_SYMBOL(hci_devcd_append); + +int hci_devcd_append_pattern(struct hci_dev *hdev, u8 pattern, u32 len) +{ + struct hci_devcoredump_skb_pattern p; + struct sk_buff *skb; + + if (!hci_devcd_enabled(hdev)) + return -EOPNOTSUPP; + + skb = alloc_skb(sizeof(p), GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + p.pattern = pattern; + p.len = len; + + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_PATTERN; + skb_put_data(skb, &p, sizeof(p)); + + skb_queue_tail(&hdev->dump.dump_q, skb); + queue_work(hdev->workqueue, &hdev->dump.dump_rx); + + return 0; +} +EXPORT_SYMBOL(hci_devcd_append_pattern); + +int hci_devcd_complete(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + if (!hci_devcd_enabled(hdev)) + return -EOPNOTSUPP; + + skb = alloc_skb(0, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_COMPLETE; + + skb_queue_tail(&hdev->dump.dump_q, skb); + queue_work(hdev->workqueue, &hdev->dump.dump_rx); + + return 0; +} +EXPORT_SYMBOL(hci_devcd_complete); + +int hci_devcd_abort(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + if (!hci_devcd_enabled(hdev)) + return -EOPNOTSUPP; + + skb = alloc_skb(0, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_ABORT; + + skb_queue_tail(&hdev->dump.dump_q, skb); + queue_work(hdev->workqueue, &hdev->dump.dump_rx); + + return 0; +} +EXPORT_SYMBOL(hci_devcd_abort); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 334e308451f53..393b317ae68f6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2544,6 +2544,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv) INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout); INIT_DELAYED_WORK(&hdev->ncmd_timer, hci_ncmd_timeout); + hci_devcd_setup(hdev); hci_request_setup(hdev); hci_init_sysfs(hdev); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 31231f0e4a28f..f21497ebc8149 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4727,6 +4727,8 @@ int hci_dev_open_sync(struct hci_dev *hdev) goto done; } + hci_devcd_reset(hdev); + set_bit(HCI_RUNNING, &hdev->flags); hci_sock_dev_event(hdev, HCI_DEV_OPEN); From ab4e4380d4e158486e595013a2635190e07e28ce Mon Sep 17 00:00:00 2001 From: Manish Mandlik Date: Thu, 30 Mar 2023 09:58:24 -0700 Subject: [PATCH 39/72] Bluetooth: Add vhci devcoredump support Add devcoredump support for vhci that creates forcce_devcoredump debugfs entry. This is used for mgmt-tester tests. Signed-off-by: Manish Mandlik Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/Kconfig | 1 + drivers/bluetooth/hci_vhci.c | 97 ++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index f84322d375de3..bc211c324206b 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -363,6 +363,7 @@ config BT_HCIBLUECARD config BT_HCIVHCI tristate "HCI VHCI (Virtual HCI device) driver" + select WANT_DEV_COREDUMP help Bluetooth Virtual HCI device driver. This driver is required if you want to use HCI Emulation software. diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index c443c3b0a4da5..691fe93b19768 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -278,6 +278,100 @@ static int vhci_setup(struct hci_dev *hdev) return 0; } +static void vhci_coredump(struct hci_dev *hdev) +{ + /* No need to do anything */ +} + +static void vhci_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb) +{ + char buf[80]; + + snprintf(buf, sizeof(buf), "Controller Name: vhci_ctrl\n"); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Firmware Version: vhci_fw\n"); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Driver: vhci_drv\n"); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Vendor: vhci\n"); + skb_put_data(skb, buf, strlen(buf)); +} + +#define MAX_COREDUMP_LINE_LEN 40 + +struct devcoredump_test_data { + enum devcoredump_state state; + unsigned int timeout; + char data[MAX_COREDUMP_LINE_LEN]; +}; + +static inline void force_devcd_timeout(struct hci_dev *hdev, + unsigned int timeout) +{ +#ifdef CONFIG_DEV_COREDUMP + hdev->dump.timeout = msecs_to_jiffies(timeout * 1000); +#endif +} + +static ssize_t force_devcd_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct vhci_data *data = file->private_data; + struct hci_dev *hdev = data->hdev; + struct sk_buff *skb = NULL; + struct devcoredump_test_data dump_data; + int ret; + + ret = simple_write_to_buffer(&dump_data, sizeof(dump_data), ppos, + user_buf, count); + if (ret < count) + return ret; + + skb = alloc_skb(sizeof(dump_data.data), GFP_ATOMIC); + if (!skb) + return -ENOMEM; + skb_put_data(skb, &dump_data.data, sizeof(dump_data.data)); + + hci_devcd_register(hdev, vhci_coredump, vhci_coredump_hdr, NULL); + + /* Force the devcoredump timeout */ + if (dump_data.timeout) + force_devcd_timeout(hdev, dump_data.timeout); + + ret = hci_devcd_init(hdev, skb->len); + if (ret) { + BT_ERR("Failed to generate devcoredump"); + kfree_skb(skb); + return ret; + } + + hci_devcd_append(hdev, skb); + + switch (dump_data.state) { + case HCI_DEVCOREDUMP_DONE: + hci_devcd_complete(hdev); + break; + case HCI_DEVCOREDUMP_ABORT: + hci_devcd_abort(hdev); + break; + case HCI_DEVCOREDUMP_TIMEOUT: + /* Do nothing */ + break; + default: + return -EINVAL; + } + + return count; +} + +static const struct file_operations force_devcoredump_fops = { + .open = simple_open, + .write = force_devcd_write, +}; + static int __vhci_create_device(struct vhci_data *data, __u8 opcode) { struct hci_dev *hdev; @@ -355,6 +449,9 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode) debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data, &aosp_capable_fops); + debugfs_create_file("force_devcoredump", 0644, hdev->debugfs, data, + &force_devcoredump_fops); + hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; skb_put_u8(skb, 0xff); From 4f9c1a089623ac606e79a7886646939e0e2f89bf Mon Sep 17 00:00:00 2001 From: Manish Mandlik Date: Thu, 30 Mar 2023 09:58:25 -0700 Subject: [PATCH 40/72] Bluetooth: btusb: Add btusb devcoredump support This patch implements the btusb driver side .coredump() callback to trigger a devcoredump via sysfs. Signed-off-by: Manish Mandlik Reviewed-by: Abhishek Pandit-Subedi Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9bf1d4ee74c23..498e131023676 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4397,6 +4397,17 @@ static int btusb_resume(struct usb_interface *intf) } #endif +#ifdef CONFIG_DEV_COREDUMP +static void btusb_coredump(struct device *dev) +{ + struct btusb_data *data = dev_get_drvdata(dev); + struct hci_dev *hdev = data->hdev; + + if (hdev->dump.coredump) + hdev->dump.coredump(hdev); +} +#endif + static struct usb_driver btusb_driver = { .name = "btusb", .probe = btusb_probe, @@ -4408,6 +4419,14 @@ static struct usb_driver btusb_driver = { .id_table = btusb_table, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, + +#ifdef CONFIG_DEV_COREDUMP + .drvwrap = { + .driver = { + .coredump = btusb_coredump, + }, + }, +#endif }; module_usb_driver(btusb_driver); From af395330abed142a2685bf3d17a938544816bf3c Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Thu, 30 Mar 2023 09:58:26 -0700 Subject: [PATCH 41/72] Bluetooth: btintel: Add Intel devcoredump support Intercept debug exception events from the controller and put them into a devcoredump using hci devcoredump APIs. The debug exception contains data in a TLV format and it will be parsed in userspace. Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Manish Mandlik Reviewed-by: Abhishek Pandit-Subedi Reviewed-by: Chethan Tumkur Narayan Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 72 ++++++++++++++++++++++++++++++++++++- drivers/bluetooth/btintel.h | 12 +++++-- drivers/bluetooth/btusb.c | 54 ++++++++++++++++++++++++---- 3 files changed, 128 insertions(+), 10 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 7a6dc05553f13..d9349ba48281e 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -43,6 +43,12 @@ struct cmd_write_boot_params { u8 fw_build_yy; } __packed; +static struct { + const char *driver_name; + u8 hw_variant; + u32 fw_build_num; +} coredump_info; + int btintel_check_bdaddr(struct hci_dev *hdev) { struct hci_rp_read_bd_addr *bda; @@ -315,6 +321,9 @@ int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver) return -EINVAL; } + coredump_info.hw_variant = ver->hw_variant; + coredump_info.fw_build_num = ver->fw_build_num; + bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u", variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f, ver->fw_build_num, ver->fw_build_ww, @@ -509,6 +518,9 @@ static int btintel_version_info_tlv(struct hci_dev *hdev, return -EINVAL; } + coredump_info.hw_variant = INTEL_HW_VARIANT(version->cnvi_bt); + coredump_info.fw_build_num = version->build_num; + bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant, 2000 + (version->timestamp >> 8), version->timestamp & 0xff, version->build_type, version->build_num); @@ -1462,6 +1474,59 @@ int btintel_set_quality_report(struct hci_dev *hdev, bool enable) } EXPORT_SYMBOL_GPL(btintel_set_quality_report); +static void btintel_coredump(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc4e, 0, NULL, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Coredump failed (%ld)", PTR_ERR(skb)); + return; + } + + kfree_skb(skb); +} + +static void btintel_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb) +{ + char buf[80]; + + snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n", + coredump_info.hw_variant); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n", + coredump_info.fw_build_num); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info.driver_name); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Vendor: Intel\n"); + skb_put_data(skb, buf, strlen(buf)); +} + +static int btintel_register_devcoredump_support(struct hci_dev *hdev) +{ + struct intel_debug_features features; + int err; + + err = btintel_read_debug_features(hdev, &features); + if (err) { + bt_dev_info(hdev, "Error reading debug features"); + return err; + } + + if (!(features.page1[0] & 0x3f)) { + bt_dev_dbg(hdev, "Telemetry exception format not supported"); + return -EOPNOTSUPP; + } + + hci_devcd_register(hdev, btintel_coredump, btintel_dmp_hdr, NULL); + + return err; +} + static const struct firmware *btintel_legacy_rom_get_fw(struct hci_dev *hdev, struct intel_version *ver) { @@ -2597,6 +2662,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) btintel_set_msft_opcode(hdev, ver.hw_variant); err = btintel_bootloader_setup(hdev, &ver); + btintel_register_devcoredump_support(hdev); break; default: bt_dev_err(hdev, "Unsupported Intel hw variant (%u)", @@ -2670,6 +2736,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) btintel_set_msft_opcode(hdev, ver.hw_variant); err = btintel_bootloader_setup(hdev, &ver); + btintel_register_devcoredump_support(hdev); break; case 0x17: case 0x18: @@ -2692,6 +2759,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) INTEL_HW_VARIANT(ver_tlv.cnvi_bt)); err = btintel_bootloader_setup_tlv(hdev, &ver_tlv); + btintel_register_devcoredump_support(hdev); break; default: bt_dev_err(hdev, "Unsupported Intel hw variant (%u)", @@ -2741,7 +2809,7 @@ static int btintel_shutdown_combined(struct hci_dev *hdev) return 0; } -int btintel_configure_setup(struct hci_dev *hdev) +int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name) { hdev->manufacturer = 2; hdev->setup = btintel_setup_combined; @@ -2750,6 +2818,8 @@ int btintel_configure_setup(struct hci_dev *hdev) hdev->set_diag = btintel_set_diag_combined; hdev->set_bdaddr = btintel_set_bdaddr; + coredump_info.driver_name = driver_name; + return 0; } EXPORT_SYMBOL_GPL(btintel_configure_setup); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 8fdb65b66315a..d6a1dc8d8a828 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -143,6 +143,13 @@ struct btintel_loc_aware_reg { __le32 delta; } __packed; +#define INTEL_TLV_TYPE_ID 0x01 + +#define INTEL_TLV_SYSTEM_EXCEPTION 0x00 +#define INTEL_TLV_FATAL_EXCEPTION 0x01 +#define INTEL_TLV_DEBUG_EXCEPTION 0x02 +#define INTEL_TLV_TEST_EXCEPTION 0xDE + #define INTEL_HW_PLATFORM(cnvx_bt) ((u8)(((cnvx_bt) & 0x0000ff00) >> 8)) #define INTEL_HW_VARIANT(cnvx_bt) ((u8)(((cnvx_bt) & 0x003f0000) >> 16)) #define INTEL_CNVX_TOP_TYPE(cnvx_top) ((cnvx_top) & 0x00000fff) @@ -212,7 +219,7 @@ int btintel_read_boot_params(struct hci_dev *hdev, struct intel_boot_params *params); int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver, const struct firmware *fw, u32 *boot_param); -int btintel_configure_setup(struct hci_dev *hdev); +int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name); void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len); void btintel_secure_send_result(struct hci_dev *hdev, const void *ptr, unsigned int len); @@ -293,7 +300,8 @@ static inline int btintel_download_firmware(struct hci_dev *dev, return -EOPNOTSUPP; } -static inline int btintel_configure_setup(struct hci_dev *hdev) +static inline int btintel_configure_setup(struct hci_dev *hdev, + const char *driver_name) { return -ENODEV; } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 498e131023676..2303b0a663231 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2386,16 +2386,47 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer, return btusb_recv_bulk(data, buffer, count); } +static int btusb_intel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct intel_tlv *tlv = (void *)&skb->data[5]; + + /* The first event is always an event type TLV */ + if (tlv->type != INTEL_TLV_TYPE_ID) + goto recv_frame; + + switch (tlv->val[0]) { + case INTEL_TLV_SYSTEM_EXCEPTION: + case INTEL_TLV_FATAL_EXCEPTION: + case INTEL_TLV_DEBUG_EXCEPTION: + case INTEL_TLV_TEST_EXCEPTION: + /* Generate devcoredump from exception */ + if (!hci_devcd_init(hdev, skb->len)) { + hci_devcd_append(hdev, skb); + hci_devcd_complete(hdev); + } else { + bt_dev_err(hdev, "Failed to generate devcoredump"); + kfree_skb(skb); + } + return 0; + default: + bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]); + } + +recv_frame: + return hci_recv_frame(hdev, skb); +} + static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) { - if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) { - struct hci_event_hdr *hdr = (void *)skb->data; + struct hci_event_hdr *hdr = (void *)skb->data; + const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 }; - if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && - hdr->plen > 0) { - const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; - unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; + if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && + hdr->plen > 0) { + const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; + unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; + if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) { switch (skb->data[2]) { case 0x02: /* When switching to the operational firmware @@ -2414,6 +2445,15 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) break; } } + + /* Handle all diagnostics events separately. May still call + * hci_recv_frame. + */ + if (len >= sizeof(diagnostics_hdr) && + memcmp(&skb->data[2], diagnostics_hdr, + sizeof(diagnostics_hdr)) == 0) { + return btusb_intel_diagnostics(hdev, skb); + } } return hci_recv_frame(hdev, skb); @@ -4018,7 +4058,7 @@ static int btusb_probe(struct usb_interface *intf, /* Combined Intel Device setup to support multiple setup routine */ if (id->driver_info & BTUSB_INTEL_COMBINED) { - err = btintel_configure_setup(hdev); + err = btintel_configure_setup(hdev, btusb_driver.name); if (err) goto out_free_dev; From 0fe8c8d071343fa9278980ce4b6f8e6ea24a2ed1 Mon Sep 17 00:00:00 2001 From: Iulia Tanasescu Date: Fri, 31 Mar 2023 18:38:01 +0300 Subject: [PATCH 42/72] Bluetooth: Split bt_iso_qos into dedicated structures Split bt_iso_qos into dedicated unicast and broadcast structures and add additional broadcast parameters. Fixes: eca0ae4aea66 ("Bluetooth: Add initial implementation of BIS connections") Signed-off-by: Iulia Tanasescu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/bluetooth.h | 43 +++++--- include/net/bluetooth/hci_core.h | 9 +- net/bluetooth/hci_conn.c | 162 ++++++++++++++++-------------- net/bluetooth/hci_event.c | 33 +++--- net/bluetooth/iso.c | 125 ++++++++++++++++++----- 5 files changed, 237 insertions(+), 135 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index bcc5a4cd2c17b..1b4230cd42a37 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -1,6 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright 2023 NXP Written 2000,2001 by Maxim Krasnyansky @@ -171,23 +172,39 @@ struct bt_iso_io_qos { __u8 rtn; }; -struct bt_iso_qos { - union { - __u8 cig; - __u8 big; - }; - union { - __u8 cis; - __u8 bis; - }; - union { - __u8 sca; - __u8 sync_interval; - }; +struct bt_iso_ucast_qos { + __u8 cig; + __u8 cis; + __u8 sca; + __u8 packing; + __u8 framing; + struct bt_iso_io_qos in; + struct bt_iso_io_qos out; +}; + +struct bt_iso_bcast_qos { + __u8 big; + __u8 bis; + __u8 sync_interval; __u8 packing; __u8 framing; struct bt_iso_io_qos in; struct bt_iso_io_qos out; + __u8 encryption; + __u8 bcode[16]; + __u8 options; + __u16 skip; + __u16 sync_timeout; + __u8 sync_cte_type; + __u8 mse; + __u16 timeout; +}; + +struct bt_iso_qos { + union { + struct bt_iso_ucast_qos ucast; + struct bt_iso_bcast_qos bcast; + }; }; #define BT_ISO_PHY_1M 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a461335beacf0..f11689284112b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1,6 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. + Copyright 2023 NXP Written 2000,2001 by Maxim Krasnyansky @@ -1096,7 +1097,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev, if (bacmp(&c->dst, ba) || c->type != ISO_LINK) continue; - if (c->iso_qos.big == big && c->iso_qos.bis == bis) { + if (c->iso_qos.bcast.big == big && c->iso_qos.bcast.bis == bis) { rcu_read_unlock(); return c; } @@ -1205,7 +1206,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev, if (c->type != ISO_LINK) continue; - if (handle == c->iso_qos.cig) { + if (handle == c->iso_qos.ucast.cig) { rcu_read_unlock(); return c; } @@ -1228,7 +1229,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev, if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK) continue; - if (handle == c->iso_qos.big) { + if (handle == c->iso_qos.bcast.big) { rcu_read_unlock(); return c; } @@ -1337,7 +1338,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, struct bt_iso_qos *qos, __u8 data_len, __u8 *data); int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, - __u8 sid); + __u8 sid, struct bt_iso_qos *qos); int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos, __u16 sync_handle, __u8 num_bis, __u8 bis[]); int hci_conn_check_link_mode(struct hci_conn *conn); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 8455ba141ee61..5672b49245721 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1,6 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. + Copyright 2023 NXP Written 2000,2001 by Maxim Krasnyansky @@ -795,8 +796,8 @@ static void bis_list(struct hci_conn *conn, void *data) if (bacmp(&conn->dst, BDADDR_ANY)) return; - if (d->big != conn->iso_qos.big || d->bis == BT_ISO_QOS_BIS_UNSET || - d->bis != conn->iso_qos.bis) + if (d->big != conn->iso_qos.bcast.big || d->bis == BT_ISO_QOS_BIS_UNSET || + d->bis != conn->iso_qos.bcast.bis) return; d->count++; @@ -916,10 +917,10 @@ static void bis_cleanup(struct hci_conn *conn) if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags)) return; - hci_le_terminate_big(hdev, conn->iso_qos.big, - conn->iso_qos.bis); + hci_le_terminate_big(hdev, conn->iso_qos.bcast.big, + conn->iso_qos.bcast.bis); } else { - hci_le_big_terminate(hdev, conn->iso_qos.big, + hci_le_big_terminate(hdev, conn->iso_qos.bcast.big, conn->sync_handle); } } @@ -959,7 +960,7 @@ static void cis_cleanup(struct hci_conn *conn) struct iso_list_data d; memset(&d, 0, sizeof(d)); - d.cig = conn->iso_qos.cig; + d.cig = conn->iso_qos.ucast.cig; /* Check if ISO connection is a CIS and remove CIG if there are * no other connections using it. @@ -968,7 +969,7 @@ static void cis_cleanup(struct hci_conn *conn) if (d.count) return; - hci_le_remove_cig(hdev, conn->iso_qos.cig); + hci_le_remove_cig(hdev, conn->iso_qos.ucast.cig); } struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, @@ -1411,7 +1412,7 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos) struct iso_list_data data; /* Allocate a BIG if not set */ - if (qos->big == BT_ISO_QOS_BIG_UNSET) { + if (qos->bcast.big == BT_ISO_QOS_BIG_UNSET) { for (data.big = 0x00; data.big < 0xef; data.big++) { data.count = 0; data.bis = 0xff; @@ -1426,7 +1427,7 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos) return -EADDRNOTAVAIL; /* Update BIG */ - qos->big = data.big; + qos->bcast.big = data.big; } return 0; @@ -1437,7 +1438,7 @@ static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos) struct iso_list_data data; /* Allocate BIS if not set */ - if (qos->bis == BT_ISO_QOS_BIS_UNSET) { + if (qos->bcast.bis == BT_ISO_QOS_BIS_UNSET) { /* Find an unused adv set to advertise BIS, skip instance 0x00 * since it is reserved as general purpose set. */ @@ -1455,7 +1456,7 @@ static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos) return -EADDRNOTAVAIL; /* Update BIS */ - qos->bis = data.bis; + qos->bcast.bis = data.bis; } return 0; @@ -1484,8 +1485,8 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst, if (err) return ERR_PTR(err); - data.big = qos->big; - data.bis = qos->bis; + data.big = qos->bcast.big; + data.bis = qos->bcast.bis; data.count = 0; /* Check if there is already a matching BIG/BIS */ @@ -1493,7 +1494,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst, if (data.count) return ERR_PTR(-EADDRINUSE); - conn = hci_conn_hash_lookup_bis(hdev, dst, qos->big, qos->bis); + conn = hci_conn_hash_lookup_bis(hdev, dst, qos->bcast.big, qos->bcast.bis); if (conn) return ERR_PTR(-EADDRINUSE); @@ -1648,13 +1649,13 @@ static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos) { struct hci_cis_params *cis = &d->pdu.cis[d->pdu.cp.num_cis]; - cis->cis_id = qos->cis; - cis->c_sdu = cpu_to_le16(qos->out.sdu); - cis->p_sdu = cpu_to_le16(qos->in.sdu); - cis->c_phy = qos->out.phy ? qos->out.phy : qos->in.phy; - cis->p_phy = qos->in.phy ? qos->in.phy : qos->out.phy; - cis->c_rtn = qos->out.rtn; - cis->p_rtn = qos->in.rtn; + cis->cis_id = qos->ucast.cis; + cis->c_sdu = cpu_to_le16(qos->ucast.out.sdu); + cis->p_sdu = cpu_to_le16(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_rtn = qos->ucast.out.rtn; + cis->p_rtn = qos->ucast.in.rtn; d->pdu.cp.num_cis++; } @@ -1667,8 +1668,8 @@ static void cis_list(struct hci_conn *conn, void *data) if (!bacmp(&conn->dst, BDADDR_ANY)) return; - if (d->cig != conn->iso_qos.cig || d->cis == BT_ISO_QOS_CIS_UNSET || - d->cis != conn->iso_qos.cis) + if (d->cig != conn->iso_qos.ucast.cig || d->cis == BT_ISO_QOS_CIS_UNSET || + d->cis != conn->iso_qos.ucast.cis) return; d->count++; @@ -1687,17 +1688,18 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) memset(&cp, 0, sizeof(cp)); - cp.handle = qos->big; - cp.adv_handle = qos->bis; + cp.handle = qos->bcast.big; + cp.adv_handle = qos->bcast.bis; cp.num_bis = 0x01; - hci_cpu_to_le24(qos->out.interval, cp.bis.sdu_interval); - cp.bis.sdu = cpu_to_le16(qos->out.sdu); - cp.bis.latency = cpu_to_le16(qos->out.latency); - cp.bis.rtn = qos->out.rtn; - cp.bis.phy = qos->out.phy; - cp.bis.packing = qos->packing; - cp.bis.framing = qos->framing; - cp.bis.encryption = 0x00; + hci_cpu_to_le24(qos->bcast.out.interval, cp.bis.sdu_interval); + 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.packing = qos->bcast.packing; + cp.bis.framing = qos->bcast.framing; + cp.bis.encryption = qos->bcast.encryption; + memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode)); memset(&cp.bis.bcode, 0, sizeof(cp.bis.bcode)); return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp); @@ -1711,7 +1713,7 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) memset(&data, 0, sizeof(data)); /* Allocate a CIG if not set */ - if (qos->cig == BT_ISO_QOS_CIG_UNSET) { + if (qos->ucast.cig == BT_ISO_QOS_CIG_UNSET) { for (data.cig = 0x00; data.cig < 0xff; data.cig++) { data.count = 0; data.cis = 0xff; @@ -1731,22 +1733,22 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) return false; /* Update CIG */ - qos->cig = data.cig; + qos->ucast.cig = data.cig; } - data.pdu.cp.cig_id = qos->cig; - hci_cpu_to_le24(qos->out.interval, data.pdu.cp.c_interval); - hci_cpu_to_le24(qos->in.interval, data.pdu.cp.p_interval); - data.pdu.cp.sca = qos->sca; - data.pdu.cp.packing = qos->packing; - data.pdu.cp.framing = qos->framing; - data.pdu.cp.c_latency = cpu_to_le16(qos->out.latency); - data.pdu.cp.p_latency = cpu_to_le16(qos->in.latency); + data.pdu.cp.cig_id = qos->ucast.cig; + hci_cpu_to_le24(qos->ucast.out.interval, data.pdu.cp.c_interval); + hci_cpu_to_le24(qos->ucast.in.interval, data.pdu.cp.p_interval); + data.pdu.cp.sca = qos->ucast.sca; + data.pdu.cp.packing = qos->ucast.packing; + data.pdu.cp.framing = qos->ucast.framing; + data.pdu.cp.c_latency = cpu_to_le16(qos->ucast.out.latency); + data.pdu.cp.p_latency = cpu_to_le16(qos->ucast.in.latency); - if (qos->cis != BT_ISO_QOS_CIS_UNSET) { + if (qos->ucast.cis != BT_ISO_QOS_CIS_UNSET) { data.count = 0; - data.cig = qos->cig; - data.cis = qos->cis; + data.cig = qos->ucast.cig; + data.cis = qos->ucast.cis; hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND, &data); @@ -1757,7 +1759,7 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) } /* Reprogram all CIS(s) with the same CIG */ - for (data.cig = qos->cig, data.cis = 0x00; data.cis < 0x11; + for (data.cig = qos->ucast.cig, data.cis = 0x00; data.cis < 0x11; data.cis++) { data.count = 0; @@ -1767,14 +1769,14 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) continue; /* Allocate a CIS if not set */ - if (qos->cis == BT_ISO_QOS_CIS_UNSET) { + if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET) { /* Update CIS */ - qos->cis = data.cis; + qos->ucast.cis = data.cis; cis_add(&data, qos); } } - if (qos->cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis) + if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis) return false; if (hci_send_cmd(hdev, HCI_OP_LE_SET_CIG_PARAMS, @@ -1809,32 +1811,32 @@ 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->out.phy; - cis->le_rx_phy = qos->in.phy; + cis->le_tx_phy = qos->ucast.out.phy; + cis->le_rx_phy = qos->ucast.in.phy; /* If output interval is not set use the input interval as it cannot be * 0x000000. */ - if (!qos->out.interval) - qos->out.interval = qos->in.interval; + if (!qos->ucast.out.interval) + qos->ucast.out.interval = qos->ucast.in.interval; /* If input interval is not set use the output interval as it cannot be * 0x000000. */ - if (!qos->in.interval) - qos->in.interval = qos->out.interval; + if (!qos->ucast.in.interval) + qos->ucast.in.interval = qos->ucast.out.interval; /* If output latency is not set use the input latency as it cannot be * 0x0000. */ - if (!qos->out.latency) - qos->out.latency = qos->in.latency; + if (!qos->ucast.out.latency) + qos->ucast.out.latency = qos->ucast.in.latency; /* If input latency is not set use the output latency as it cannot be * 0x0000. */ - if (!qos->in.latency) - qos->in.latency = qos->out.latency; + if (!qos->ucast.in.latency) + qos->ucast.in.latency = qos->ucast.out.latency; if (!hci_le_set_cig_params(cis, qos)) { hci_conn_drop(cis); @@ -1854,7 +1856,7 @@ bool hci_iso_setup_path(struct hci_conn *conn) memset(&cmd, 0, sizeof(cmd)); - if (conn->iso_qos.out.sdu) { + if (conn->iso_qos.ucast.out.sdu) { cmd.handle = cpu_to_le16(conn->handle); cmd.direction = 0x00; /* Input (Host to Controller) */ cmd.path = 0x00; /* HCI path if enabled */ @@ -1865,7 +1867,7 @@ bool hci_iso_setup_path(struct hci_conn *conn) return false; } - if (conn->iso_qos.in.sdu) { + if (conn->iso_qos.ucast.in.sdu) { cmd.handle = cpu_to_le16(conn->handle); cmd.direction = 0x01; /* Output (Controller to Host) */ cmd.path = 0x00; /* HCI path if enabled */ @@ -1892,7 +1894,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) cmd.cis[0].acl_handle = cpu_to_le16(conn->link->handle); cmd.cis[0].cis_handle = cpu_to_le16(conn->handle); cmd.cp.num_cis++; - cig = conn->iso_qos.cig; + cig = conn->iso_qos.ucast.cig; hci_dev_lock(hdev); @@ -1902,7 +1904,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis]; if (conn == data || conn->type != ISO_LINK || - conn->state == BT_CONNECTED || conn->iso_qos.cig != cig) + conn->state == BT_CONNECTED || conn->iso_qos.ucast.cig != cig) continue; /* Check if all CIS(s) belonging to a CIG are ready */ @@ -2002,8 +2004,8 @@ static void hci_bind_bis(struct hci_conn *conn, struct bt_iso_qos *qos) { /* Update LINK PHYs according to QoS preference */ - conn->le_tx_phy = qos->out.phy; - conn->le_tx_phy = qos->out.phy; + conn->le_tx_phy = qos->bcast.out.phy; + conn->le_tx_phy = qos->bcast.out.phy; conn->iso_qos = *qos; conn->state = BT_BOUND; } @@ -2016,16 +2018,16 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; - if (qos->out.phy == 0x02) + if (qos->bcast.out.phy == 0x02) flags |= MGMT_ADV_FLAG_SEC_2M; /* Align intervals */ - interval = qos->out.interval / 1250; + interval = qos->bcast.out.interval / 1250; - if (qos->bis) - sync_interval = qos->sync_interval * 1600; + if (qos->bcast.bis) + sync_interval = qos->bcast.sync_interval * 1600; - err = hci_start_per_adv_sync(hdev, qos->bis, conn->le_per_adv_data_len, + err = hci_start_per_adv_sync(hdev, qos->bcast.bis, conn->le_per_adv_data_len, conn->le_per_adv_data, flags, interval, interval, sync_interval); if (err) @@ -2062,7 +2064,7 @@ static int create_pa_sync(struct hci_dev *hdev, void *data) } int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, - __u8 sid) + __u8 sid, struct bt_iso_qos *qos) { struct hci_cp_le_pa_create_sync *cp; @@ -2075,9 +2077,13 @@ int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, return -ENOMEM; } + cp->options = qos->bcast.options; cp->sid = sid; cp->addr_type = dst_type; bacpy(&cp->addr, dst); + cp->skip = cpu_to_le16(qos->bcast.skip); + cp->sync_timeout = cpu_to_le16(qos->bcast.sync_timeout); + cp->sync_cte_type = qos->bcast.sync_cte_type; /* Queue start pa_create_sync and scan */ return hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete); @@ -2100,8 +2106,12 @@ int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos, return err; memset(&pdu, 0, sizeof(pdu)); - pdu.cp.handle = qos->big; + pdu.cp.handle = qos->bcast.big; pdu.cp.sync_handle = cpu_to_le16(sync_handle); + pdu.cp.encryption = qos->bcast.encryption; + memcpy(pdu.cp.bcode, qos->bcast.bcode, sizeof(pdu.cp.bcode)); + pdu.cp.mse = qos->bcast.mse; + pdu.cp.timeout = cpu_to_le16(qos->bcast.timeout); pdu.cp.num_bis = num_bis; memcpy(pdu.bis, bis, num_bis); @@ -2151,7 +2161,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, return ERR_PTR(err); } - hci_iso_qos_setup(hdev, conn, &qos->out, + hci_iso_qos_setup(hdev, conn, &qos->bcast.out, conn->le_tx_phy ? conn->le_tx_phy : hdev->le_tx_def_phys); @@ -2177,9 +2187,9 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, if (IS_ERR(le)) return le; - hci_iso_qos_setup(hdev, le, &qos->out, + hci_iso_qos_setup(hdev, le, &qos->ucast.out, le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys); - hci_iso_qos_setup(hdev, le, &qos->in, + hci_iso_qos_setup(hdev, le, &qos->ucast.in, le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys); cis = hci_bind_cis(hdev, dst, dst_type, qos); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 51f13518dba9b..0e0a93cc12186 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1,6 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. + Copyright 2023 NXP Written 2000,2001 by Maxim Krasnyansky @@ -3833,7 +3834,7 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data, rcu_read_lock(); list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) { - if (conn->type != ISO_LINK || conn->iso_qos.cig != rp->cig_id || + if (conn->type != ISO_LINK || conn->iso_qos.ucast.cig != rp->cig_id || conn->state == BT_CONNECTED) continue; @@ -3890,7 +3891,7 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data, /* Input (Host to Controller) */ case 0x00: /* Only confirm connection if output only */ - if (conn->iso_qos.out.sdu && !conn->iso_qos.in.sdu) + if (conn->iso_qos.ucast.out.sdu && !conn->iso_qos.ucast.in.sdu) hci_connect_cfm(conn, rp->status); break; /* Output (Controller to Host) */ @@ -6818,15 +6819,15 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data, memset(&interval, 0, sizeof(interval)); memcpy(&interval, ev->c_latency, sizeof(ev->c_latency)); - conn->iso_qos.in.interval = le32_to_cpu(interval); + conn->iso_qos.ucast.in.interval = le32_to_cpu(interval); memcpy(&interval, ev->p_latency, sizeof(ev->p_latency)); - conn->iso_qos.out.interval = le32_to_cpu(interval); - conn->iso_qos.in.latency = le16_to_cpu(ev->interval); - conn->iso_qos.out.latency = le16_to_cpu(ev->interval); - conn->iso_qos.in.sdu = le16_to_cpu(ev->c_mtu); - conn->iso_qos.out.sdu = le16_to_cpu(ev->p_mtu); - conn->iso_qos.in.phy = ev->c_phy; - conn->iso_qos.out.phy = ev->p_phy; + conn->iso_qos.ucast.out.interval = le32_to_cpu(interval); + conn->iso_qos.ucast.in.latency = le16_to_cpu(ev->interval); + conn->iso_qos.ucast.out.latency = le16_to_cpu(ev->interval); + conn->iso_qos.ucast.in.sdu = le16_to_cpu(ev->c_mtu); + conn->iso_qos.ucast.out.sdu = le16_to_cpu(ev->p_mtu); + conn->iso_qos.ucast.in.phy = ev->c_phy; + conn->iso_qos.ucast.out.phy = ev->p_phy; } if (!ev->status) { @@ -6900,8 +6901,8 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data, cis->handle = cis_handle; } - cis->iso_qos.cig = ev->cig_id; - cis->iso_qos.cis = ev->cis_id; + cis->iso_qos.ucast.cig = ev->cig_id; + cis->iso_qos.ucast.cis = ev->cis_id; if (!(flags & HCI_PROTO_DEFER)) { hci_le_accept_cis(hdev, ev->cis_handle); @@ -6988,13 +6989,13 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data, bis->handle = handle; } - bis->iso_qos.big = ev->handle; + bis->iso_qos.bcast.big = ev->handle; memset(&interval, 0, sizeof(interval)); memcpy(&interval, ev->latency, sizeof(ev->latency)); - bis->iso_qos.in.interval = le32_to_cpu(interval); + bis->iso_qos.bcast.in.interval = le32_to_cpu(interval); /* Convert ISO Interval (1.25 ms slots) to latency (ms) */ - bis->iso_qos.in.latency = le16_to_cpu(ev->interval) * 125 / 100; - bis->iso_qos.in.sdu = le16_to_cpu(ev->max_pdu); + bis->iso_qos.bcast.in.latency = le16_to_cpu(ev->interval) * 125 / 100; + bis->iso_qos.bcast.in.sdu = le16_to_cpu(ev->max_pdu); hci_iso_setup_path(bis); } diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 8d136a7301630..74117df03a3fa 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -3,6 +3,7 @@ * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2022 Intel Corporation + * Copyright 2023 NXP */ #include @@ -59,11 +60,17 @@ struct iso_pinfo { __u16 sync_handle; __u32 flags; struct bt_iso_qos qos; + bool qos_user_set; __u8 base_len; __u8 base[BASE_MAX_LENGTH]; struct iso_conn *conn; }; +static struct bt_iso_qos default_qos; + +static bool check_ucast_qos(struct bt_iso_qos *qos); +static bool check_bcast_qos(struct bt_iso_qos *qos); + /* ---- ISO timers ---- */ #define ISO_CONN_TIMEOUT (HZ * 40) #define ISO_DISCONN_TIMEOUT (HZ * 2) @@ -264,8 +271,15 @@ static int iso_connect_bis(struct sock *sk) goto unlock; } + /* Fail if user set invalid QoS */ + if (iso_pi(sk)->qos_user_set && !check_bcast_qos(&iso_pi(sk)->qos)) { + iso_pi(sk)->qos = default_qos; + err = -EINVAL; + goto unlock; + } + /* Fail if out PHYs are marked as disabled */ - if (!iso_pi(sk)->qos.out.phy) { + if (!iso_pi(sk)->qos.bcast.out.phy) { err = -EINVAL; goto unlock; } @@ -336,8 +350,15 @@ static int iso_connect_cis(struct sock *sk) goto unlock; } + /* Fail if user set invalid QoS */ + if (iso_pi(sk)->qos_user_set && !check_ucast_qos(&iso_pi(sk)->qos)) { + iso_pi(sk)->qos = default_qos; + err = -EINVAL; + goto unlock; + } + /* Fail if either PHYs are marked as disabled */ - if (!iso_pi(sk)->qos.in.phy && !iso_pi(sk)->qos.out.phy) { + if (!iso_pi(sk)->qos.ucast.in.phy && !iso_pi(sk)->qos.ucast.out.phy) { err = -EINVAL; goto unlock; } @@ -417,7 +438,7 @@ static int iso_send_frame(struct sock *sk, struct sk_buff *skb) BT_DBG("sk %p len %d", sk, skb->len); - if (skb->len > qos->out.sdu) + if (skb->len > qos->ucast.out.sdu) return -EMSGSIZE; len = skb->len; @@ -680,13 +701,23 @@ static struct proto iso_proto = { } static struct bt_iso_qos default_qos = { - .cig = BT_ISO_QOS_CIG_UNSET, - .cis = BT_ISO_QOS_CIS_UNSET, - .sca = 0x00, - .packing = 0x00, - .framing = 0x00, - .in = DEFAULT_IO_QOS, - .out = DEFAULT_IO_QOS, + .bcast = { + .big = BT_ISO_QOS_BIG_UNSET, + .bis = BT_ISO_QOS_BIS_UNSET, + .sync_interval = 0x00, + .packing = 0x00, + .framing = 0x00, + .in = DEFAULT_IO_QOS, + .out = DEFAULT_IO_QOS, + .encryption = 0x00, + .bcode = {0x00}, + .options = 0x00, + .skip = 0x0000, + .sync_timeout = 0x4000, + .sync_cte_type = 0x00, + .mse = 0x00, + .timeout = 0x4000, + }, }; static struct sock *iso_sock_alloc(struct net *net, struct socket *sock, @@ -893,9 +924,15 @@ static int iso_listen_bis(struct sock *sk) if (!hdev) return -EHOSTUNREACH; + /* Fail if user set invalid QoS */ + if (iso_pi(sk)->qos_user_set && !check_bcast_qos(&iso_pi(sk)->qos)) { + iso_pi(sk)->qos = default_qos; + return -EINVAL; + } + err = hci_pa_create_sync(hdev, &iso_pi(sk)->dst, le_addr_type(iso_pi(sk)->dst_type), - iso_pi(sk)->bc_sid); + iso_pi(sk)->bc_sid, &iso_pi(sk)->qos); hci_dev_put(hdev); @@ -1154,21 +1191,62 @@ static bool check_io_qos(struct bt_iso_io_qos *qos) return true; } -static bool check_qos(struct bt_iso_qos *qos) +static bool check_ucast_qos(struct bt_iso_qos *qos) { - if (qos->sca > 0x07) + if (qos->ucast.sca > 0x07) return false; - if (qos->packing > 0x01) + if (qos->ucast.packing > 0x01) return false; - if (qos->framing > 0x01) + if (qos->ucast.framing > 0x01) return false; - if (!check_io_qos(&qos->in)) + if (!check_io_qos(&qos->ucast.in)) return false; - if (!check_io_qos(&qos->out)) + if (!check_io_qos(&qos->ucast.out)) + return false; + + return true; +} + +static bool check_bcast_qos(struct bt_iso_qos *qos) +{ + if (qos->bcast.sync_interval > 0x07) + return false; + + if (qos->bcast.packing > 0x01) + return false; + + if (qos->bcast.framing > 0x01) + return false; + + if (!check_io_qos(&qos->bcast.in)) + return false; + + if (!check_io_qos(&qos->bcast.out)) + return false; + + if (qos->bcast.encryption > 0x01) + return false; + + if (qos->bcast.options > 0x07) + return false; + + if (qos->bcast.skip > 0x01f3) + return false; + + if (qos->bcast.sync_timeout < 0x000a || qos->bcast.sync_timeout > 0x4000) + return false; + + if (qos->bcast.sync_cte_type > 0x1f) + return false; + + if (qos->bcast.mse > 0x1f) + return false; + + if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000) return false; return true; @@ -1179,7 +1257,7 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname, { struct sock *sk = sock->sk; int len, err = 0; - struct bt_iso_qos qos; + struct bt_iso_qos qos = default_qos; u32 opt; BT_DBG("sk %p", sk); @@ -1212,24 +1290,19 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname, } len = min_t(unsigned int, sizeof(qos), optlen); - if (len != sizeof(qos)) { - err = -EINVAL; - break; - } - - memset(&qos, 0, sizeof(qos)); if (copy_from_sockptr(&qos, optval, len)) { err = -EFAULT; break; } - if (!check_qos(&qos)) { + if (len == sizeof(qos.ucast) && !check_ucast_qos(&qos)) { err = -EINVAL; break; } iso_pi(sk)->qos = qos; + iso_pi(sk)->qos_user_set = true; break; @@ -1419,7 +1492,7 @@ static bool iso_match_big(struct sock *sk, void *data) { struct hci_evt_le_big_sync_estabilished *ev = data; - return ev->handle == iso_pi(sk)->qos.big; + return ev->handle == iso_pi(sk)->qos.bcast.big; } static void iso_conn_ready(struct iso_conn *conn) From 288c90224eec55d13e786844b7954ef060752089 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 19 Dec 2022 13:37:02 -0800 Subject: [PATCH 43/72] Bluetooth: Enable all supported LE PHY by default This enables 2M and Coded PHY by default if they are marked as supported in the LE features bits. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 4 ++++ net/bluetooth/hci_sync.c | 28 ++++++++++++++++++++++++---- net/bluetooth/mgmt.c | 4 ++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f11689284112b..827e671595236 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1683,9 +1683,13 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define scan_1m(dev) (((dev)->le_tx_def_phys & HCI_LE_SET_PHY_1M) || \ ((dev)->le_rx_def_phys & HCI_LE_SET_PHY_1M)) +#define le_2m_capable(dev) (((dev)->le_features[1] & HCI_LE_PHY_2M)) + #define scan_2m(dev) (((dev)->le_tx_def_phys & HCI_LE_SET_PHY_2M) || \ ((dev)->le_rx_def_phys & HCI_LE_SET_PHY_2M)) +#define le_coded_capable(dev) (((dev)->le_features[1] & HCI_LE_PHY_CODED)) + #define scan_coded(dev) (((dev)->le_tx_def_phys & HCI_LE_SET_PHY_CODED) || \ ((dev)->le_rx_def_phys & HCI_LE_SET_PHY_CODED)) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index f21497ebc8149..00017f75cd419 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4414,18 +4414,38 @@ static int hci_le_set_write_def_data_len_sync(struct hci_dev *hdev) sizeof(cp), &cp, HCI_CMD_TIMEOUT); } -/* Set Default PHY parameters if command is supported */ +/* Set Default PHY parameters if command is supported, enables all supported + * PHYs according to the LE Features bits. + */ static int hci_le_set_default_phy_sync(struct hci_dev *hdev) { struct hci_cp_le_set_default_phy cp; - if (!(hdev->commands[35] & 0x20)) + if (!(hdev->commands[35] & 0x20)) { + /* If the command is not supported it means only 1M PHY is + * supported. + */ + hdev->le_tx_def_phys = HCI_LE_SET_PHY_1M; + hdev->le_rx_def_phys = HCI_LE_SET_PHY_1M; return 0; + } memset(&cp, 0, sizeof(cp)); cp.all_phys = 0x00; - cp.tx_phys = hdev->le_tx_def_phys; - cp.rx_phys = hdev->le_rx_def_phys; + cp.tx_phys = HCI_LE_SET_PHY_1M; + cp.rx_phys = HCI_LE_SET_PHY_1M; + + /* Enables 2M PHY if supported */ + if (le_2m_capable(hdev)) { + cp.tx_phys |= HCI_LE_SET_PHY_2M; + cp.rx_phys |= HCI_LE_SET_PHY_2M; + } + + /* Enables Coded PHY if supported */ + if (le_coded_capable(hdev)) { + cp.tx_phys |= HCI_LE_SET_PHY_CODED; + cp.rx_phys |= HCI_LE_SET_PHY_CODED; + } return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_DEFAULT_PHY, sizeof(cp), &cp, HCI_CMD_TIMEOUT); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 249dc6777fb4e..5f8c144c84b88 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -8393,10 +8393,10 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev) flags |= MGMT_ADV_FLAG_HW_OFFLOAD; flags |= MGMT_ADV_FLAG_CAN_SET_TX_POWER; - if (hdev->le_features[1] & HCI_LE_PHY_2M) + if (le_2m_capable(hdev)) flags |= MGMT_ADV_FLAG_SEC_2M; - if (hdev->le_features[1] & HCI_LE_PHY_CODED) + if (le_coded_capable(hdev)) flags |= MGMT_ADV_FLAG_SEC_CODED; } From 3c0d41f141c97e516d986f0571b0d7a745a4e2a8 Mon Sep 17 00:00:00 2001 From: Qiqi Zhang Date: Tue, 4 Apr 2023 10:27:11 +0800 Subject: [PATCH 44/72] Bluetooth: hci_h5: Complements reliable packet processing logic As shown in the schematic diagram below.There may be a critical scenario in the current code. If the device does not receive an pure ack sent by the host due to insufficient receive buffer or other reasons and triggers a retransmission, the host will always be in an 'out-of-order' state.The state machine will get stuck. host device SEQ3,ACK4 ---------> <--------- SEQ4,ACK4 pure ACK ---------> (not received) (out-of-order) <--------- SEQ4,ACK4(retransmission) ........ (out-of-order) <--------- SEQ4,ACK4(retransmission) According to the description in the core specification: "whenever a reliable packet is received, an acknowledgment shall be generated." So set H5_TX_ACK_REQ bit to trigger retransmission of pure ack packet when "out-of-order" occurs. Signed-off-by: Qiqi Zhang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_h5.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index e90670955df2c..fefc37b98b4ac 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -463,6 +463,8 @@ static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c) if (H5_HDR_RELIABLE(hdr) && H5_HDR_SEQ(hdr) != h5->tx_ack) { bt_dev_err(hu->hdev, "Out-of-order packet arrived (%u != %u)", H5_HDR_SEQ(hdr), h5->tx_ack); + set_bit(H5_TX_ACK_REQ, &h5->flags); + hci_uart_tx_wakeup(hu); h5_reset_rx(h5); return 0; } From 0d218c3642b9ccf71f44987cd03c19320f3bd918 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Mar 2023 23:11:21 +0200 Subject: [PATCH 45/72] Bluetooth: hci_bcm: Fall back to getting bdaddr from EFI if not set On some devices the BCM Bluetooth adapter does not have a valid bdaddr set. btbcm.c currently sets HCI_QUIRK_INVALID_BDADDR to indicate when this is the case. But this requires users to manual setup a btaddr, by doing e.g.: btmgmt -i hci0 public-addr 'B0:F1:EC:82:1D:B3' Which means that Bluetooth will not work out of the box on such devices. To avoid this (where possible) hci_bcm sets: HCI_QUIRK_USE_BDADDR_PROPERTY which tries to get the bdaddr from devicetree. But this only works on devicetree platforms. On UEFI based platforms there is a special Broadcom UEFI variable which when present contains the devices bdaddr, just like how there is another UEFI variable which contains wifi nvram contents including the wifi MAC address. Add support for getting the bdaddr from this Broadcom UEFI variable, so that Bluetooth will work OOTB for users on devices where this UEFI variable is present. This fixes Bluetooth not working on for example Asus T100HA 2-in-1s. Signed-off-by: Hans de Goede Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btbcm.c | 47 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 43e98a598bd9a..de2ea589aa49b 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -6,6 +6,7 @@ * Copyright (C) 2015 Intel Corporation */ +#include #include #include #include @@ -34,6 +35,43 @@ /* For kmalloc-ing the fw-name array instead of putting it on the stack */ typedef char bcm_fw_name[BCM_FW_NAME_LEN]; +#ifdef CONFIG_EFI +static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev) +{ + efi_guid_t guid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, 0xb5, 0x1f, + 0x43, 0x26, 0x81, 0x23, 0xd1, 0x13); + bdaddr_t efi_bdaddr, bdaddr; + efi_status_t status; + unsigned long len; + int ret; + + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) + return -EOPNOTSUPP; + + len = sizeof(efi_bdaddr); + status = efi.get_variable(L"BDADDR", &guid, NULL, &len, &efi_bdaddr); + if (status != EFI_SUCCESS) + return -ENXIO; + + if (len != sizeof(efi_bdaddr)) + return -EIO; + + baswap(&bdaddr, &efi_bdaddr); + + ret = btbcm_set_bdaddr(hdev, &bdaddr); + if (ret) + return ret; + + bt_dev_info(hdev, "BCM: Using EFI device address (%pMR)", &bdaddr); + return 0; +} +#else +static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} +#endif + int btbcm_check_bdaddr(struct hci_dev *hdev) { struct hci_rp_read_bd_addr *bda; @@ -87,9 +125,12 @@ int btbcm_check_bdaddr(struct hci_dev *hdev) !bacmp(&bda->bdaddr, BDADDR_BCM4345C5) || !bacmp(&bda->bdaddr, BDADDR_BCM43430A0) || !bacmp(&bda->bdaddr, BDADDR_BCM43341B)) { - bt_dev_info(hdev, "BCM: Using default device address (%pMR)", - &bda->bdaddr); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + /* Try falling back to BDADDR EFI variable */ + if (btbcm_set_bdaddr_from_efi(hdev) != 0) { + bt_dev_info(hdev, "BCM: Using default device address (%pMR)", + &bda->bdaddr); + set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + } } kfree_skb(skb); From ce439473acdf0a3aecbda616a0f92973ab1c2e52 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Mar 2023 23:11:22 +0200 Subject: [PATCH 46/72] Bluetooth: hci_bcm: Limit bcm43430a0 / bcm43430a1 baudrate to 2000000 The bcm43430a0 and bcm43430a1 BT does not support the 0xfc45 command to set the UART clock to 48 MHz and it also does not work at 4000000 baud without this command as some newer models do. These chips are found on ACPI/x86 devices where the operating baudrate does not come from the firmware but is hardcoded at 4000000, which does not work. Add a max_baudrate value to struct bcm_device_data and set this to 2000000 on all known ACPI hardware-ids for the bcm43430a0 and the bcm43430a1. Note this also adds the BCM2E9F ACPI HID which was missing until now. Signed-off-by: Hans de Goede Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcm.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 2b6c0e1922cb3..4a3ef844df5d2 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -55,12 +55,14 @@ * @drive_rts_on_open: drive RTS signal on ->open() when platform requires it * @no_uart_clock_set: UART clock set command for >3Mbps mode is unavailable * @max_autobaud_speed: max baudrate supported by device in autobaud mode + * @max_speed: max baudrate supported */ struct bcm_device_data { bool no_early_set_baudrate; bool drive_rts_on_open; bool no_uart_clock_set; u32 max_autobaud_speed; + u32 max_speed; }; /** @@ -1300,6 +1302,12 @@ static const struct hci_uart_proto bcm_proto = { }; #ifdef CONFIG_ACPI + +/* bcm43430a0/a1 BT does not support 48MHz UART clock, limit to 2000000 baud */ +static struct bcm_device_data bcm43430_device_data = { + .max_speed = 2000000, +}; + static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E00" }, { "BCM2E01" }, @@ -1414,19 +1422,19 @@ static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E71" }, { "BCM2E72" }, { "BCM2E73" }, - { "BCM2E74" }, - { "BCM2E75" }, + { "BCM2E74", (long)&bcm43430_device_data }, + { "BCM2E75", (long)&bcm43430_device_data }, { "BCM2E76" }, { "BCM2E77" }, { "BCM2E78" }, { "BCM2E79" }, { "BCM2E7A" }, - { "BCM2E7B" }, + { "BCM2E7B", (long)&bcm43430_device_data }, { "BCM2E7C" }, { "BCM2E7D" }, { "BCM2E7E" }, { "BCM2E7F" }, - { "BCM2E80" }, + { "BCM2E80", (long)&bcm43430_device_data }, { "BCM2E81" }, { "BCM2E82" }, { "BCM2E83" }, @@ -1435,7 +1443,7 @@ static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E86" }, { "BCM2E87" }, { "BCM2E88" }, - { "BCM2E89" }, + { "BCM2E89", (long)&bcm43430_device_data }, { "BCM2E8A" }, { "BCM2E8B" }, { "BCM2E8C" }, @@ -1444,29 +1452,30 @@ static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E90" }, { "BCM2E92" }, { "BCM2E93" }, - { "BCM2E94" }, + { "BCM2E94", (long)&bcm43430_device_data }, { "BCM2E95" }, { "BCM2E96" }, { "BCM2E97" }, { "BCM2E98" }, - { "BCM2E99" }, + { "BCM2E99", (long)&bcm43430_device_data }, { "BCM2E9A" }, - { "BCM2E9B" }, + { "BCM2E9B", (long)&bcm43430_device_data }, { "BCM2E9C" }, { "BCM2E9D" }, + { "BCM2E9F", (long)&bcm43430_device_data }, { "BCM2EA0" }, { "BCM2EA1" }, - { "BCM2EA2" }, - { "BCM2EA3" }, + { "BCM2EA2", (long)&bcm43430_device_data }, + { "BCM2EA3", (long)&bcm43430_device_data }, { "BCM2EA4" }, { "BCM2EA5" }, { "BCM2EA6" }, { "BCM2EA7" }, { "BCM2EA8" }, { "BCM2EA9" }, - { "BCM2EAA" }, - { "BCM2EAB" }, - { "BCM2EAC" }, + { "BCM2EAA", (long)&bcm43430_device_data }, + { "BCM2EAB", (long)&bcm43430_device_data }, + { "BCM2EAC", (long)&bcm43430_device_data }, { }, }; MODULE_DEVICE_TABLE(acpi, bcm_acpi_match); @@ -1535,6 +1544,8 @@ static int bcm_serdev_probe(struct serdev_device *serdev) bcmdev->no_early_set_baudrate = data->no_early_set_baudrate; bcmdev->drive_rts_on_open = data->drive_rts_on_open; bcmdev->no_uart_clock_set = data->no_uart_clock_set; + if (data->max_speed && bcmdev->oper_speed > data->max_speed) + bcmdev->oper_speed = data->max_speed; } return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto); From 9a09460274d311466b44743c281c81f36c3e4df4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Mar 2023 23:11:23 +0200 Subject: [PATCH 47/72] Bluetooth: hci_bcm: Add Lenovo Yoga Tablet 2 830 / 1050 to the bcm_broken_irq_dmi_table The DSDT for the Lenovo Yoga Tablet 2 830 / 1050 models (which share the same mainboard) specifies a IOAPIC IRQ for the HCI -> host IRQ but this is not correct. Like the Asus TF103C these tablets use pin 17 of the INT33FC:02 GPIO controller for the IRQ and this pin is _not_ configured in direct IRQ mode by the firmware. Add a DMI match for this, re-using the Asus TF103C gpiod_lookup_table, to fix bluetooth not working on these tablets. Signed-off-by: Hans de Goede Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcm.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 4a3ef844df5d2..eb9e1c858e914 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -890,7 +890,7 @@ static int bcm_resume(struct device *dev) #endif /* Some firmware reports an IRQ which does not work (wrong pin in fw table?) */ -static struct gpiod_lookup_table asus_tf103c_irq_gpios = { +static struct gpiod_lookup_table irq_on_int33fc02_pin17_gpios = { .dev_id = "serial0-0", .table = { GPIO_LOOKUP("INT33FC:02", 17, "host-wakeup-alt", GPIO_ACTIVE_HIGH), @@ -905,7 +905,18 @@ static const struct dmi_system_id bcm_broken_irq_dmi_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), }, - .driver_data = &asus_tf103c_irq_gpios, + .driver_data = &irq_on_int33fc02_pin17_gpios, + }, + { + .ident = "Lenovo Yoga Tablet 2 830F/L / 1050F/L", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), + DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), + /* Partial match on beginning of BIOS version */ + DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), + }, + .driver_data = &irq_on_int33fc02_pin17_gpios, }, { .ident = "Meegopad T08", From 09df5a91e620a2d21f7f410938e5e28364fae0bf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Mar 2023 23:11:24 +0200 Subject: [PATCH 48/72] Bluetooth: hci_bcm: Add Acer Iconia One 7 B1-750 to the bcm_broken_irq_dmi_table The DSDT for the Acer Iconia One 7 B1-750 models (which share the same mainboard) specifies a IOAPIC IRQ for the HCI -> host IRQ but this is not correct. Like the Asus TF103C these tablets use pin 17 of the INT33FC:02 GPIO controller for the IRQ and this pin is _not_ configured in direct IRQ mode by the firmware. Add a DMI match for this, re-using the Asus TF103C gpiod_lookup_table, to fix bluetooth not working on these tablets. Signed-off-by: Hans de Goede Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index eb9e1c858e914..83bf5d4330c40 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -899,6 +899,14 @@ static struct gpiod_lookup_table irq_on_int33fc02_pin17_gpios = { }; static const struct dmi_system_id bcm_broken_irq_dmi_table[] = { + { + .ident = "Acer Iconia One 7 B1-750", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), + }, + .driver_data = &irq_on_int33fc02_pin17_gpios, + }, { .ident = "Asus TF103C", .matches = { From 38a4f83d802caf5e7ec93a187d211d87b6d67800 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Mon, 3 Apr 2023 17:54:27 +0530 Subject: [PATCH 49/72] Bluetooth: btnxpuart: Add support to download helper FW file for w8997 This adds support to download helper FW file for the legacy NXP chipset 88w8997 for the btnxpuart driver. This helper FW file is necessary to set the bootloader baudrate to 3000000 after which the actual BT FW file can be downloaded. This change helps bring the FW download time from around 10 sec to less than 2 sec for 88w8997 chip. For newer chipsets, both V1 and V3 bootloader, driver sends the cmd5 and cmd7 to the chip bootloader, and does not need a helper FW file. Signed-off-by: Neeraj Sanjay Kale Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 67 ++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 5f641466d695d..32e22044b6b2a 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -34,6 +34,7 @@ #define FIRMWARE_W9098 "nxp/uartuart9098_bt_v1.bin" #define FIRMWARE_IW416 "nxp/uartiw416_bt_v0.bin" #define FIRMWARE_IW612 "nxp/uartspi_n61x_v1.bin.se" +#define FIRMWARE_HELPER "nxp/helper_uart_3000000.bin" #define CHIP_ID_W9098 0x5c03 #define CHIP_ID_IW416 0x7201 @@ -123,7 +124,7 @@ struct psmode_cmd_payload { } __packed; struct btnxpuart_data { - bool fw_download_3M_baudrate; + const char *helper_fw_name; const char *fw_name; }; @@ -150,6 +151,7 @@ struct btnxpuart_dev { u32 fw_init_baudrate; bool timeout_changed; bool baudrate_changed; + bool helper_downloaded; struct ps_data psdata; struct btnxpuart_data *nxp_data; @@ -168,6 +170,13 @@ struct btnxpuart_dev { #define HDR_LEN 16 +#define NXP_RECV_CHIP_VER_V1 \ + .type = NXP_V1_CHIP_VER_PKT, \ + .hlen = 4, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = 4 + #define NXP_RECV_FW_REQ_V1 \ .type = NXP_V1_FW_REQ_PKT, \ .hlen = 4, \ @@ -194,6 +203,11 @@ struct v1_data_req { __le16 len_comp; } __packed; +struct v1_start_ind { + __le16 chip_id; + __le16 chip_id_comp; +} __packed; + struct v3_data_req { __le16 len; __le32 offset; @@ -518,6 +532,7 @@ static int nxp_download_firmware(struct hci_dev *hdev) nxpdev->fw_v3_offset_correction = 0; nxpdev->baudrate_changed = false; nxpdev->timeout_changed = false; + nxpdev->helper_downloaded = false; serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE); serdev_device_set_flow_control(nxpdev->serdev, 0); @@ -664,6 +679,29 @@ static int nxp_request_firmware(struct hci_dev *hdev, const char *fw_name) } /* for legacy chipsets with V1 bootloader */ +static int nxp_recv_chip_ver_v1(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct v1_start_ind *req; + + req = (struct v1_start_ind *)skb_pull_data(skb, sizeof(struct v1_start_ind)); + if (!req) + goto free_skb; + + if ((req->chip_id ^ req->chip_id_comp) == 0xffff) { + nxpdev->fw_dnld_v1_offset = 0; + nxpdev->fw_v1_sent_bytes = 0; + nxpdev->fw_v1_expected_len = HDR_LEN; + release_firmware(nxpdev->fw); + memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name)); + nxp_send_ack(NXP_ACK_V1, hdev); + } + +free_skb: + kfree_skb(skb); + return 0; +} + static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); @@ -685,7 +723,7 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) } nxp_send_ack(NXP_ACK_V1, hdev); - if (nxp_data->fw_download_3M_baudrate) { + if (!nxp_data->helper_fw_name) { if (!nxpdev->timeout_changed) { nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, req->len); goto free_skb; @@ -702,14 +740,26 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) } } - if (nxp_request_firmware(hdev, nxp_data->fw_name)) - goto free_skb; + if (!nxp_data->helper_fw_name || nxpdev->helper_downloaded) { + if (nxp_request_firmware(hdev, nxp_data->fw_name)) + goto free_skb; + } else if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) { + if (nxp_request_firmware(hdev, nxp_data->helper_fw_name)) + goto free_skb; + } requested_len = req->len; if (requested_len == 0) { bt_dev_dbg(hdev, "FW Downloaded Successfully: %zu bytes", nxpdev->fw->size); - clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); - wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); + if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) { + nxpdev->helper_downloaded = true; + serdev_device_wait_until_sent(nxpdev->serdev, 0); + serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_SEC_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, 1); + } else { + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); + } goto free_skb; } if (requested_len & 0x01) { @@ -1142,6 +1192,7 @@ static const struct h4_recv_pkt nxp_recv_pkts[] = { { H4_RECV_ACL, .recv = hci_recv_frame }, { H4_RECV_SCO, .recv = hci_recv_frame }, { H4_RECV_EVENT, .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 }, { NXP_RECV_CHIP_VER_V3, .recv = nxp_recv_chip_ver_v3 }, { NXP_RECV_FW_REQ_V3, .recv = nxp_recv_fw_req_v3 }, @@ -1265,12 +1316,12 @@ static void nxp_serdev_remove(struct serdev_device *serdev) } static struct btnxpuart_data w8987_data = { - .fw_download_3M_baudrate = true, + .helper_fw_name = NULL, .fw_name = FIRMWARE_W8987, }; static struct btnxpuart_data w8997_data = { - .fw_download_3M_baudrate = false, + .helper_fw_name = FIRMWARE_HELPER, .fw_name = FIRMWARE_W8997, }; From 86d55f124b52de2ba0d066d89b766bcc0387fd72 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Mon, 3 Apr 2023 17:54:28 +0530 Subject: [PATCH 50/72] Bluetooth: btnxpuart: Deasset UART break before closing serdev device This adds a call to ps_wakeup() before closing the serdev device, to de-assert UART break. Signed-off-by: Neeraj Sanjay Kale Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 32e22044b6b2a..12cc53c0b88bd 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1167,6 +1167,7 @@ static int btnxpuart_close(struct hci_dev *hdev) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + ps_wakeup(nxpdev); serdev_device_close(nxpdev->serdev); clear_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state); return 0; From 893410b221f4966890e5bc33be08d38d4bc968b2 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Mon, 3 Apr 2023 17:54:29 +0530 Subject: [PATCH 51/72] Bluetooth: btnxpuart: Disable Power Save feature on startup This sets the default power save mode setting to disabled. With this setting, this driver will behave like a normal h4 driver. If user needs to use the power save feature, it can be enabled using the following vendor command: hcitool cmd 3f 23 02 00 00 (HCI_NXP_AUTO_SLEEP_MODE) Signed-off-by: Neeraj Sanjay Kale Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 12cc53c0b88bd..a2335fe3e264b 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -262,7 +262,7 @@ static u8 crc8_table[CRC8_TABLE_SIZE]; /* Default configurations */ #define DEFAULT_H2C_WAKEUP_MODE WAKEUP_METHOD_BREAK -#define DEFAULT_PS_MODE PS_MODE_ENABLE +#define DEFAULT_PS_MODE PS_MODE_DISABLE #define FW_INIT_BAUDRATE HCI_NXP_PRI_BAUDRATE static struct sk_buff *nxp_drv_send_cmd(struct hci_dev *hdev, u16 opcode, From 305d6b6e485e46a7a3e85d5bfecda9af2294f423 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Mon, 3 Apr 2023 17:54:30 +0530 Subject: [PATCH 52/72] Bluetooth: btnxpuart: No need to check the received bootloader signature We can never assume the uart will deliver a complete packet to the BT layer at once, the expected packet may be divided into several parts by uart as uart doesn't know the received packet size, the received data count may mismatch with the expected packet size, so here is_valid_bootloader_signature() check may always return false. Even we remove the count check in is_valid_bootloader_signature(), then the first part of the data which includes the packet type can pass the is_valid_bootloader_signature() check, but the remaining parts don't have the packet type data still cannot pass the check, here return directly will cause the data loss. So need to remove the received bootloader signature check here, the h4_recv_buf() can help us combine the different data received into one packet. If any out-of-sync or incomplete bootloader signature is received, it is safe to ignore and discard it, and process the next bootloader signature. Co-developed-by: Sherry Sun Signed-off-by: Sherry Sun Signed-off-by: Neeraj Sanjay Kale Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index a2335fe3e264b..93f3afc0c0c80 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1199,33 +1199,20 @@ static const struct h4_recv_pkt nxp_recv_pkts[] = { { NXP_RECV_FW_REQ_V3, .recv = nxp_recv_fw_req_v3 }, }; -static bool is_valid_bootloader_signature(const u8 *data, size_t count) -{ - if ((*data == NXP_V1_FW_REQ_PKT && count == sizeof(struct v1_data_req) + 1) || - (*data == NXP_V3_FW_REQ_PKT && count == sizeof(struct v3_data_req) + 1) || - (*data == NXP_V3_CHIP_VER_PKT && count == sizeof(struct v3_start_ind) + 1)) - return true; - else - return false; -} - static int btnxpuart_receive_buf(struct serdev_device *serdev, const u8 *data, size_t count) { struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev); - if (is_fw_downloading(nxpdev) && !is_valid_bootloader_signature(data, count)) { - /* Unknown bootloader signature, skip without returning error */ - return count; - } - ps_start_timer(nxpdev); nxpdev->rx_skb = h4_recv_buf(nxpdev->hdev, nxpdev->rx_skb, data, count, nxp_recv_pkts, ARRAY_SIZE(nxp_recv_pkts)); if (IS_ERR(nxpdev->rx_skb)) { int err = PTR_ERR(nxpdev->rx_skb); - + /* Safe to ignore out-of-sync bootloader signatures */ + if (is_fw_downloading(nxpdev)) + return count; bt_dev_err(nxpdev->hdev, "Frame reassembly failed (%d)", err); nxpdev->rx_skb = NULL; return err; From 3c690a0d64f5964977050a91b6bba8de09c13d94 Mon Sep 17 00:00:00 2001 From: Lanzhe Li Date: Sun, 9 Apr 2023 21:02:29 +0800 Subject: [PATCH 53/72] Bluetooth: fix inconsistent indenting Fixed a wrong indentation before "return".This line uses a 7 space indent instead of a tab. Signed-off-by: Lanzhe Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index b7f682922a165..ec0df2f9188ed 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -189,7 +189,7 @@ static int uuids_show(struct seq_file *f, void *p) } hci_dev_unlock(hdev); - return 0; + return 0; } DEFINE_SHOW_ATTRIBUTE(uuids); From 9e3c2ea67a4c24f7a7517e863e3adc89c4e3b66d Mon Sep 17 00:00:00 2001 From: Iulia Tanasescu Date: Tue, 11 Apr 2023 10:41:35 +0300 Subject: [PATCH 54/72] Bluetooth: hci_conn: remove extra line in hci_le_big_create_sync Remove extra line setting the broadcast code parameter of the hci_cp_le_create_big struct to 0. The broadcast code is copied from the QoS struct. Signed-off-by: Iulia Tanasescu Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5672b49245721..01e0b72551743 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1700,7 +1700,6 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) cp.bis.framing = qos->bcast.framing; cp.bis.encryption = qos->bcast.encryption; memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode)); - memset(&cp.bis.bcode, 0, sizeof(cp.bis.bcode)); return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp); } From 0811ff480978a2122f0465cb8682d9f7a2f6a169 Mon Sep 17 00:00:00 2001 From: Steev Klimaszewski Date: Wed, 12 Apr 2023 18:58:17 -0500 Subject: [PATCH 55/72] Bluetooth: hci_qca: mark OF related data as maybe unused The driver can be compile tested with !CONFIG_OF making certain data unused. Signed-off-by: Steev Klimaszewski Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_qca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 1597797ff1693..1b064504b3887 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1891,7 +1891,7 @@ static const struct qca_device_data qca_soc_data_wcn6750 __maybe_unused = { .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES, }; -static const struct qca_device_data qca_soc_data_wcn6855 = { +static const struct qca_device_data qca_soc_data_wcn6855 __maybe_unused = { .soc_type = QCA_WCN6855, .vregs = (struct qca_vreg []) { { "vddio", 5000 }, From e4eea890369c00dd58d97b1c066dc2bddf0da2c7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 6 Apr 2023 11:55:17 +0300 Subject: [PATCH 56/72] Bluetooth: vhci: Fix info leak in force_devcd_write() There are a number of bugs here: 1) If "count" is less than sizeof(dump_data.data) then it copies uninitialized data. 2) If simple_write_to_buffer() returns -EFAULT then we run into a problem "ret < count" comparison. "count" is an unsigned long so the comparison is type promoted to unsigned long and the negative returns become high positive values. That also results in copying uninitialized data. 3) If "*ppos" is non-zero then the first part of the dump_data buffer is uninitialized. Using copy_from_user() instead of simple_write_to_buffer() is more appropriate here. Fixes: d5d5df6da0aa ("Bluetooth: Add vhci devcoredump support") Signed-off-by: Dan Carpenter Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_vhci.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 691fe93b19768..40e2b9fa11a26 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -323,17 +323,21 @@ static ssize_t force_devcd_write(struct file *file, const char __user *user_buf, struct hci_dev *hdev = data->hdev; struct sk_buff *skb = NULL; struct devcoredump_test_data dump_data; + size_t data_size; int ret; - ret = simple_write_to_buffer(&dump_data, sizeof(dump_data), ppos, - user_buf, count); - if (ret < count) - return ret; + if (count < offsetof(struct devcoredump_test_data, data) || + count > sizeof(dump_data)) + return -EINVAL; + + if (copy_from_user(&dump_data, user_buf, count)) + return -EFAULT; - skb = alloc_skb(sizeof(dump_data.data), GFP_ATOMIC); + data_size = count - offsetof(struct devcoredump_test_data, data); + skb = alloc_skb(data_size, GFP_ATOMIC); if (!skb) return -ENOMEM; - skb_put_data(skb, &dump_data.data, sizeof(dump_data.data)); + skb_put_data(skb, &dump_data.data, data_size); hci_devcd_register(hdev, vhci_coredump, vhci_coredump_hdr, NULL); From 06149746e7203d5ffe2d6faf9799ee36203aa8b8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 11 Apr 2023 16:02:22 -0700 Subject: [PATCH 57/72] Bluetooth: hci_conn: Add support for linking multiple hcon Since it is required for some configurations to have multiple CIS with the same peer which is now covered by iso-tester in the following test cases: ISO AC 6(i) - Success ISO AC 7(i) - Success ISO AC 8(i) - Success ISO AC 9(i) - Success ISO AC 11(i) - Success Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 14 ++- net/bluetooth/hci_conn.c | 155 ++++++++++++++++++++++--------- net/bluetooth/hci_event.c | 92 ++++++++---------- net/bluetooth/iso.c | 8 +- 4 files changed, 172 insertions(+), 97 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 827e671595236..4fe1e71cb9d83 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -770,7 +770,10 @@ struct hci_conn { void *iso_data; struct amp_mgr *amp_mgr; - struct hci_conn *link; + struct list_head link_list; + struct hci_conn *parent; + struct hci_link *link; + struct bt_codec codec; void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); @@ -780,6 +783,11 @@ struct hci_conn { void (*cleanup)(struct hci_conn *conn); }; +struct hci_link { + struct list_head list; + struct hci_conn *conn; +}; + struct hci_chan { struct list_head list; __u16 handle; @@ -1383,12 +1391,14 @@ static inline void hci_conn_put(struct hci_conn *conn) put_device(&conn->dev); } -static inline void hci_conn_hold(struct hci_conn *conn) +static inline struct hci_conn *hci_conn_hold(struct hci_conn *conn) { BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt)); atomic_inc(&conn->refcnt); cancel_delayed_work(&conn->disc_work); + + return conn; } static inline void hci_conn_drop(struct hci_conn *conn) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 01e0b72551743..d8466abbb36a5 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -330,8 +330,11 @@ static void hci_add_sco(struct hci_conn *conn, __u16 handle) static bool find_next_esco_param(struct hci_conn *conn, const struct sco_param *esco_param, int size) { + if (!conn->parent) + return false; + for (; conn->attempt <= size; conn->attempt++) { - if (lmp_esco_2m_capable(conn->link) || + if (lmp_esco_2m_capable(conn->parent) || (esco_param[conn->attempt - 1].pkt_type & ESCO_2EV3)) break; BT_DBG("hcon %p skipped attempt %d, eSCO 2M not supported", @@ -461,7 +464,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) break; case BT_CODEC_CVSD: - if (lmp_esco_capable(conn->link)) { + if (conn->parent && lmp_esco_capable(conn->parent)) { if (!find_next_esco_param(conn, esco_param_cvsd, ARRAY_SIZE(esco_param_cvsd))) return -EINVAL; @@ -531,7 +534,7 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle) param = &esco_param_msbc[conn->attempt - 1]; break; case SCO_AIRMODE_CVSD: - if (lmp_esco_capable(conn->link)) { + if (conn->parent && lmp_esco_capable(conn->parent)) { if (!find_next_esco_param(conn, esco_param_cvsd, ARRAY_SIZE(esco_param_cvsd))) return false; @@ -637,21 +640,22 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, /* Device _must_ be locked */ void hci_sco_setup(struct hci_conn *conn, __u8 status) { - struct hci_conn *sco = conn->link; + struct hci_link *link; - if (!sco) + link = list_first_entry_or_null(&conn->link_list, struct hci_link, list); + if (!link || !link->conn) return; BT_DBG("hcon %p", conn); if (!status) { if (lmp_esco_capable(conn->hdev)) - hci_setup_sync(sco, conn->handle); + hci_setup_sync(link->conn, conn->handle); else - hci_add_sco(sco, conn->handle); + hci_add_sco(link->conn, conn->handle); } else { - hci_connect_cfm(sco, status); - hci_conn_del(sco); + hci_connect_cfm(link->conn, status); + hci_conn_del(link->conn); } } @@ -1042,6 +1046,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, skb_queue_head_init(&conn->data_q); INIT_LIST_HEAD(&conn->chan_list); + INIT_LIST_HEAD(&conn->link_list); INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout); INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept); @@ -1069,15 +1074,39 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, return conn; } -static bool hci_conn_unlink(struct hci_conn *conn) +static void hci_conn_unlink(struct hci_conn *conn) { + struct hci_dev *hdev = conn->hdev; + + bt_dev_dbg(hdev, "hcon %p", conn); + + if (!conn->parent) { + struct hci_link *link, *t; + + list_for_each_entry_safe(link, t, &conn->link_list, list) + hci_conn_unlink(link->conn); + + return; + } + if (!conn->link) - return false; + return; + + hci_conn_put(conn->parent); + conn->parent = NULL; - conn->link->link = NULL; + list_del_rcu(&conn->link->list); + synchronize_rcu(); + + kfree(conn->link); conn->link = NULL; - return true; + /* Due to race, SCO connection might be not established + * yet at this point. Delete it now, otherwise it is + * possible for it to be stuck and can't be deleted. + */ + if (conn->handle == HCI_CONN_HANDLE_UNSET) + hci_conn_del(conn); } int hci_conn_del(struct hci_conn *conn) @@ -1091,18 +1120,7 @@ int hci_conn_del(struct hci_conn *conn) cancel_delayed_work_sync(&conn->idle_work); if (conn->type == ACL_LINK) { - struct hci_conn *link = conn->link; - - if (link) { - hci_conn_unlink(conn); - /* Due to race, SCO connection might be not established - * yet at this point. Delete it now, otherwise it is - * possible for it to be stuck and can't be deleted. - */ - if (link->handle == HCI_CONN_HANDLE_UNSET) - hci_conn_del(link); - } - + hci_conn_unlink(conn); /* Unacked frames */ hdev->acl_cnt += conn->sent; } else if (conn->type == LE_LINK) { @@ -1113,7 +1131,7 @@ int hci_conn_del(struct hci_conn *conn) else hdev->acl_cnt += conn->sent; } else { - struct hci_conn *acl = conn->link; + struct hci_conn *acl = conn->parent; if (acl) { hci_conn_unlink(conn); @@ -1600,11 +1618,40 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, return acl; } +static struct hci_link *hci_conn_link(struct hci_conn *parent, + struct hci_conn *conn) +{ + struct hci_dev *hdev = parent->hdev; + struct hci_link *link; + + bt_dev_dbg(hdev, "parent %p hcon %p", parent, conn); + + if (conn->link) + return conn->link; + + if (conn->parent) + return NULL; + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) + return NULL; + + link->conn = hci_conn_hold(conn); + conn->link = link; + conn->parent = hci_conn_get(parent); + + /* Use list_add_tail_rcu append to the list */ + list_add_tail_rcu(&link->list, &parent->link_list); + + return link; +} + struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, __u16 setting, struct bt_codec *codec) { struct hci_conn *acl; struct hci_conn *sco; + struct hci_link *link; acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING, CONN_REASON_SCO_CONNECT); @@ -1620,10 +1667,12 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, } } - acl->link = sco; - sco->link = acl; - - hci_conn_hold(sco); + link = hci_conn_link(acl, sco); + if (!link) { + hci_conn_drop(acl); + hci_conn_drop(sco); + return NULL; + } sco->setting = setting; sco->codec = *codec; @@ -1890,7 +1939,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) u8 cig; memset(&cmd, 0, sizeof(cmd)); - cmd.cis[0].acl_handle = cpu_to_le16(conn->link->handle); + cmd.cis[0].acl_handle = cpu_to_le16(conn->parent->handle); cmd.cis[0].cis_handle = cpu_to_le16(conn->handle); cmd.cp.num_cis++; cig = conn->iso_qos.ucast.cig; @@ -1903,11 +1952,12 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis]; if (conn == data || conn->type != ISO_LINK || - conn->state == BT_CONNECTED || conn->iso_qos.ucast.cig != cig) + conn->state == BT_CONNECTED || + conn->iso_qos.ucast.cig != cig) continue; /* Check if all CIS(s) belonging to a CIG are ready */ - if (!conn->link || conn->link->state != BT_CONNECTED || + if (!conn->parent || conn->parent->state != BT_CONNECTED || conn->state != BT_CONNECT) { cmd.cp.num_cis = 0; break; @@ -1924,7 +1974,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) * command have been generated, the Controller shall return the * error code Command Disallowed (0x0C). */ - cis->acl_handle = cpu_to_le16(conn->link->handle); + cis->acl_handle = cpu_to_le16(conn->parent->handle); cis->cis_handle = cpu_to_le16(conn->handle); cmd.cp.num_cis++; } @@ -1943,15 +1993,33 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) int hci_le_create_cis(struct hci_conn *conn) { struct hci_conn *cis; + struct hci_link *link, *t; struct hci_dev *hdev = conn->hdev; int err; + bt_dev_dbg(hdev, "hcon %p", conn); + switch (conn->type) { case LE_LINK: - if (!conn->link || conn->state != BT_CONNECTED) + if (conn->state != BT_CONNECTED || list_empty(&conn->link_list)) return -EINVAL; - cis = conn->link; - break; + + cis = NULL; + + /* hci_conn_link uses list_add_tail_rcu so the list is in + * the same order as the connections are requested. + */ + list_for_each_entry_safe(link, t, &conn->link_list, list) { + if (link->conn->state == BT_BOUND) { + err = hci_le_create_cis(link->conn); + if (err) + return err; + + cis = link->conn; + } + } + + return cis ? 0 : -EINVAL; case ISO_LINK: cis = conn; break; @@ -2172,6 +2240,7 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, { struct hci_conn *le; struct hci_conn *cis; + struct hci_link *link; if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) le = hci_connect_le(hdev, dst, dst_type, false, @@ -2197,16 +2266,18 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, return cis; } - le->link = cis; - cis->link = le; - - hci_conn_hold(cis); + link = hci_conn_link(le, cis); + if (!link) { + hci_conn_drop(le); + hci_conn_drop(cis); + return NULL; + } /* If LE is already connected and CIS handle is already set proceed to * Create CIS immediately. */ if (le->state == BT_CONNECTED && cis->handle != HCI_CONN_HANDLE_UNSET) - hci_le_create_cis(le); + hci_le_create_cis(cis); return cis; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0e0a93cc12186..d00ef6e3fc451 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2345,7 +2345,8 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) { struct hci_cp_add_sco *cp; - struct hci_conn *acl, *sco; + struct hci_conn *acl; + struct hci_link *link; __u16 handle; bt_dev_dbg(hdev, "status 0x%2.2x", status); @@ -2365,12 +2366,13 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) acl = hci_conn_hash_lookup_handle(hdev, handle); if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; + link = list_first_entry_or_null(&acl->link_list, + struct hci_link, list); + if (link && link->conn) { + link->conn->state = BT_CLOSED; - hci_connect_cfm(sco, status); - hci_conn_del(sco); + hci_connect_cfm(link->conn, status); + hci_conn_del(link->conn); } } @@ -2637,74 +2639,61 @@ static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status) hci_dev_unlock(hdev); } -static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) +static void hci_setup_sync_conn_status(struct hci_dev *hdev, __u16 handle, + __u8 status) { - struct hci_cp_setup_sync_conn *cp; - struct hci_conn *acl, *sco; - __u16 handle; - - bt_dev_dbg(hdev, "status 0x%2.2x", status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN); - if (!cp) - return; + struct hci_conn *acl; + struct hci_link *link; - handle = __le16_to_cpu(cp->handle); - - bt_dev_dbg(hdev, "handle 0x%4.4x", handle); + bt_dev_dbg(hdev, "handle 0x%4.4x status 0x%2.2x", handle, status); hci_dev_lock(hdev); acl = hci_conn_hash_lookup_handle(hdev, handle); if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; + link = list_first_entry_or_null(&acl->link_list, + struct hci_link, list); + if (link && link->conn) { + link->conn->state = BT_CLOSED; - hci_connect_cfm(sco, status); - hci_conn_del(sco); + hci_connect_cfm(link->conn, status); + hci_conn_del(link->conn); } } hci_dev_unlock(hdev); } -static void hci_cs_enhanced_setup_sync_conn(struct hci_dev *hdev, __u8 status) +static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) { - struct hci_cp_enhanced_setup_sync_conn *cp; - struct hci_conn *acl, *sco; - __u16 handle; + struct hci_cp_setup_sync_conn *cp; bt_dev_dbg(hdev, "status 0x%2.2x", status); if (!status) return; - cp = hci_sent_cmd_data(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN); + cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN); if (!cp) return; - handle = __le16_to_cpu(cp->handle); + hci_setup_sync_conn_status(hdev, __le16_to_cpu(cp->handle), status); +} - bt_dev_dbg(hdev, "handle 0x%4.4x", handle); +static void hci_cs_enhanced_setup_sync_conn(struct hci_dev *hdev, __u8 status) +{ + struct hci_cp_enhanced_setup_sync_conn *cp; - hci_dev_lock(hdev); + bt_dev_dbg(hdev, "status 0x%2.2x", status); - acl = hci_conn_hash_lookup_handle(hdev, handle); - if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; + if (!status) + return; - hci_connect_cfm(sco, status); - hci_conn_del(sco); - } - } + cp = hci_sent_cmd_data(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN); + if (!cp) + return; - hci_dev_unlock(hdev); + hci_setup_sync_conn_status(hdev, __le16_to_cpu(cp->handle), status); } static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) @@ -3834,19 +3823,20 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data, rcu_read_lock(); list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) { - if (conn->type != ISO_LINK || conn->iso_qos.ucast.cig != rp->cig_id || + if (conn->type != ISO_LINK || + conn->iso_qos.ucast.cig != rp->cig_id || conn->state == BT_CONNECTED) continue; conn->handle = __le16_to_cpu(rp->handle[i++]); - bt_dev_dbg(hdev, "%p handle 0x%4.4x link %p", conn, - conn->handle, conn->link); + bt_dev_dbg(hdev, "%p handle 0x%4.4x parent %p", conn, + conn->handle, conn->parent); /* Create CIS if LE is already connected */ - if (conn->link && conn->link->state == BT_CONNECTED) { + if (conn->parent && conn->parent->state == BT_CONNECTED) { rcu_read_unlock(); - hci_le_create_cis(conn->link); + hci_le_create_cis(conn); rcu_read_lock(); } @@ -5031,7 +5021,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data, if (conn->out) { conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | (hdev->esco_type & EDR_ESCO_MASK); - if (hci_setup_sync(conn, conn->link->handle)) + if (hci_setup_sync(conn, conn->parent->handle)) goto unlock; } fallthrough; diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 74117df03a3fa..34d55a85d8f6f 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1657,8 +1657,12 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) /* Check if LE link has failed */ if (status) { - if (hcon->link) - iso_conn_del(hcon->link, bt_to_errno(status)); + struct hci_link *link, *t; + + list_for_each_entry_safe(link, t, &hcon->link_list, + list) + iso_conn_del(link->conn, bt_to_errno(status)); + return; } From c14516faede33c2c31da45cf950d55dbff42962e Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 11 Apr 2023 16:14:25 -0700 Subject: [PATCH 58/72] Bluetooth: hci_conn: Fix not matching by CIS ID This fixes only matching CIS by address which prevents creating new hcon if upper layer is requesting a specific CIS ID. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 12 +++++++++++- net/bluetooth/hci_conn.c | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4fe1e71cb9d83..a6c8aee2f256c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1180,7 +1180,9 @@ static inline struct hci_conn *hci_conn_hash_lookup_le(struct hci_dev *hdev, static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev, bdaddr_t *ba, - __u8 ba_type) + __u8 ba_type, + __u8 cig, + __u8 id) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; @@ -1191,6 +1193,14 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev, if (c->type != ISO_LINK) continue; + /* Match CIG ID if set */ + if (cig != BT_ISO_QOS_CIG_UNSET && cig != c->iso_qos.ucast.cig) + continue; + + /* Match CIS ID if set */ + if (id != BT_ISO_QOS_CIS_UNSET && id != c->iso_qos.ucast.cis) + continue; + if (ba_type == c->dst_type && !bacmp(&c->dst, ba)) { rcu_read_unlock(); return c; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index d8466abbb36a5..c215e983e2874 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1841,7 +1841,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, { struct hci_conn *cis; - cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type); + cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type, qos->ucast.cig, + qos->ucast.cis); if (!cis) { cis = hci_conn_add(hdev, ISO_LINK, dst, HCI_ROLE_MASTER); if (!cis) From c09b80be6ffc338634b2f5f8cfa12b6843410834 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 12 Apr 2023 17:45:51 -0700 Subject: [PATCH 59/72] Bluetooth: hci_conn: Fix not waiting for HCI_EVT_LE_CIS_ESTABLISHED When submitting HCI_OP_LE_CREATE_CIS the code shall wait for HCI_EVT_LE_CIS_ESTABLISHED thus enforcing the serialization of HCI_OP_LE_CREATE_CIS as the Core spec does not allow to send them in parallel: BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E page 2566: If the Host issues this command before all the HCI_LE_CIS_Established events from the previous use of the command have been generated, the Controller shall return the error code Command Disallowed (0x0C). Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_sync.h | 2 + net/bluetooth/hci_conn.c | 58 +--------------------------- net/bluetooth/hci_sync.c | 65 ++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 57 deletions(-) diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h index 17f5a4c32f36e..f61b249787fca 100644 --- a/include/net/bluetooth/hci_sync.h +++ b/include/net/bluetooth/hci_sync.h @@ -122,6 +122,8 @@ int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason); int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn); +int hci_le_create_cis_sync(struct hci_dev *hdev, struct hci_conn *conn); + int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle); int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c215e983e2874..640b951bf40a1 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1932,63 +1932,7 @@ bool hci_iso_setup_path(struct hci_conn *conn) static int hci_create_cis_sync(struct hci_dev *hdev, void *data) { - struct { - struct hci_cp_le_create_cis cp; - struct hci_cis cis[0x1f]; - } cmd; - struct hci_conn *conn = data; - u8 cig; - - memset(&cmd, 0, sizeof(cmd)); - cmd.cis[0].acl_handle = cpu_to_le16(conn->parent->handle); - cmd.cis[0].cis_handle = cpu_to_le16(conn->handle); - cmd.cp.num_cis++; - cig = conn->iso_qos.ucast.cig; - - hci_dev_lock(hdev); - - rcu_read_lock(); - - list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) { - struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis]; - - if (conn == data || conn->type != ISO_LINK || - conn->state == BT_CONNECTED || - conn->iso_qos.ucast.cig != cig) - continue; - - /* Check if all CIS(s) belonging to a CIG are ready */ - if (!conn->parent || conn->parent->state != BT_CONNECTED || - conn->state != BT_CONNECT) { - cmd.cp.num_cis = 0; - break; - } - - /* Group all CIS with state BT_CONNECT since the spec don't - * allow to send them individually: - * - * BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E - * page 2566: - * - * If the Host issues this command before all the - * HCI_LE_CIS_Established events from the previous use of the - * command have been generated, the Controller shall return the - * error code Command Disallowed (0x0C). - */ - cis->acl_handle = cpu_to_le16(conn->parent->handle); - cis->cis_handle = cpu_to_le16(conn->handle); - cmd.cp.num_cis++; - } - - rcu_read_unlock(); - - hci_dev_unlock(hdev); - - if (!cmd.cp.num_cis) - return 0; - - return hci_send_cmd(hdev, HCI_OP_LE_CREATE_CIS, sizeof(cmd.cp) + - sizeof(cmd.cis[0]) * cmd.cp.num_cis, &cmd); + return hci_le_create_cis_sync(hdev, data); } int hci_le_create_cis(struct hci_conn *conn) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 00017f75cd419..6f060d00a70af 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6137,6 +6137,71 @@ int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn) return err; } +int hci_le_create_cis_sync(struct hci_dev *hdev, struct hci_conn *conn) +{ + struct { + struct hci_cp_le_create_cis cp; + struct hci_cis cis[0x1f]; + } cmd; + u8 cig; + struct hci_conn *hcon = conn; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cis[0].acl_handle = cpu_to_le16(conn->parent->handle); + cmd.cis[0].cis_handle = cpu_to_le16(conn->handle); + cmd.cp.num_cis++; + cig = conn->iso_qos.ucast.cig; + + hci_dev_lock(hdev); + + rcu_read_lock(); + + list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) { + struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis]; + + if (conn == hcon || conn->type != ISO_LINK || + conn->state == BT_CONNECTED || + conn->iso_qos.ucast.cig != cig) + continue; + + /* Check if all CIS(s) belonging to a CIG are ready */ + if (!conn->parent || conn->parent->state != BT_CONNECTED || + conn->state != BT_CONNECT) { + cmd.cp.num_cis = 0; + break; + } + + /* Group all CIS with state BT_CONNECT since the spec don't + * allow to send them individually: + * + * BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E + * page 2566: + * + * If the Host issues this command before all the + * HCI_LE_CIS_Established events from the previous use of the + * command have been generated, the Controller shall return the + * error code Command Disallowed (0x0C). + */ + cis->acl_handle = cpu_to_le16(conn->parent->handle); + cis->cis_handle = cpu_to_le16(conn->handle); + cmd.cp.num_cis++; + } + + rcu_read_unlock(); + + hci_dev_unlock(hdev); + + if (!cmd.cp.num_cis) + return 0; + + /* Wait for HCI_LE_CIS_Established */ + return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_CREATE_CIS, + sizeof(cmd.cp) + sizeof(cmd.cis[0]) * + cmd.cp.num_cis, &cmd, + HCI_EVT_LE_CIS_ESTABLISHED, + conn->conn_timeout, NULL); +} + int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle) { struct hci_cp_le_remove_cig cp; From 91b6d02ddcd113352bdd895990b252065c596de7 Mon Sep 17 00:00:00 2001 From: Raul Cheleguini Date: Thu, 23 Mar 2023 10:45:39 -0300 Subject: [PATCH 60/72] Bluetooth: Add new quirk for broken set random RPA timeout for ATS2851 The ATS2851 based controller advertises support for command "LE Set Random Private Address Timeout" but does not actually implement it, impeding the controller initialization. Add the quirk HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT to unblock the controller initialization. < HCI Command: LE Set Resolvable Private... (0x08|0x002e) plen 2 Timeout: 900 seconds > HCI Event: Command Status (0x0f) plen 4 LE Set Resolvable Private Address Timeout (0x08|0x002e) ncmd 1 Status: Unknown HCI Command (0x01) Co-developed-by: imoc Signed-off-by: imoc Signed-off-by: Raul Cheleguini Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 1 + include/net/bluetooth/hci.h | 8 ++++++++ net/bluetooth/hci_sync.c | 6 +++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 2303b0a663231..3aa189b1986d4 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4149,6 +4149,7 @@ static int btusb_probe(struct usb_interface *intf, /* Support is advertised, but not implemented */ set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, &hdev->quirks); set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks); } diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 997107bfc0b12..07df96c47ef4f 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -301,6 +301,14 @@ enum { * don't actually support features declared there. */ HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, + + /* + * When this quirk is set, the HCI_OP_LE_SET_RPA_TIMEOUT command is + * skipped during initialization. This is required for the Actions + * Semiconductor ATS2851 based controllers, which erroneously claims + * to support it. + */ + HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, }; /* HCI device flags */ diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 6f060d00a70af..771aaa8089672 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4093,7 +4093,8 @@ static int hci_le_set_rpa_timeout_sync(struct hci_dev *hdev) { __le16 timeout = cpu_to_le16(hdev->rpa_timeout); - if (!(hdev->commands[35] & 0x04)) + if (!(hdev->commands[35] & 0x04) || + test_bit(HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, &hdev->quirks)) return 0; return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_RPA_TIMEOUT, @@ -4553,6 +4554,9 @@ static const struct { "HCI Set Event Filter command not supported."), HCI_QUIRK_BROKEN(ENHANCED_SETUP_SYNC_CONN, "HCI Enhanced Setup Synchronous Connection command is " + "advertised, but not supported."), + HCI_QUIRK_BROKEN(SET_RPA_TIMEOUT, + "HCI LE Set Random Private Address Timeout command is " "advertised, but not supported.") }; From db2bf510bd5d57f064d9e1db395ed86a08320c54 Mon Sep 17 00:00:00 2001 From: Liu Jian Date: Fri, 14 Apr 2023 18:30:06 +0800 Subject: [PATCH 61/72] Revert "Bluetooth: btsdio: fix use after free bug in btsdio_remove due to unfinished work" This reverts commit 1e9ac114c4428fdb7ff4635b45d4f46017e8916f. This patch introduces a possible null-ptr-def problem. Revert it. And the fixed bug by this patch have resolved by commit 73f7b171b7c0 ("Bluetooth: btsdio: fix use after free bug in btsdio_remove due to race condition"). Fixes: 1e9ac114c442 ("Bluetooth: btsdio: fix use after free bug in btsdio_remove due to unfinished work") Signed-off-by: Liu Jian Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btsdio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 51000320e1ea8..f19d31ee37ea8 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -354,7 +354,6 @@ static void btsdio_remove(struct sdio_func *func) BT_DBG("func %p", func); - cancel_work_sync(&data->work); if (!data) return; From 000c2fa2c144c499c881a101819cf1936a1f7cf2 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Sun, 16 Apr 2023 16:02:51 +0800 Subject: [PATCH 62/72] bluetooth: Add cmd validity checks at the start of hci_sock_ioctl() Previously, channel open messages were always sent to monitors on the first ioctl() call for unbound HCI sockets, even if the command and arguments were completely invalid. This can leave an exploitable hole with the abuse of invalid ioctl calls. This commit hardens the ioctl processing logic by first checking if the command is valid, and immediately returning with an ENOIOCTLCMD error code if it is not. This ensures that ioctl calls with invalid commands are free of side effects, and increases the difficulty of further exploitation by forcing exploitation to find a way to pass a valid command first. Signed-off-by: Ruihan Li Co-developed-by: Marcel Holtmann Signed-off-by: Marcel Holtmann Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sock.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 06581223238c5..8d8950101cee6 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -987,6 +987,34 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, BT_DBG("cmd %x arg %lx", cmd, arg); + /* Make sure the cmd is valid before doing anything */ + switch (cmd) { + case HCIGETDEVLIST: + case HCIGETDEVINFO: + case HCIGETCONNLIST: + case HCIDEVUP: + case HCIDEVDOWN: + case HCIDEVRESET: + case HCIDEVRESTAT: + case HCISETSCAN: + case HCISETAUTH: + case HCISETENCRYPT: + case HCISETPTYPE: + case HCISETLINKPOL: + case HCISETLINKMODE: + case HCISETACLMTU: + case HCISETSCOMTU: + case HCIINQUIRY: + case HCISETRAW: + case HCIGETCONNINFO: + case HCIGETAUTHINFO: + case HCIBLOCKADDR: + case HCIUNBLOCKADDR: + break; + default: + return -ENOIOCTLCMD; + } + lock_sock(sk); if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { From 25e97f7b1866e6b8503be349eeea44bb52d661ce Mon Sep 17 00:00:00 2001 From: Min Li Date: Mon, 17 Apr 2023 10:27:54 +0800 Subject: [PATCH 63/72] Bluetooth: L2CAP: fix "bad unlock balance" in l2cap_disconnect_rsp conn->chan_lock isn't acquired before l2cap_get_chan_by_scid, if l2cap_get_chan_by_scid returns NULL, then 'bad unlock balance' is triggered. Reported-by: syzbot+9519d6b5b79cf7787cf3@syzkaller.appspotmail.com Link: https://lore.kernel.org/all/000000000000894f5f05f95e9f4d@google.com/ Signed-off-by: Min Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5cc95fd17f7d6..376b523c7b268 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4693,7 +4693,6 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, chan = l2cap_get_chan_by_scid(conn, scid); if (!chan) { - mutex_unlock(&conn->chan_lock); return 0; } From 25c150ac103a4ebeed0319994c742a90634ddf18 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Sun, 16 Apr 2023 16:14:04 +0800 Subject: [PATCH 64/72] bluetooth: Perform careful capability checks in hci_sock_ioctl() Previously, capability was checked using capable(), which verified that the caller of the ioctl system call had the required capability. In addition, the result of the check would be stored in the HCI_SOCK_TRUSTED flag, making it persistent for the socket. However, malicious programs can abuse this approach by deliberately sharing an HCI socket with a privileged task. The HCI socket will be marked as trusted when the privileged task occasionally makes an ioctl call. This problem can be solved by using sk_capable() to check capability, which ensures that not only the current task but also the socket opener has the specified capability, thus reducing the risk of privilege escalation through the previously identified vulnerability. Cc: stable@vger.kernel.org Fixes: f81f5b2db869 ("Bluetooth: Send control open and close messages for HCI raw sockets") Signed-off-by: Ruihan Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sock.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 8d8950101cee6..1d249d839819d 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1031,7 +1031,14 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, if (hci_sock_gen_cookie(sk)) { struct sk_buff *skb; - if (capable(CAP_NET_ADMIN)) + /* Perform careful checks before setting the HCI_SOCK_TRUSTED + * flag. Make sure that not only the current task but also + * the socket opener has the required capability, since + * privileged programs can be tricked into making ioctl calls + * on HCI sockets, and the socket should not be marked as + * trusted simply because the ioctl caller is privileged. + */ + if (sk_capable(sk, CAP_NET_ADMIN)) hci_sock_set_flag(sk, HCI_SOCK_TRUSTED); /* Send event to monitor */ From 0ab905c3be8c2be368821dd2bd1f8d126064d993 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Mon, 17 Apr 2023 17:39:59 +0800 Subject: [PATCH 65/72] Bluetooth: Devcoredump: Fix storing u32 without specifying byte order issue API hci_devcd_init() stores its u32 type parameter @dump_size into skb, but it does not specify which byte order is used to store the integer, let us take little endian to store and parse the integer. Fixes: f5cc609d09d4 ("Bluetooth: Add support for hci devcoredump") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/coredump.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/coredump.c b/net/bluetooth/coredump.c index 08fa98505454a..d2d2624ec708f 100644 --- a/net/bluetooth/coredump.c +++ b/net/bluetooth/coredump.c @@ -5,6 +5,7 @@ #include +#include #include #include @@ -180,25 +181,25 @@ static int hci_devcd_prepare(struct hci_dev *hdev, u32 dump_size) static void hci_devcd_handle_pkt_init(struct hci_dev *hdev, struct sk_buff *skb) { - u32 *dump_size; + u32 dump_size; if (hdev->dump.state != HCI_DEVCOREDUMP_IDLE) { DBG_UNEXPECTED_STATE(); return; } - if (skb->len != sizeof(*dump_size)) { + if (skb->len != sizeof(dump_size)) { bt_dev_dbg(hdev, "Invalid dump init pkt"); return; } - dump_size = skb_pull_data(skb, sizeof(*dump_size)); - if (!*dump_size) { + dump_size = get_unaligned_le32(skb_pull_data(skb, 4)); + if (!dump_size) { bt_dev_err(hdev, "Zero size dump init pkt"); return; } - if (hci_devcd_prepare(hdev, *dump_size)) { + if (hci_devcd_prepare(hdev, dump_size)) { bt_dev_err(hdev, "Failed to prepare for dump"); return; } @@ -441,7 +442,7 @@ int hci_devcd_init(struct hci_dev *hdev, u32 dump_size) return -ENOMEM; hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_INIT; - skb_put_data(skb, &dump_size, sizeof(dump_size)); + put_unaligned_le32(dump_size, skb_put(skb, 4)); skb_queue_tail(&hdev->dump.dump_q, skb); queue_work(hdev->workqueue, &hdev->dump.dump_rx); From 9a24ce5e29b15c4c6b0c89c04f9df6ce14addefa Mon Sep 17 00:00:00 2001 From: Max Chou Date: Mon, 17 Apr 2023 10:33:58 +0800 Subject: [PATCH 66/72] Bluetooth: btrtl: Firmware format v2 support Realtek changed the format of the firmware file as v2. The driver should implement the patch to extract the firmware data from the firmware file. The future chips must apply this patch for firmware loading. This patch is compatible with the both previous format and v2 as well. Signed-off-by: Allen Chen Signed-off-by: Alex Lu Tested-by: Hilda Wu Signed-off-by: Max Chou Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 350 +++++++++++++++++++++++++++++++++----- drivers/bluetooth/btrtl.h | 53 +++++- 2 files changed, 362 insertions(+), 41 deletions(-) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 0f256a8abef42..2ced74f8f87d1 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -21,6 +21,7 @@ #define RTL_CHIP_8723CS_VF 4 #define RTL_CHIP_8723CS_XX 5 #define RTL_EPATCH_SIGNATURE "Realtech" +#define RTL_EPATCH_SIGNATURE_V2 "RTBTCore" #define RTL_ROM_LMP_8703B 0x8703 #define RTL_ROM_LMP_8723A 0x1200 #define RTL_ROM_LMP_8723B 0x8723 @@ -43,6 +44,14 @@ .hci_ver = (hciv), \ .hci_bus = (bus) +#define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}}) +#define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}}) +#define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}}) + +#define RTL_PATCH_SNIPPETS 0x01 +#define RTL_PATCH_DUMMY_HEADER 0x02 +#define RTL_PATCH_SECURITY_HEADER 0x03 + enum btrtl_chip_id { CHIP_ID_8723A, CHIP_ID_8723B, @@ -81,6 +90,8 @@ struct btrtl_device_info { int cfg_len; bool drop_fw; int project_id; + u8 key_id; + struct list_head patch_subsecs; }; static const struct id_table ic_id_table[] = { @@ -343,6 +354,227 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version) return 0; } +static int btrtl_vendor_read_reg16(struct hci_dev *hdev, + struct rtl_vendor_cmd *cmd, u8 *rp) +{ + struct sk_buff *skb; + int err = 0; + + skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + rtl_dev_err(hdev, "RTL: Read reg16 failed (%d)", err); + return err; + } + + if (skb->len != 3 || skb->data[0]) { + bt_dev_err(hdev, "RTL: Read reg16 length mismatch"); + kfree_skb(skb); + return -EIO; + } + + if (rp) + memcpy(rp, skb->data + 1, 2); + + kfree_skb(skb); + + return 0; +} + +static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len) +{ + void *data = iov->data; + + if (iov->len < len) + return NULL; + + iov->data += len; + iov->len -= len; + + return data; +} + +static void btrtl_insert_ordered_subsec(struct rtl_subsection *node, + struct btrtl_device_info *btrtl_dev) +{ + struct list_head *pos; + struct list_head *next; + struct rtl_subsection *subsec; + + list_for_each_safe(pos, next, &btrtl_dev->patch_subsecs) { + subsec = list_entry(pos, struct rtl_subsection, list); + if (subsec->prio >= node->prio) + break; + } + __list_add(&node->list, pos->prev, pos); +} + +static int btrtl_parse_section(struct hci_dev *hdev, + struct btrtl_device_info *btrtl_dev, u32 opcode, + u8 *data, u32 len) +{ + struct rtl_section_hdr *hdr; + struct rtl_subsection *subsec; + struct rtl_common_subsec *common_subsec; + struct rtl_sec_hdr *sec_hdr; + int i; + u8 *ptr; + u16 num_subsecs; + u32 subsec_len; + int rc = 0; + struct rtl_iovec iov = { + .data = data, + .len = len, + }; + + hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); + if (!hdr) + return -EINVAL; + num_subsecs = le16_to_cpu(hdr->num); + + for (i = 0; i < num_subsecs; i++) { + common_subsec = rtl_iov_pull_data(&iov, sizeof(*common_subsec)); + if (!common_subsec) + break; + subsec_len = le32_to_cpu(common_subsec->len); + + rtl_dev_dbg(hdev, "subsec, eco 0x%02x, len %08x", + common_subsec->eco, subsec_len); + + ptr = rtl_iov_pull_data(&iov, subsec_len); + if (!ptr) + break; + + if (common_subsec->eco != btrtl_dev->rom_version + 1) + continue; + + switch (opcode) { + case RTL_PATCH_SECURITY_HEADER: + sec_hdr = (void *)common_subsec; + if (sec_hdr->key_id != btrtl_dev->key_id) + continue; + break; + } + + subsec = kzalloc(sizeof(*subsec), GFP_KERNEL); + if (!subsec) + return -ENOMEM; + subsec->opcode = opcode; + subsec->prio = common_subsec->prio; + subsec->len = subsec_len; + subsec->data = ptr; + btrtl_insert_ordered_subsec(subsec, btrtl_dev); + rc += subsec_len; + } + + return rc; +} + +static int rtlbt_parse_firmware_v2(struct hci_dev *hdev, + struct btrtl_device_info *btrtl_dev, + unsigned char **_buf) +{ + struct rtl_epatch_header_v2 *hdr; + int rc; + u8 reg_val[2]; + u8 key_id; + u32 num_sections; + struct rtl_section *section; + struct rtl_subsection *entry, *tmp; + u32 section_len; + u32 opcode; + int len = 0; + int i; + u8 *ptr; + struct rtl_iovec iov = { + .data = btrtl_dev->fw_data, + .len = btrtl_dev->fw_len - 7, /* Cut the tail */ + }; + + rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val); + if (rc < 0) + return -EIO; + key_id = reg_val[0]; + + rtl_dev_dbg(hdev, "%s: key id %u", __func__, key_id); + + btrtl_dev->key_id = key_id; + + hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); + if (!hdr) + return -EINVAL; + num_sections = le32_to_cpu(hdr->num_sections); + + rtl_dev_dbg(hdev, "FW version %08x-%08x", *((u32 *)hdr->fw_version), + *((u32 *)(hdr->fw_version + 4))); + + for (i = 0; i < num_sections; i++) { + section = rtl_iov_pull_data(&iov, sizeof(*section)); + if (!section) + break; + section_len = le32_to_cpu(section->len); + opcode = le32_to_cpu(section->opcode); + + rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode); + + ptr = rtl_iov_pull_data(&iov, section_len); + if (!ptr) + break; + + switch (opcode) { + case RTL_PATCH_SNIPPETS: + rc = btrtl_parse_section(hdev, btrtl_dev, opcode, + ptr, section_len); + break; + case RTL_PATCH_SECURITY_HEADER: + /* If key_id from chip is zero, ignore all security + * headers. + */ + if (!key_id) + break; + rc = btrtl_parse_section(hdev, btrtl_dev, opcode, + ptr, section_len); + break; + case RTL_PATCH_DUMMY_HEADER: + rc = btrtl_parse_section(hdev, btrtl_dev, opcode, + ptr, section_len); + break; + default: + rc = 0; + break; + } + if (rc < 0) { + rtl_dev_err(hdev, "RTL: Parse section (%u) err %d", + opcode, rc); + return rc; + } + len += rc; + } + + if (!len) + return -ENODATA; + + /* Allocate mem and copy all found subsecs. */ + ptr = kvmalloc(len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + len = 0; + list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) { + rtl_dev_dbg(hdev, "RTL: opcode %08x, addr %p, len 0x%x", + entry->opcode, entry->data, entry->len); + memcpy(ptr + len, entry->data, entry->len); + len += entry->len; + } + + if (!len) + return -EPERM; + + *_buf = ptr; + return len; +} + static int rtlbt_parse_firmware(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev, unsigned char **_buf) @@ -377,7 +609,18 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8852A, 25 }, /* 8852C */ }; - min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3; + if (btrtl_dev->fw_len <= 8) + return -EINVAL; + + if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) + min_size = sizeof(struct rtl_epatch_header) + + sizeof(extension_sig) + 3; + else if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8)) + min_size = sizeof(struct rtl_epatch_header_v2) + + sizeof(extension_sig) + 3; + else + return -EINVAL; + if (btrtl_dev->fw_len < min_size) return -EINVAL; @@ -442,12 +685,14 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, return -EINVAL; } - epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data; - if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) { + if (memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8) != 0) { + if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8)) + return rtlbt_parse_firmware_v2(hdev, btrtl_dev, _buf); rtl_dev_err(hdev, "bad EPATCH signature"); return -EINVAL; } + epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data; num_patches = le16_to_cpu(epatch_info->num_patches); BT_DBG("fw_version=%x, num_patches=%d", le32_to_cpu(epatch_info->fw_version), num_patches); @@ -511,6 +756,7 @@ static int rtl_download_firmware(struct hci_dev *hdev, int frag_len = RTL_FRAG_LEN; int ret = 0; int i; + int j = 0; struct sk_buff *skb; struct hci_rp_read_local_version *rp; @@ -521,17 +767,16 @@ static int rtl_download_firmware(struct hci_dev *hdev, for (i = 0; i < frag_num; i++) { struct sk_buff *skb; - BT_DBG("download fw (%d/%d)", i, frag_num); - - if (i > 0x7f) - dl_cmd->index = (i & 0x7f) + 1; - else - dl_cmd->index = i; + dl_cmd->index = j++; + if (dl_cmd->index == 0x7f) + j = 1; if (i == (frag_num - 1)) { dl_cmd->index |= 0x80; /* data end */ frag_len = fw_len % RTL_FRAG_LEN; } + rtl_dev_dbg(hdev, "download fw (%d/%d). index = %d", i, + frag_num, dl_cmd->index); memcpy(dl_cmd->data, data, frag_len); /* Send download command */ @@ -691,8 +936,16 @@ static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type) void btrtl_free(struct btrtl_device_info *btrtl_dev) { + struct rtl_subsection *entry, *tmp; + kvfree(btrtl_dev->fw_data); kvfree(btrtl_dev->cfg_data); + + list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) { + list_del(&entry->list); + kfree(entry); + } + kfree(btrtl_dev); } EXPORT_SYMBOL_GPL(btrtl_free); @@ -705,10 +958,11 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, struct hci_rp_read_local_version *resp; char cfg_name[40]; u16 hci_rev, lmp_subver; - u8 hci_ver, chip_type = 0; + u8 hci_ver, lmp_ver, chip_type = 0; int ret; u16 opcode; u8 cmd[2]; + u8 reg_val[2]; btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL); if (!btrtl_dev) { @@ -716,6 +970,31 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, goto err_alloc; } + INIT_LIST_HEAD(&btrtl_dev->patch_subsecs); + +check_version: + ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val); + if (ret < 0) + goto err_free; + lmp_subver = get_unaligned_le16(reg_val); + + if (lmp_subver == RTL_ROM_LMP_8822B) { + ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_REV, reg_val); + if (ret < 0) + goto err_free; + hci_rev = get_unaligned_le16(reg_val); + + /* 8822E */ + if (hci_rev == 0x000e) { + hci_ver = 0x0c; + lmp_ver = 0x0c; + btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, + hci_ver, hdev->bus, + chip_type); + goto next; + } + } + skb = btrtl_read_local_version(hdev); if (IS_ERR(skb)) { ret = PTR_ERR(skb); @@ -723,14 +1002,14 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, } resp = (struct hci_rp_read_local_version *)skb->data; - rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x", - resp->hci_ver, resp->hci_rev, - resp->lmp_ver, resp->lmp_subver); - hci_ver = resp->hci_ver; - hci_rev = le16_to_cpu(resp->hci_rev); + hci_ver = resp->hci_ver; + hci_rev = le16_to_cpu(resp->hci_rev); + lmp_ver = resp->lmp_ver; lmp_subver = le16_to_cpu(resp->lmp_subver); + kfree_skb(skb); + if (rtl_has_chip_type(lmp_subver)) { ret = rtl_read_chip_type(hdev, &chip_type); if (ret) @@ -740,8 +1019,15 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, hdev->bus, chip_type); - if (!btrtl_dev->ic_info) +next: + rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x", + hci_ver, hci_rev, + lmp_ver, lmp_subver); + + if (!btrtl_dev->ic_info && !btrtl_dev->drop_fw) btrtl_dev->drop_fw = true; + else + btrtl_dev->drop_fw = false; if (btrtl_dev->drop_fw) { opcode = hci_opcode_pack(0x3f, 0x66); @@ -750,41 +1036,25 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL); if (!skb) - goto out_free; + goto err_free; skb_put_data(skb, cmd, sizeof(cmd)); hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; - hdev->send(hdev, skb); + ret = hdev->send(hdev, skb); + if (ret < 0) { + bt_dev_err(hdev, "sending frame failed (%d)", ret); + kfree_skb(skb); + goto err_free; + } /* Ensure the above vendor command is sent to controller and * process has done. */ msleep(200); - /* Read the local version again. Expect to have the vanilla - * version as cold boot. - */ - skb = btrtl_read_local_version(hdev); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - goto err_free; - } - - resp = (struct hci_rp_read_local_version *)skb->data; - rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x", - resp->hci_ver, resp->hci_rev, - resp->lmp_ver, resp->lmp_subver); - - hci_ver = resp->hci_ver; - hci_rev = le16_to_cpu(resp->hci_rev); - lmp_subver = le16_to_cpu(resp->lmp_subver); - - btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, - hdev->bus, chip_type); + goto check_version; } -out_free: - kfree_skb(skb); if (!btrtl_dev->ic_info) { rtl_dev_info(hdev, "unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x", diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h index 349d72ee571b6..adb4c2c9abc50 100644 --- a/drivers/bluetooth/btrtl.h +++ b/drivers/bluetooth/btrtl.h @@ -49,7 +49,58 @@ struct rtl_vendor_config_entry { struct rtl_vendor_config { __le32 signature; __le16 total_len; - struct rtl_vendor_config_entry entry[]; + __u8 entry[]; +} __packed; + +struct rtl_epatch_header_v2 { + __u8 signature[8]; + __u8 fw_version[8]; + __le32 num_sections; +} __packed; + +struct rtl_section { + __le32 opcode; + __le32 len; + u8 data[]; +} __packed; + +struct rtl_section_hdr { + __le16 num; + __le16 reserved; +} __packed; + +struct rtl_common_subsec { + __u8 eco; + __u8 prio; + __u8 cb[2]; + __le32 len; + __u8 data[]; +}; + +struct rtl_sec_hdr { + __u8 eco; + __u8 prio; + __u8 key_id; + __u8 reserved; + __le32 len; + __u8 data[]; +} __packed; + +struct rtl_subsection { + struct list_head list; + u32 opcode; + u32 len; + u8 prio; + u8 *data; +}; + +struct rtl_iovec { + u8 *data; + u32 len; +}; + +struct rtl_vendor_cmd { + __u8 param[5]; } __packed; enum { From 9e080b53dafae52759c87a105abcb87aa0592cf4 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 17 Apr 2023 17:01:16 -0700 Subject: [PATCH 67/72] Bluetooth: btnxpuart: Fix sparse warnings This fixes the following sparse warnings: drivers/bluetooth/btnxpuart.c:681:23: sparse: sparse: restricted __le16 degrades to integer drivers/bluetooth/btnxpuart.c:690:82: sparse: sparse: incorrect type in argument 2 (different base types) @@ expected unsigned short [usertype] req_len @@ got restricted __le16 [usertype] len @@ drivers/bluetooth/btnxpuart.c:690:82: sparse: expected unsigned short [usertype] req_len drivers/bluetooth/btnxpuart.c:690:82: sparse: got restricted __le16 [usertype] len drivers/bluetooth/btnxpuart.c:694:84: sparse: sparse: incorrect type in argument 2 (different base types) @@ expected unsigned short [usertype] req_len @@ got restricted __le16 [usertype] len @@ drivers/bluetooth/btnxpuart.c:694:84: sparse: expected unsigned short [usertype] req_len drivers/bluetooth/btnxpuart.c:694:84: sparse: got restricted __le16 [usertype] len drivers/bluetooth/btnxpuart.c:708:23: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int [usertype] requested_len @@ got restricted __le16 [usertype] len @@ drivers/bluetooth/btnxpuart.c:708:23: sparse: expected unsigned int [usertype] requested_len drivers/bluetooth/btnxpuart.c:708:23: sparse: got restricted __le16 [usertype] len drivers/bluetooth/btnxpuart.c:787:78: sparse: sparse: incorrect type in argument 2 (different base types) @@ expected unsigned short [usertype] chipid @@ got restricted __le16 [usertype] chip_id @@ drivers/bluetooth/btnxpuart.c:787:78: sparse: expected unsigned short [usertype] chipid drivers/bluetooth/btnxpuart.c:787:78: sparse: got restricted __le16 [usertype] chip_id drivers/bluetooth/btnxpuart.c:810:74: sparse: sparse: incorrect type in argument 2 (different base types) @@ expected unsigned short [usertype] req_len @@ got restricted __le16 [usertype] len @@ drivers/bluetooth/btnxpuart.c:810:74: sparse: expected unsigned short [usertype] req_len drivers/bluetooth/btnxpuart.c:810:74: sparse: got restricted __le16 [usertype] len drivers/bluetooth/btnxpuart.c:815:76: sparse: sparse: incorrect type in argument 2 (different base types) @@ expected unsigned short [usertype] req_len @@ got restricted __le16 [usertype] len @@ drivers/bluetooth/btnxpuart.c:815:76: sparse: expected unsigned short [usertype] req_len drivers/bluetooth/btnxpuart.c:815:76: sparse: got restricted __le16 [usertype] len drivers/bluetooth/btnxpuart.c:834:16: sparse: sparse: restricted __le32 degrades to integer drivers/bluetooth/btnxpuart.c:843:55: sparse: sparse: restricted __le32 degrades to integer drivers/bluetooth/btnxpuart.c:844:36: sparse: sparse: incorrect type in argument 3 (different base types) @@ expected unsigned long [usertype] @@ got restricted __le16 [usertype] len @@ drivers/bluetooth/btnxpuart.c:844:36: sparse: expected unsigned long [usertype] drivers/bluetooth/btnxpuart.c:844:36: sparse: got restricted __le16 [usertype] len Reported-by: kernel test robot Link: https://lore.kernel.org/oe-kbuild-all/202304160736.Tsa0zTBU-lkp@intel.com/ Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 85 +++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 93f3afc0c0c80..31046f26e7124 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -683,12 +683,14 @@ static int nxp_recv_chip_ver_v1(struct hci_dev *hdev, struct sk_buff *skb) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); struct v1_start_ind *req; + __u16 chip_id; - req = (struct v1_start_ind *)skb_pull_data(skb, sizeof(struct v1_start_ind)); + req = skb_pull_data(skb, sizeof(*req)); if (!req) goto free_skb; - if ((req->chip_id ^ req->chip_id_comp) == 0xffff) { + chip_id = le16_to_cpu(req->chip_id ^ req->chip_id_comp); + if (chip_id == 0xffff) { nxpdev->fw_dnld_v1_offset = 0; nxpdev->fw_v1_sent_bytes = 0; nxpdev->fw_v1_expected_len = HDR_LEN; @@ -707,29 +709,34 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); struct btnxpuart_data *nxp_data = nxpdev->nxp_data; struct v1_data_req *req; - u32 requested_len; + __u16 len; if (!process_boot_signature(nxpdev)) goto free_skb; - req = (struct v1_data_req *)skb_pull_data(skb, sizeof(struct v1_data_req)); + req = skb_pull_data(skb, sizeof(*req)); if (!req) goto free_skb; - if ((req->len ^ req->len_comp) != 0xffff) { + len = __le16_to_cpu(req->len ^ req->len_comp); + if (len != 0xffff) { bt_dev_dbg(hdev, "ERR: Send NAK"); nxp_send_ack(NXP_NAK_V1, hdev); goto free_skb; } nxp_send_ack(NXP_ACK_V1, hdev); + len = __le16_to_cpu(req->len); + if (!nxp_data->helper_fw_name) { if (!nxpdev->timeout_changed) { - nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, req->len); + nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, + len); goto free_skb; } if (!nxpdev->baudrate_changed) { - nxpdev->baudrate_changed = nxp_fw_change_baudrate(hdev, req->len); + nxpdev->baudrate_changed = nxp_fw_change_baudrate(hdev, + len); if (nxpdev->baudrate_changed) { serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_SEC_BAUDRATE); @@ -748,13 +755,14 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) goto free_skb; } - requested_len = req->len; - if (requested_len == 0) { - bt_dev_dbg(hdev, "FW Downloaded Successfully: %zu bytes", nxpdev->fw->size); + if (!len) { + bt_dev_dbg(hdev, "FW Downloaded Successfully: %zu bytes", + nxpdev->fw->size); if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) { nxpdev->helper_downloaded = true; serdev_device_wait_until_sent(nxpdev->serdev, 0); - serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_SEC_BAUDRATE); + serdev_device_set_baudrate(nxpdev->serdev, + HCI_NXP_SEC_BAUDRATE); serdev_device_set_flow_control(nxpdev->serdev, 1); } else { clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); @@ -762,12 +770,12 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) } goto free_skb; } - if (requested_len & 0x01) { + if (len & 0x01) { /* The CRC did not match at the other end. * Simply send the same bytes again. */ - requested_len = nxpdev->fw_v1_sent_bytes; - bt_dev_dbg(hdev, "CRC error. Resend %d bytes of FW.", requested_len); + len = nxpdev->fw_v1_sent_bytes; + bt_dev_dbg(hdev, "CRC error. Resend %d bytes of FW.", len); } else { nxpdev->fw_dnld_v1_offset += nxpdev->fw_v1_sent_bytes; @@ -781,24 +789,23 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) * mismatch, clearly the driver and FW are out of sync, * and we need to re-send the previous header again. */ - if (requested_len == nxpdev->fw_v1_expected_len) { - if (requested_len == HDR_LEN) + if (len == nxpdev->fw_v1_expected_len) { + if (len == HDR_LEN) nxpdev->fw_v1_expected_len = nxp_get_data_len(nxpdev->fw->data + nxpdev->fw_dnld_v1_offset); else nxpdev->fw_v1_expected_len = HDR_LEN; - } else if (requested_len == HDR_LEN) { + } else if (len == HDR_LEN) { /* FW download out of sync. Send previous chunk again */ nxpdev->fw_dnld_v1_offset -= nxpdev->fw_v1_sent_bytes; nxpdev->fw_v1_expected_len = HDR_LEN; } } - if (nxpdev->fw_dnld_v1_offset + requested_len <= nxpdev->fw->size) - serdev_device_write_buf(nxpdev->serdev, - nxpdev->fw->data + nxpdev->fw_dnld_v1_offset, - requested_len); - nxpdev->fw_v1_sent_bytes = requested_len; + if (nxpdev->fw_dnld_v1_offset + len <= nxpdev->fw->size) + serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + + nxpdev->fw_dnld_v1_offset, len); + nxpdev->fw_v1_sent_bytes = len; free_skb: kfree_skb(skb); @@ -828,13 +835,16 @@ static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid) static int nxp_recv_chip_ver_v3(struct hci_dev *hdev, struct sk_buff *skb) { - struct v3_start_ind *req = skb_pull_data(skb, sizeof(struct v3_start_ind)); + struct v3_start_ind *req = skb_pull_data(skb, sizeof(*req)); struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + u16 chip_id; if (!process_boot_signature(nxpdev)) goto free_skb; - if (!nxp_request_firmware(hdev, nxp_get_fw_name_from_chipid(hdev, req->chip_id))) + chip_id = le16_to_cpu(req->chip_id); + if (!nxp_request_firmware(hdev, nxp_get_fw_name_from_chipid(hdev, + chip_id))) nxp_send_ack(NXP_ACK_V3, hdev); free_skb: @@ -846,23 +856,27 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); struct v3_data_req *req; + __u16 len; + __u32 offset; if (!process_boot_signature(nxpdev)) goto free_skb; - req = (struct v3_data_req *)skb_pull_data(skb, sizeof(struct v3_data_req)); + req = skb_pull_data(skb, sizeof(*req)); if (!req || !nxpdev->fw) goto free_skb; nxp_send_ack(NXP_ACK_V3, hdev); + len = __le16_to_cpu(req->len); + if (!nxpdev->timeout_changed) { - nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, req->len); + nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, len); goto free_skb; } if (!nxpdev->baudrate_changed) { - nxpdev->baudrate_changed = nxp_fw_change_baudrate(hdev, req->len); + nxpdev->baudrate_changed = nxp_fw_change_baudrate(hdev, len); if (nxpdev->baudrate_changed) { serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_SEC_BAUDRATE); @@ -873,25 +887,28 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } if (req->len == 0) { - bt_dev_dbg(hdev, "FW Downloaded Successfully: %zu bytes", nxpdev->fw->size); + bt_dev_dbg(hdev, "FW Downloaded Successfully: %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; } if (req->error) - bt_dev_dbg(hdev, "FW Download received err 0x%02x from chip", req->error); + bt_dev_dbg(hdev, "FW Download received err 0x%02x from chip", + req->error); - if (req->offset < nxpdev->fw_v3_offset_correction) { + offset = __le32_to_cpu(req->offset); + if (offset < nxpdev->fw_v3_offset_correction) { /* This scenario should ideally never occur. But if it ever does, * FW is out of sync and needs a power cycle. */ - bt_dev_err(hdev, "Something went wrong during FW download. Please power cycle and try again"); + bt_dev_err(hdev, "Something went wrong during FW download"); + bt_dev_err(hdev, "Please power cycle and try again"); goto free_skb; } - serdev_device_write_buf(nxpdev->serdev, - nxpdev->fw->data + req->offset - nxpdev->fw_v3_offset_correction, - req->len); + serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + offset - + nxpdev->fw_v3_offset_correction, len); free_skb: kfree_skb(skb); From 7948fe1c92d92313eea5453f83deb7f0141355e8 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 18 Apr 2023 13:43:54 +0800 Subject: [PATCH 68/72] Bluetooth: btrtl: Add the support for RTL8851B Add the support for RTL8851B BT controller on USB interface. The necessary firmware will be submitted to linux-firmware project. Note that the Bluetooth devices WITH the VID=0x0bda would be set the feature quirk in btrtl_setup_realtek(). It's able to ignore the feature flag set for the specific VID and PID in blacklist_table[] of btusb.c. (check [1]) If Realtek Bluetooth chips WITHOUT the VID=0x0bda, it shall be added the feature flag for the specific VID and PID in blacklist_table[] of btusb.c. (check [2]) [1] '9ab9235fe5cf ("Bluetooth: btrtl: Enable WBS for the specific Realtek devices")' [2] '73280f13c9bb ("Bluetooth: btusb: Add the more support IDs for Realtek RTL8822CE")' The device info from /sys/kernel/debug/usb/devices as below. T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 33 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0bda ProdID=b851 Rev= 0.00 S: Manufacturer=Realtek S: Product=802.11ax WLAN Adapter S: SerialNumber=00E04C885A01 C:* #Ifs= 3 Cfg#= 1 Atr=80 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 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=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) 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=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) 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=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 8 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=09(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0a(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0b(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0c(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Signed-off-by: Max Chou Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 2ced74f8f87d1..2915c82d719d8 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -29,6 +29,7 @@ #define RTL_ROM_LMP_8761A 0x8761 #define RTL_ROM_LMP_8822B 0x8822 #define RTL_ROM_LMP_8852A 0x8852 +#define RTL_ROM_LMP_8851B 0x8851 #define RTL_CONFIG_MAGIC 0x8723ab55 #define IC_MATCH_FL_LMPSUBV (1 << 0) @@ -65,6 +66,7 @@ enum btrtl_chip_id { CHIP_ID_8852A = 18, CHIP_ID_8852B = 20, CHIP_ID_8852C = 25, + CHIP_ID_8851B = 36, }; struct id_table { @@ -271,6 +273,14 @@ static const struct id_table ic_id_table[] = { .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852cu_fw.bin", .cfg_name = "rtl_bt/rtl8852cu_config" }, + + /* 8851B */ + { IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB), + .config_needed = false, + .has_rom_version = true, + .has_msft_ext = false, + .fw_name = "rtl_bt/rtl8851bu_fw.bin", + .cfg_name = "rtl_bt/rtl8851bu_config" }, }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, @@ -607,6 +617,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8852A, 18 }, /* 8852A */ { RTL_ROM_LMP_8852A, 20 }, /* 8852B */ { RTL_ROM_LMP_8852A, 25 }, /* 8852C */ + { RTL_ROM_LMP_8851B, 36 }, /* 8851B */ }; if (btrtl_dev->fw_len <= 8) @@ -1134,6 +1145,7 @@ int btrtl_download_firmware(struct hci_dev *hdev, case RTL_ROM_LMP_8822B: case RTL_ROM_LMP_8852A: case RTL_ROM_LMP_8703B: + case RTL_ROM_LMP_8851B: return btrtl_setup_rtl8723b(hdev, btrtl_dev); default: rtl_dev_info(hdev, "assuming no firmware upload needed"); @@ -1158,6 +1170,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) case CHIP_ID_8852A: case CHIP_ID_8852B: case CHIP_ID_8852C: + case CHIP_ID_8851B: set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); @@ -1370,3 +1383,5 @@ MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8851bu_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8851bu_config.bin"); From f419863588217f76eaf754e1dfce21ea7fcb026d Mon Sep 17 00:00:00 2001 From: Archie Pusaka Date: Thu, 20 Apr 2023 20:23:36 +0800 Subject: [PATCH 69/72] Bluetooth: Cancel sync command before suspend and power off Some of the sync commands might take a long time to complete, e.g. LE Create Connection when the peer device isn't responding might take 20 seconds before it times out. If suspend command is issued during this time, it will need to wait for completion since both commands are using the same sync lock. This patch cancel any running sync commands before attempting to suspend or adapter power off. Signed-off-by: Archie Pusaka Reviewed-by: Ying Hsu Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_core.c | 3 +++ net/bluetooth/mgmt.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 393b317ae68f6..a856b1051d355 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2803,6 +2803,9 @@ int hci_suspend_dev(struct hci_dev *hdev) if (mgmt_powering_down(hdev)) return 0; + /* Cancel potentially blocking sync operation before suspend */ + __hci_cmd_sync_cancel(hdev, -EHOSTDOWN); + hci_req_sync_lock(hdev); ret = hci_suspend_sync(hdev); hci_req_sync_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5f8c144c84b88..13c745876b390 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1399,6 +1399,10 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } + /* Cancel potentially blocking sync operation before power off */ + if (cp->val == 0x00) + __hci_cmd_sync_cancel(hdev, -EHOSTDOWN); + err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd, mgmt_set_powered_complete); From b0310d6ed684b862dd29049d7b9cca5589555654 Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Wed, 19 Apr 2023 20:54:13 +0530 Subject: [PATCH 70/72] Bluetooth: btnxpuart: Enable flow control before checking boot signature This enables flow control before checking for bootloader signature and deciding whether FW download is needed or not. In case of V1 bootloader chips w8987 and w8997, it is observed that if WLAN FW is downloaded first and power save is enabled in wlan core, bootloader signatures are not emitted by the BT core when the chip is put to sleep. As a result, the driver skips FW download and subsequent HCI commands get timeout errors in dmesg as shown below: [ 112.898867] Bluetooth: hci0: Opcode 0x c03 failed: -110 [ 114.914865] Bluetooth: hci0: Setting baudrate failed (-110) [ 116.930856] Bluetooth: hci0: Setting wake-up method failed (-110) By enabling the flow control, the host enables its RTS pin, and an interrupt in chip's UART peripheral causes the bootloader to wake up, enabling the bootloader signatures, which then helps in downloading the bluetooth FW file. This changes all instances of 0/1 for serdev_device_set_flow_control() to false/true. Signed-off-by: Neeraj Sanjay Kale Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 31046f26e7124..3a34d7c1475bc 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -535,7 +535,7 @@ static int nxp_download_firmware(struct hci_dev *hdev) nxpdev->helper_downloaded = false; serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE); - serdev_device_set_flow_control(nxpdev->serdev, 0); + serdev_device_set_flow_control(nxpdev->serdev, false); nxpdev->current_baudrate = HCI_NXP_PRI_BAUDRATE; /* Wait till FW is downloaded and CTS becomes low */ @@ -548,7 +548,7 @@ static int nxp_download_firmware(struct hci_dev *hdev) return -ETIMEDOUT; } - serdev_device_set_flow_control(nxpdev->serdev, 1); + serdev_device_set_flow_control(nxpdev->serdev, true); err = serdev_device_wait_for_cts(nxpdev->serdev, 1, 60000); if (err < 0) { bt_dev_err(hdev, "CTS is still high. FW Download failed."); @@ -740,7 +740,7 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) if (nxpdev->baudrate_changed) { serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_SEC_BAUDRATE); - serdev_device_set_flow_control(nxpdev->serdev, 1); + serdev_device_set_flow_control(nxpdev->serdev, true); nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; } goto free_skb; @@ -763,7 +763,7 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) serdev_device_wait_until_sent(nxpdev->serdev, 0); serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_SEC_BAUDRATE); - serdev_device_set_flow_control(nxpdev->serdev, 1); + serdev_device_set_flow_control(nxpdev->serdev, true); } else { clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); @@ -880,7 +880,7 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) if (nxpdev->baudrate_changed) { serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_SEC_BAUDRATE); - serdev_device_set_flow_control(nxpdev->serdev, 1); + serdev_device_set_flow_control(nxpdev->serdev, true); nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; } goto free_skb; @@ -984,7 +984,7 @@ static int nxp_set_ind_reset(struct hci_dev *hdev, void *data) static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev) { serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE); - serdev_device_set_flow_control(nxpdev->serdev, 0); + serdev_device_set_flow_control(nxpdev->serdev, true); set_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state); return wait_event_interruptible_timeout(nxpdev->check_boot_sign_wait_q, @@ -1012,7 +1012,6 @@ static int nxp_setup(struct hci_dev *hdev) clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); } - serdev_device_set_flow_control(nxpdev->serdev, 1); device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate", &nxpdev->fw_init_baudrate); if (!nxpdev->fw_init_baudrate) From 20981ce2d5a5f79d17da1ace3a93df7b3c6ba3d6 Mon Sep 17 00:00:00 2001 From: Tim Jiang Date: Wed, 19 Apr 2023 11:38:05 +0800 Subject: [PATCH 71/72] Bluetooth: btusb: Add WCN6855 devcoredump support WCN6855 will report memdump via ACL data or HCI event when it get crashed, so we collect memdump to debug firmware. Signed-off-by: Tim Jiang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 222 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3aa189b1986d4..2a8e2bb038f58 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -733,6 +733,16 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = { {} }; +struct qca_dump_info { + /* fields for dump collection */ + u16 id_vendor; + u16 id_product; + u32 fw_version; + u32 controller_id; + u32 ram_dump_size; + u16 ram_dump_seqno; +}; + #define BTUSB_MAX_ISOC_FRAMES 10 #define BTUSB_INTR_RUNNING 0 @@ -752,6 +762,7 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = { #define BTUSB_WAKEUP_AUTOSUSPEND 14 #define BTUSB_USE_ALT3_FOR_WBS 15 #define BTUSB_ALT6_CONTINUOUS_TX 16 +#define BTUSB_HW_SSR_ACTIVE 17 struct btusb_data { struct hci_dev *hdev; @@ -814,6 +825,8 @@ struct btusb_data { int oob_wake_irq; /* irq for out-of-band wake-on-bt */ unsigned cmd_timeout_cnt; + + struct qca_dump_info qca_dump; }; static void btusb_reset(struct hci_dev *hdev) @@ -904,6 +917,11 @@ static void btusb_qca_cmd_timeout(struct hci_dev *hdev) struct btusb_data *data = hci_get_drvdata(hdev); struct gpio_desc *reset_gpio = data->reset_gpio; + if (test_bit(BTUSB_HW_SSR_ACTIVE, &data->flags)) { + bt_dev_info(hdev, "Ramdump in progress, defer cmd_timeout"); + return; + } + if (++data->cmd_timeout_cnt < 5) return; @@ -3294,6 +3312,202 @@ static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev, return 0; } +#define QCA_MEMDUMP_ACL_HANDLE 0x2EDD +#define QCA_MEMDUMP_SIZE_MAX 0x100000 +#define QCA_MEMDUMP_VSE_CLASS 0x01 +#define QCA_MEMDUMP_MSG_TYPE 0x08 +#define QCA_MEMDUMP_PKT_SIZE 248 +#define QCA_LAST_SEQUENCE_NUM 0xffff + +struct qca_dump_hdr { + u8 vse_class; + u8 msg_type; + __le16 seqno; + u8 reserved; + union { + u8 data[0]; + struct { + __le32 ram_dump_size; + u8 data0[0]; + } __packed; + }; +} __packed; + + +static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ + char buf[128]; + struct btusb_data *btdata = hci_get_drvdata(hdev); + + snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n", + btdata->qca_dump.controller_id); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n", + btdata->qca_dump.fw_version); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Driver: %s\nVendor: qca\n", + btusb_driver.name); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "VID: 0x%x\nPID:0x%x\n", + btdata->qca_dump.id_vendor, btdata->qca_dump.id_product); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Lmp Subversion: 0x%x\n", + hdev->lmp_subver); + skb_put_data(skb, buf, strlen(buf)); +} + +static void btusb_coredump_qca(struct hci_dev *hdev) +{ + static const u8 param[] = { 0x26 }; + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc0c, 1, param, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) + bt_dev_err(hdev, "%s: triggle crash failed (%ld)", __func__, PTR_ERR(skb)); + kfree_skb(skb); +} + +/* + * ==0: not a dump pkt. + * < 0: fails to handle a dump pkt + * > 0: otherwise. + */ +static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ + int ret = 1; + u8 pkt_type; + u8 *sk_ptr; + unsigned int sk_len; + u16 seqno; + u32 dump_size; + + struct hci_event_hdr *event_hdr; + struct hci_acl_hdr *acl_hdr; + struct qca_dump_hdr *dump_hdr; + struct btusb_data *btdata = hci_get_drvdata(hdev); + struct usb_device *udev = btdata->udev; + + pkt_type = hci_skb_pkt_type(skb); + sk_ptr = skb->data; + sk_len = skb->len; + + if (pkt_type == HCI_ACLDATA_PKT) { + acl_hdr = hci_acl_hdr(skb); + if (le16_to_cpu(acl_hdr->handle) != QCA_MEMDUMP_ACL_HANDLE) + return 0; + sk_ptr += HCI_ACL_HDR_SIZE; + sk_len -= HCI_ACL_HDR_SIZE; + event_hdr = (struct hci_event_hdr *)sk_ptr; + } else { + event_hdr = hci_event_hdr(skb); + } + + if ((event_hdr->evt != HCI_VENDOR_PKT) + || (event_hdr->plen != (sk_len - HCI_EVENT_HDR_SIZE))) + return 0; + + sk_ptr += HCI_EVENT_HDR_SIZE; + sk_len -= HCI_EVENT_HDR_SIZE; + + dump_hdr = (struct qca_dump_hdr *)sk_ptr; + if ((sk_len < offsetof(struct qca_dump_hdr, data)) + || (dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) + || (dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE)) + return 0; + + /*it is dump pkt now*/ + seqno = le16_to_cpu(dump_hdr->seqno); + if (seqno == 0) { + set_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags); + dump_size = le32_to_cpu(dump_hdr->ram_dump_size); + if (!dump_size || (dump_size > QCA_MEMDUMP_SIZE_MAX)) { + ret = -EILSEQ; + bt_dev_err(hdev, "Invalid memdump size(%u)", + dump_size); + goto out; + } + + ret = hci_devcd_init(hdev, dump_size); + if (ret < 0) { + bt_dev_err(hdev, "memdump init error(%d)", ret); + goto out; + } + + btdata->qca_dump.ram_dump_size = dump_size; + btdata->qca_dump.ram_dump_seqno = 0; + sk_ptr += offsetof(struct qca_dump_hdr, data0); + sk_len -= offsetof(struct qca_dump_hdr, data0); + + usb_disable_autosuspend(udev); + bt_dev_info(hdev, "%s memdump size(%u)\n", + (pkt_type == HCI_ACLDATA_PKT) ? "ACL" : "event", + dump_size); + } else { + sk_ptr += offsetof(struct qca_dump_hdr, data); + sk_len -= offsetof(struct qca_dump_hdr, data); + } + + if (!btdata->qca_dump.ram_dump_size) { + ret = -EINVAL; + bt_dev_err(hdev, "memdump is not active"); + goto out; + } + + if ((seqno > btdata->qca_dump.ram_dump_seqno + 1) && (seqno != QCA_LAST_SEQUENCE_NUM)) { + dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - btdata->qca_dump.ram_dump_seqno - 1); + hci_devcd_append_pattern(hdev, 0x0, dump_size); + bt_dev_err(hdev, + "expected memdump seqno(%u) is not received(%u)\n", + btdata->qca_dump.ram_dump_seqno, seqno); + btdata->qca_dump.ram_dump_seqno = seqno; + kfree_skb(skb); + return ret; + } + + skb_pull(skb, skb->len - sk_len); + hci_devcd_append(hdev, skb); + btdata->qca_dump.ram_dump_seqno++; + if (seqno == QCA_LAST_SEQUENCE_NUM) { + bt_dev_info(hdev, + "memdump done: pkts(%u), total(%u)\n", + btdata->qca_dump.ram_dump_seqno, btdata->qca_dump.ram_dump_size); + + hci_devcd_complete(hdev); + goto out; + } + return ret; + +out: + if (btdata->qca_dump.ram_dump_size) + usb_enable_autosuspend(udev); + btdata->qca_dump.ram_dump_size = 0; + btdata->qca_dump.ram_dump_seqno = 0; + clear_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags); + + if (ret < 0) + kfree_skb(skb); + return ret; +} + +static int btusb_recv_acl_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ + if (handle_dump_pkt_qca(hdev, skb)) + return 0; + return hci_recv_frame(hdev, skb); +} + +static int btusb_recv_evt_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ + if (handle_dump_pkt_qca(hdev, skb)) + return 0; + return hci_recv_frame(hdev, skb); +} + + #define QCA_DFU_PACKET_LEN 4096 #define QCA_GET_TARGET_VERSION 0x09 @@ -3628,6 +3842,9 @@ static int btusb_setup_qca(struct hci_dev *hdev) if (err < 0) return err; + btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); + btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version); + if (!(status & QCA_SYSCFG_UPDATED)) { err = btusb_setup_qca_load_nvm(hdev, &ver, info); if (err < 0) @@ -4117,6 +4334,11 @@ static int btusb_probe(struct usb_interface *intf, } if (id->driver_info & BTUSB_QCA_WCN6855) { + data->qca_dump.id_vendor = id->idVendor; + data->qca_dump.id_product = id->idProduct; + data->recv_event = btusb_recv_evt_qca; + data->recv_acl = btusb_recv_acl_qca; + hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL); data->setup_on_usb = btusb_setup_qca; hdev->shutdown = btusb_shutdown_qca; hdev->set_bdaddr = btusb_set_bdaddr_wcn6855; From d883a4669a1def6d121ccf5e64ad28260d1c9531 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 21 Apr 2023 11:37:55 -0700 Subject: [PATCH 72/72] Bluetooth: hci_sync: Only allow hci_cmd_sync_queue if running This makes sure hci_cmd_sync_queue only queue new work if HCI_RUNNING has been set otherwise there is a risk of commands being sent while turning off. Because hci_cmd_sync_queue can no longer queue work while HCI_RUNNING is not set it cannot be used to power on adapters so instead hci_cmd_sync_submit is introduced which bypass the HCI_RUNNING check, so it behaves like the old implementation. Link: https://lore.kernel.org/all/CAB4PzUpDMvdc8j2MdeSAy1KkAE-D3woprCwAdYWeOc-3v3c9Sw@mail.gmail.com/ Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_sync.h | 2 ++ net/bluetooth/hci_sync.c | 25 +++++++++++++++++++++++-- net/bluetooth/mgmt.c | 12 ++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h index f61b249787fca..2495be4d8b828 100644 --- a/include/net/bluetooth/hci_sync.h +++ b/include/net/bluetooth/hci_sync.h @@ -41,6 +41,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev); void hci_cmd_sync_cancel(struct hci_dev *hdev, int err); void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err); +int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, + void *data, hci_cmd_sync_work_destroy_t destroy); int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, void *data, hci_cmd_sync_work_destroy_t destroy); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 771aaa8089672..647a8ce540623 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -684,8 +684,12 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err) } EXPORT_SYMBOL(hci_cmd_sync_cancel); -int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, - void *data, hci_cmd_sync_work_destroy_t destroy) +/* Submit HCI command to be run in as cmd_sync_work: + * + * - hdev must _not_ be unregistered + */ +int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, + void *data, hci_cmd_sync_work_destroy_t destroy) { struct hci_cmd_sync_work_entry *entry; @@ -708,6 +712,23 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, return 0; } +EXPORT_SYMBOL(hci_cmd_sync_submit); + +/* Queue HCI command: + * + * - hdev must be running + */ +int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, + void *data, hci_cmd_sync_work_destroy_t destroy) +{ + /* Only queue command if hdev is running which means it had been opened + * and is either on init phase or is already up. + */ + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -ENETDOWN; + + return hci_cmd_sync_submit(hdev, func, data, destroy); +} EXPORT_SYMBOL(hci_cmd_sync_queue); int hci_update_eir_sync(struct hci_dev *hdev) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 13c745876b390..f7b2d0971f240 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1400,11 +1400,15 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, } /* Cancel potentially blocking sync operation before power off */ - if (cp->val == 0x00) + if (cp->val == 0x00) { __hci_cmd_sync_cancel(hdev, -EHOSTDOWN); - - err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd, - mgmt_set_powered_complete); + err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd, + mgmt_set_powered_complete); + } else { + /* Use hci_cmd_sync_submit since hdev might not be running */ + err = hci_cmd_sync_submit(hdev, set_powered_sync, cmd, + mgmt_set_powered_complete); + } if (err < 0) mgmt_pending_remove(cmd);