mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-10 15:58:47 +00:00
bluetooth-next pull request for net-next:
- Introduce devcoredump support - Add support for Realtek RTL8821CS, RTL8851B, RTL8852BS - Add support for Mediatek MT7663, MT7922 - Add support for NXP w8997 - Add support for Actions Semi ATS2851 - Add support for QTI WCN6855 - Add support for Marvell 88W8997 -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmRGEjgZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKaMPEACVgwFPOwbSUiv+tZrLKjo8 VzsuRYDXba9xV88L1gMWMer25wrVKS0+LfGbR0qXdU3zMWhmGInPVWGeGgpuJtjp bu6jJeQyhM//BXcVNZ90FrPBG8+IyCLdPRwh/+c3qiXFvLSnDq4fhcJLtFxY2X8f EhEIUEY+WFjvSqjGirOucsixwm4v9nZg98hD4hFv80iFM6eWiCh12zOj8qPPcBGA SHgMrrk/7NQC5gJv+VCvZXx+I49kq1YpVcTXBAK7zpcdtnbHzpkWJGuSAr6JHqx8 zblr/1kcGGN5tEuhMyiM81DuVTaYfeOy1M1GUgb3q/lvSQWuYiRNQhe5OqZ11ady txefiQlfmCsLGoQ6DtSh4rr9ygvjzrZ5VKquWmy037TZ7+a5fF9iStsngMZhnX+W tcvbW0T/D02aR1uLVJaXy6tD/K6wqsm/AIJ5jYY1jaC9iQgJLAHI1qOV2b96Q28v eppr9fYwX56JVsVZHnbFlk1Gf5zZffZn5RCq6iagZKFo3zdehgLEiyD4FQFK94qn b3+r8E9h6r9FPJ7rip5Vaxe7lyr9WAf3HcQMjfd41zqgB4RSE2z4jVRPh98RKzDa hFH0KhVZZVqPSu3CPZ7JWXBGWYD7/AiFLD6UrUy+9bbe+6nmYKYu6NSw4LGe6962 1Uuab5QdUzSjD8db+945Xg== =DOQV -----END PGP SIGNATURE----- Merge tag 'for-net-next-2023-04-23' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next bluetooth-next pull request for net-next: - Introduce devcoredump support - Add support for Realtek RTL8821CS, RTL8851B, RTL8852BS - Add support for Mediatek MT7663, MT7922 - Add support for NXP w8997 - Add support for Actions Semi ATS2851 - Add support for QTI WCN6855 - Add support for Marvell 88W8997
This commit is contained in:
commit
2efb07b5ce
@ -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 <neeraj.sanjaykale@nxp.com>
|
||||
|
||||
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>;
|
||||
};
|
||||
};
|
@ -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:
|
||||
- |
|
||||
|
@ -15,11 +15,29 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mrvl,88w8897
|
||||
enum:
|
||||
- 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:
|
||||
|
@ -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 <anarsoul@gmail.com>
|
||||
- Alistair Francis <alistair@alistair23.me>
|
||||
|
||||
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
|
||||
|
@ -23237,6 +23237,13 @@ L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
F: mm/zswap.c
|
||||
|
||||
NXP BLUETOOTH WIRELESS DRIVERS
|
||||
M: Amitkumar Karwar <amitkumar.karwar@nxp.com>
|
||||
M: Neeraj Kale <neeraj.sanjaykale@nxp.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/bluetooth/nxp,88w8987-bt.yaml
|
||||
F: drivers/bluetooth/btnxpuart.c
|
||||
|
||||
THE REST
|
||||
M: Linus Torvalds <torvalds@linux-foundation.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
|
@ -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>;
|
||||
|
@ -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.
|
||||
@ -465,4 +466,17 @@ 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
|
||||
select CRC8
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/dmi.h>
|
||||
@ -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);
|
||||
|
@ -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:
|
||||
@ -2684,15 +2751,15 @@ 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,
|
||||
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)",
|
||||
@ -2742,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;
|
||||
@ -2751,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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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" },
|
||||
{ }
|
||||
|
@ -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,
|
||||
};
|
||||
|
1352
drivers/bluetooth/btnxpuart.c
Normal file
1352
drivers/bluetooth/btnxpuart.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -17,19 +17,26 @@
|
||||
|
||||
#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_EPATCH_SIGNATURE_V2 "RTBTCore"
|
||||
#define RTL_ROM_LMP_8703B 0x8703
|
||||
#define RTL_ROM_LMP_8723A 0x1200
|
||||
#define RTL_ROM_LMP_8723B 0x8723
|
||||
#define RTL_ROM_LMP_8821A 0x8821
|
||||
#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)
|
||||
#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, \
|
||||
@ -38,6 +45,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,
|
||||
@ -51,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 {
|
||||
@ -59,6 +75,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;
|
||||
@ -75,6 +92,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[] = {
|
||||
@ -99,6 +118,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,
|
||||
@ -128,6 +180,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,
|
||||
@ -190,6 +250,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,
|
||||
@ -205,10 +273,19 @@ 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,
|
||||
u8 hci_ver, u8 hci_bus)
|
||||
u8 hci_ver, u8 hci_bus,
|
||||
u8 chip_type)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -225,6 +302,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;
|
||||
}
|
||||
@ -284,6 +364,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)
|
||||
@ -307,6 +608,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 */
|
||||
@ -315,9 +617,21 @@ 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 */
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@ -382,12 +696,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);
|
||||
@ -451,6 +767,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;
|
||||
|
||||
@ -461,17 +778,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 */
|
||||
@ -587,10 +903,60 @@ out:
|
||||
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)
|
||||
{
|
||||
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);
|
||||
@ -603,10 +969,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;
|
||||
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) {
|
||||
@ -614,6 +981,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);
|
||||
@ -621,19 +1013,32 @@ 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);
|
||||
|
||||
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
|
||||
hdev->bus);
|
||||
kfree_skb(skb);
|
||||
|
||||
if (!btrtl_dev->ic_info)
|
||||
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, chip_type);
|
||||
|
||||
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);
|
||||
@ -642,41 +1047,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);
|
||||
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",
|
||||
@ -755,6 +1144,8 @@ 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:
|
||||
case RTL_ROM_LMP_8851B:
|
||||
return btrtl_setup_rtl8723b(hdev, btrtl_dev);
|
||||
default:
|
||||
rtl_dev_info(hdev, "assuming no firmware upload needed");
|
||||
@ -779,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);
|
||||
|
||||
@ -795,6 +1187,22 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
|
||||
rtl_dev_dbg(hdev, "WBS supported not enabled.");
|
||||
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,
|
||||
* 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);
|
||||
|
||||
@ -953,6 +1361,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");
|
||||
@ -963,7 +1377,11 @@ 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");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8851bu_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8851bu_config.bin");
|
||||
|
@ -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];
|
||||
@ -44,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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -540,6 +540,10 @@ 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 },
|
||||
{ USB_DEVICE(0x13d3, 0x3571), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek Bluetooth devices */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
|
||||
@ -558,6 +562,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 |
|
||||
@ -612,6 +619,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 },
|
||||
@ -723,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
|
||||
@ -742,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;
|
||||
@ -804,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)
|
||||
@ -894,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;
|
||||
|
||||
@ -2376,16 +2404,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
|
||||
@ -2404,6 +2463,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);
|
||||
@ -3244,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
|
||||
@ -3578,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)
|
||||
@ -3831,13 +4098,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;
|
||||
|
||||
@ -4012,7 +4275,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;
|
||||
|
||||
@ -4071,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;
|
||||
@ -4102,6 +4370,9 @@ 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_SET_RPA_TIMEOUT, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks);
|
||||
}
|
||||
|
||||
if (!reset)
|
||||
@ -4389,6 +4660,17 @@ done:
|
||||
}
|
||||
#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,
|
||||
@ -4400,6 +4682,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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -888,7 +890,7 @@ unlock:
|
||||
#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),
|
||||
@ -897,13 +899,32 @@ static struct gpiod_lookup_table asus_tf103c_irq_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 = {
|
||||
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",
|
||||
@ -1300,6 +1321,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 +1441,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 +1462,7 @@ static const struct acpi_device_id bcm_acpi_match[] = {
|
||||
{ "BCM2E86" },
|
||||
{ "BCM2E87" },
|
||||
{ "BCM2E88" },
|
||||
{ "BCM2E89" },
|
||||
{ "BCM2E89", (long)&bcm43430_device_data },
|
||||
{ "BCM2E8A" },
|
||||
{ "BCM2E8B" },
|
||||
{ "BCM2E8C" },
|
||||
@ -1444,29 +1471,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 +1563,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);
|
||||
|
@ -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;
|
||||
}
|
||||
@ -936,6 +938,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 +1104,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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 const struct hci_uart_proto mrvl_proto = {
|
||||
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_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)
|
||||
@ -414,13 +485,12 @@ 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[] = {
|
||||
{ .compatible = "mrvl,88w8897" },
|
||||
static const struct of_device_id __maybe_unused mrvl_bluetooth_of_match[] = {
|
||||
{ .compatible = "mrvl,88w8897", .data = &mrvl_proto_8897},
|
||||
{ .compatible = "mrvl,88w8997", .data = &mrvl_proto_8997},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mrvl_bluetooth_of_match);
|
||||
#endif
|
||||
|
||||
static struct serdev_device_driver mrvl_serdev_driver = {
|
||||
.probe = mrvl_serdev_probe,
|
||||
@ -435,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);
|
||||
}
|
||||
|
@ -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 @@ retry:
|
||||
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 @@ retry:
|
||||
}
|
||||
|
||||
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)
|
||||
@ -1827,7 +1835,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 +1846,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 +1858,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 +1869,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 },
|
||||
@ -1883,6 +1891,20 @@ static const struct qca_device_data qca_soc_data_wcn6750 = {
|
||||
.capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
|
||||
};
|
||||
|
||||
static const struct qca_device_data qca_soc_data_wcn6855 __maybe_unused = {
|
||||
.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);
|
||||
|
@ -278,6 +278,104 @@ 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;
|
||||
size_t data_size;
|
||||
int 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;
|
||||
|
||||
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, data_size);
|
||||
|
||||
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 +453,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);
|
||||
|
@ -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,12 +399,23 @@ 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);
|
||||
}
|
||||
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);
|
||||
|
@ -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,11 +242,22 @@ 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);
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -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 *);
|
||||
@ -250,11 +252,15 @@ 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_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)
|
||||
|
@ -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 <maxk@qualcomm.com>
|
||||
|
||||
@ -171,25 +172,41 @@ 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
|
||||
#define BT_ISO_PHY_2M 0x02
|
||||
#define BT_ISO_PHY_CODED 0x04
|
||||
|
116
include/net/bluetooth/coredump.h
Normal file
116
include/net/bluetooth/coredump.h
Normal file
@ -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 */
|
@ -294,6 +294,21 @@ 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,
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
|
@ -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 <maxk@qualcomm.com>
|
||||
|
||||
@ -32,6 +33,7 @@
|
||||
#include <net/bluetooth/hci.h>
|
||||
#include <net/bluetooth/hci_sync.h>
|
||||
#include <net/bluetooth/hci_sock.h>
|
||||
#include <net/bluetooth/coredump.h>
|
||||
|
||||
/* HCI priority */
|
||||
#define HCI_PRIO_MAX 7
|
||||
@ -590,6 +592,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;
|
||||
@ -764,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);
|
||||
@ -774,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;
|
||||
@ -979,7 +993,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++;
|
||||
@ -1091,7 +1105,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;
|
||||
}
|
||||
@ -1166,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;
|
||||
@ -1177,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;
|
||||
@ -1200,7 +1224,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;
|
||||
}
|
||||
@ -1223,7 +1247,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;
|
||||
}
|
||||
@ -1332,7 +1356,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);
|
||||
@ -1377,12 +1401,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)
|
||||
@ -1497,6 +1523,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);
|
||||
@ -1668,9 +1703,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))
|
||||
|
||||
|
@ -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);
|
||||
|
||||
@ -122,6 +124,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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
536
net/bluetooth/coredump.c
Normal file
536
net/bluetooth/coredump.c
Normal file
@ -0,0 +1,536 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2023 Google Corporation
|
||||
*/
|
||||
|
||||
#include <linux/devcoredump.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
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 = 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)) {
|
||||
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;
|
||||
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);
|
||||
|
||||
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);
|
@ -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 <maxk@qualcomm.com>
|
||||
|
||||
@ -329,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",
|
||||
@ -460,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;
|
||||
@ -530,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;
|
||||
@ -636,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -795,8 +800,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 +921,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 +964,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 +973,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,
|
||||
@ -1041,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);
|
||||
@ -1068,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)
|
||||
{
|
||||
if (!conn->link)
|
||||
return false;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
conn->link->link = NULL;
|
||||
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;
|
||||
|
||||
hci_conn_put(conn->parent);
|
||||
conn->parent = 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)
|
||||
@ -1090,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) {
|
||||
@ -1112,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);
|
||||
@ -1411,7 +1430,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 +1445,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 +1456,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 +1474,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 +1503,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 +1512,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);
|
||||
|
||||
@ -1599,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);
|
||||
@ -1619,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;
|
||||
@ -1648,13 +1698,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 +1717,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,18 +1737,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;
|
||||
memset(&cp.bis.bcode, 0, sizeof(cp.bis.bcode));
|
||||
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));
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp);
|
||||
}
|
||||
@ -1711,7 +1761,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 +1781,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 +1807,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 +1817,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,
|
||||
@ -1791,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)
|
||||
@ -1809,32 +1860,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 +1905,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 +1916,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 */
|
||||
@ -1881,76 +1932,39 @@ 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->link->handle);
|
||||
cmd.cis[0].cis_handle = cpu_to_le16(conn->handle);
|
||||
cmd.cp.num_cis++;
|
||||
cig = conn->iso_qos.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.cig != cig)
|
||||
continue;
|
||||
|
||||
/* Check if all CIS(s) belonging to a CIG are ready */
|
||||
if (!conn->link || conn->link->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->link->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)
|
||||
{
|
||||
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;
|
||||
@ -2002,8 +2016,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 +2030,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 +2076,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 +2089,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 +2118,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 +2173,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);
|
||||
|
||||
@ -2163,6 +2185,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,
|
||||
@ -2177,9 +2200,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);
|
||||
@ -2188,16 +2211,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;
|
||||
}
|
||||
|
@ -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);
|
||||
@ -2802,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);
|
||||
|
@ -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);
|
||||
|
@ -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 <maxk@qualcomm.com>
|
||||
|
||||
@ -886,8 +887,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);
|
||||
@ -2339,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);
|
||||
@ -2359,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2631,11 +2639,34 @@ static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_setup_sync_conn_status(struct hci_dev *hdev, __u16 handle,
|
||||
__u8 status)
|
||||
{
|
||||
struct hci_conn *acl;
|
||||
struct hci_link *link;
|
||||
|
||||
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) {
|
||||
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(link->conn, status);
|
||||
hci_conn_del(link->conn);
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __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);
|
||||
|
||||
@ -2646,31 +2677,12 @@ static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
handle = __le16_to_cpu(cp->handle);
|
||||
|
||||
bt_dev_dbg(hdev, "handle 0x%4.4x", handle);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
acl = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (acl) {
|
||||
sco = acl->link;
|
||||
if (sco) {
|
||||
sco->state = BT_CLOSED;
|
||||
|
||||
hci_connect_cfm(sco, status);
|
||||
hci_conn_del(sco);
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_setup_sync_conn_status(hdev, __le16_to_cpu(cp->handle), status);
|
||||
}
|
||||
|
||||
static void hci_cs_enhanced_setup_sync_conn(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
struct hci_cp_enhanced_setup_sync_conn *cp;
|
||||
struct hci_conn *acl, *sco;
|
||||
__u16 handle;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
|
||||
@ -2681,24 +2693,7 @@ static void hci_cs_enhanced_setup_sync_conn(struct hci_dev *hdev, __u8 status)
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
handle = __le16_to_cpu(cp->handle);
|
||||
|
||||
bt_dev_dbg(hdev, "handle 0x%4.4x", handle);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
acl = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (acl) {
|
||||
sco = acl->link;
|
||||
if (sco) {
|
||||
sco->state = BT_CLOSED;
|
||||
|
||||
hci_connect_cfm(sco, status);
|
||||
hci_conn_del(sco);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@ -3828,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.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();
|
||||
}
|
||||
|
||||
@ -3885,7 +3881,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) */
|
||||
@ -5025,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;
|
||||
@ -6813,15 +6809,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) {
|
||||
@ -6895,8 +6891,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);
|
||||
@ -6983,13 +6979,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);
|
||||
}
|
||||
|
@ -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) {
|
||||
@ -1003,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 */
|
||||
|
@ -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)
|
||||
@ -2403,7 +2424,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;
|
||||
@ -4093,7 +4114,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,
|
||||
@ -4414,18 +4436,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);
|
||||
@ -4533,6 +4575,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.")
|
||||
};
|
||||
|
||||
@ -4727,6 +4772,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);
|
||||
|
||||
@ -5108,10 +5155,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,
|
||||
@ -5853,7 +5902,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;
|
||||
@ -6114,6 +6162,71 @@ done:
|
||||
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;
|
||||
|
@ -3,6 +3,7 @@
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2022 Intel Corporation
|
||||
* Copyright 2023 NXP
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -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)
|
||||
@ -1584,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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
@ -4694,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;
|
||||
}
|
||||
|
||||
@ -7874,7 +7872,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;
|
||||
|
||||
|
@ -1399,8 +1399,16 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd,
|
||||
mgmt_set_powered_complete);
|
||||
/* 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);
|
||||
} 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);
|
||||
@ -8393,10 +8401,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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
return err;
|
||||
msft_le_set_advertisement_filter_enable_cb(hdev, &cp, err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool msft_curve_validity(struct hci_dev *hdev)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user