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:
David S. Miller 2023-04-24 11:07:20 +01:00
commit 2efb07b5ce
50 changed files with 4125 additions and 519 deletions

View File

@ -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>;
};
};

View File

@ -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:
- |

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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>;

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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" },
{ }

View File

@ -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,
};

File diff suppressed because it is too large Load Diff

View File

@ -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");

View File

@ -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;

View File

@ -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");

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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");
}
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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)

View File

@ -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

View 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 */

View File

@ -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 */

View File

@ -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))

View File

@ -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);

View File

@ -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;

View File

@ -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 {

View File

@ -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
View 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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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 */

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
}
}