mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-09 14:50:19 +00:00
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says: ==================== pull request: bluetooth-next 2020-12-07 Here's the main bluetooth-next pull request for the 5.11 kernel. - Updated Bluetooth entries in MAINTAINERS to include Luiz von Dentz - Added support for Realtek 8822CE and 8852A devices - Added support for MediaTek MT7615E device - Improved workarounds for fake CSR devices - Fix Bluetooth qualification test case L2CAP/COS/CFD/BV-14-C - Fixes for LL Privacy support - Enforce 16 byte encryption key size for FIPS security level - Added new mgmt commands for extended advertising support - Multiple other smaller fixes & improvements Please let me know if there are any issues pulling. Thanks. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
e1be4b5990
@ -3194,8 +3194,9 @@ F: drivers/mtd/devices/block2mtd.c
|
||||
BLUETOOTH DRIVERS
|
||||
M: Marcel Holtmann <marcel@holtmann.org>
|
||||
M: Johan Hedberg <johan.hedberg@gmail.com>
|
||||
M: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
|
||||
L: linux-bluetooth@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Supported
|
||||
W: http://www.bluez.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
|
||||
@ -3204,8 +3205,9 @@ F: drivers/bluetooth/
|
||||
BLUETOOTH SUBSYSTEM
|
||||
M: Marcel Holtmann <marcel@holtmann.org>
|
||||
M: Johan Hedberg <johan.hedberg@gmail.com>
|
||||
M: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
|
||||
L: linux-bluetooth@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Supported
|
||||
W: http://www.bluez.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
|
||||
|
@ -437,31 +437,38 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver
|
||||
tlv = (struct intel_tlv *)skb->data;
|
||||
switch (tlv->type) {
|
||||
case INTEL_TLV_CNVI_TOP:
|
||||
version->cnvi_top = get_unaligned_le32(tlv->val);
|
||||
version->cnvi_top =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
break;
|
||||
case INTEL_TLV_CNVR_TOP:
|
||||
version->cnvr_top = get_unaligned_le32(tlv->val);
|
||||
version->cnvr_top =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
break;
|
||||
case INTEL_TLV_CNVI_BT:
|
||||
version->cnvi_bt = get_unaligned_le32(tlv->val);
|
||||
version->cnvi_bt =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
break;
|
||||
case INTEL_TLV_CNVR_BT:
|
||||
version->cnvr_bt = get_unaligned_le32(tlv->val);
|
||||
version->cnvr_bt =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
break;
|
||||
case INTEL_TLV_DEV_REV_ID:
|
||||
version->dev_rev_id = get_unaligned_le16(tlv->val);
|
||||
version->dev_rev_id =
|
||||
__le16_to_cpu(get_unaligned_le16(tlv->val));
|
||||
break;
|
||||
case INTEL_TLV_IMAGE_TYPE:
|
||||
version->img_type = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_TIME_STAMP:
|
||||
version->timestamp = get_unaligned_le16(tlv->val);
|
||||
version->timestamp =
|
||||
__le16_to_cpu(get_unaligned_le16(tlv->val));
|
||||
break;
|
||||
case INTEL_TLV_BUILD_TYPE:
|
||||
version->build_type = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_BUILD_NUM:
|
||||
version->build_num = get_unaligned_le32(tlv->val);
|
||||
version->build_num =
|
||||
__le32_to_cpu(get_unaligned_le32(tlv->val));
|
||||
break;
|
||||
case INTEL_TLV_SECURE_BOOT:
|
||||
version->secure_boot = tlv->val[0];
|
||||
|
@ -132,6 +132,12 @@ struct intel_debug_features {
|
||||
__u8 page1[16];
|
||||
} __packed;
|
||||
|
||||
#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)
|
||||
#define INTEL_CNVX_TOP_STEP(cnvx_top) (((cnvx_top) & 0x0f000000) >> 24)
|
||||
#define INTEL_CNVX_TOP_PACK_SWAB(t, s) __swab16(((__u16)(((t) << 4) | (s))))
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_INTEL)
|
||||
|
||||
int btintel_check_bdaddr(struct hci_dev *hdev);
|
||||
|
@ -704,7 +704,7 @@ static int mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
|
||||
err = mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to power on data RAM (%d)", err);
|
||||
return err;
|
||||
goto free_fw;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
|
@ -14,12 +14,11 @@
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
|
||||
enum qca_btsoc_type soc_type)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct edl_event_hdr *edl;
|
||||
struct qca_btsoc_version *ver;
|
||||
char cmd;
|
||||
int err = 0;
|
||||
u8 event_type = HCI_EV_VENDOR;
|
||||
@ -70,9 +69,9 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
}
|
||||
|
||||
if (soc_type >= QCA_WCN3991)
|
||||
memmove(&edl->data, &edl->data[1], sizeof(*ver));
|
||||
|
||||
ver = (struct qca_btsoc_version *)(edl->data);
|
||||
memcpy(ver, edl->data + 1, sizeof(*ver));
|
||||
else
|
||||
memcpy(ver, &edl->data, sizeof(*ver));
|
||||
|
||||
bt_dev_info(hdev, "QCA Product ID :0x%08x",
|
||||
le32_to_cpu(ver->product_id));
|
||||
@ -83,13 +82,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
bt_dev_info(hdev, "QCA Patch Version:0x%08x",
|
||||
le16_to_cpu(ver->patch_ver));
|
||||
|
||||
/* QCA chipset version can be decided by patch and SoC
|
||||
* version, combination with upper 2 bytes from SoC
|
||||
* and lower 2 bytes from patch will be used.
|
||||
*/
|
||||
*soc_version = (le32_to_cpu(ver->soc_id) << 16) |
|
||||
(le16_to_cpu(ver->rom_ver) & 0x0000ffff);
|
||||
if (*soc_version == 0)
|
||||
if (ver->soc_id == 0 || ver->rom_ver == 0)
|
||||
err = -EILSEQ;
|
||||
|
||||
out:
|
||||
@ -446,15 +439,20 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
|
||||
|
||||
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver,
|
||||
enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
|
||||
const char *firmware_name)
|
||||
{
|
||||
struct qca_fw_config config;
|
||||
int err;
|
||||
u8 rom_ver = 0;
|
||||
u32 soc_ver;
|
||||
|
||||
bt_dev_dbg(hdev, "QCA setup on UART");
|
||||
|
||||
soc_ver = get_soc_ver(ver.soc_id, ver.rom_ver);
|
||||
|
||||
bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver);
|
||||
|
||||
config.user_baud_rate = baudrate;
|
||||
|
||||
/* Download rampatch file */
|
||||
@ -491,9 +489,15 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
if (firmware_name)
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/%s", firmware_name);
|
||||
else if (qca_is_wcn399x(soc_type))
|
||||
else if (qca_is_wcn399x(soc_type)) {
|
||||
if (ver.soc_id == QCA_WCN3991_SOC_ID) {
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/crnv%02xu.bin", rom_ver);
|
||||
} else {
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/crnv%02x.bin", rom_ver);
|
||||
}
|
||||
}
|
||||
else if (soc_type == QCA_QCA6390)
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/htnv%02x.bin", rom_ver);
|
||||
|
@ -34,6 +34,18 @@
|
||||
#define QCA_HCI_CC_OPCODE 0xFC00
|
||||
#define QCA_HCI_CC_SUCCESS 0x00
|
||||
|
||||
#define QCA_WCN3991_SOC_ID (0x40014320)
|
||||
|
||||
/* QCA chipset version can be decided by patch and SoC
|
||||
* version, combination with upper 2 bytes from SoC
|
||||
* and lower 2 bytes from patch will be used.
|
||||
*/
|
||||
#define get_soc_ver(soc_id, rom_ver) \
|
||||
((le32_to_cpu(soc_id) << 16) | (le16_to_cpu(rom_ver)))
|
||||
|
||||
#define QCA_FW_BUILD_VER_LEN 255
|
||||
|
||||
|
||||
enum qca_baudrate {
|
||||
QCA_BAUDRATE_115200 = 0,
|
||||
QCA_BAUDRATE_57600,
|
||||
@ -136,9 +148,9 @@ enum qca_btsoc_type {
|
||||
|
||||
int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver,
|
||||
enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
|
||||
const char *firmware_name);
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
|
||||
enum qca_btsoc_type);
|
||||
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
int qca_send_pre_shutdown_cmd(struct hci_dev *hdev);
|
||||
@ -155,13 +167,15 @@ static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdad
|
||||
}
|
||||
|
||||
static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver,
|
||||
enum qca_btsoc_type soc_type,
|
||||
struct qca_btsoc_version ver,
|
||||
const char *firmware_name)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
static inline int qca_read_soc_version(struct hci_dev *hdev,
|
||||
struct qca_btsoc_version *ver,
|
||||
enum qca_btsoc_type)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -18,23 +18,25 @@
|
||||
#define VERSION "0.1"
|
||||
|
||||
#define RTL_EPATCH_SIGNATURE "Realtech"
|
||||
#define RTL_ROM_LMP_3499 0x3499
|
||||
#define RTL_ROM_LMP_8723A 0x1200
|
||||
#define RTL_ROM_LMP_8723B 0x8723
|
||||
#define RTL_ROM_LMP_8723D 0x8873
|
||||
#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_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_INFO(lmps, hcir) \
|
||||
.match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV, \
|
||||
#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, \
|
||||
.lmp_subver = (lmps), \
|
||||
.hci_rev = (hcir)
|
||||
.hci_rev = (hcir), \
|
||||
.hci_ver = (hciv), \
|
||||
.hci_bus = (bus)
|
||||
|
||||
struct id_table {
|
||||
__u16 match_flags;
|
||||
@ -55,119 +57,100 @@ struct btrtl_device_info {
|
||||
int fw_len;
|
||||
u8 *cfg_data;
|
||||
int cfg_len;
|
||||
bool drop_fw;
|
||||
};
|
||||
|
||||
static const struct id_table ic_id_table[] = {
|
||||
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8723A, 0x0,
|
||||
.config_needed = false,
|
||||
.has_rom_version = false,
|
||||
.fw_name = "rtl_bt/rtl8723a_fw.bin",
|
||||
.cfg_name = NULL },
|
||||
|
||||
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_3499, 0x0,
|
||||
/* 8723A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = false,
|
||||
.fw_name = "rtl_bt/rtl8723a_fw.bin",
|
||||
.cfg_name = NULL },
|
||||
|
||||
/* 8723BS */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
|
||||
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS,
|
||||
.lmp_subver = RTL_ROM_LMP_8723B,
|
||||
.hci_rev = 0xb,
|
||||
.hci_ver = 6,
|
||||
.hci_bus = HCI_UART,
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723bs_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723bs_config" },
|
||||
|
||||
/* 8723B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb),
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723b_config" },
|
||||
|
||||
/* 8723D */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd),
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723d_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723d_config" },
|
||||
|
||||
/* 8723DS */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
|
||||
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS,
|
||||
.lmp_subver = RTL_ROM_LMP_8723B,
|
||||
.hci_rev = 0xd,
|
||||
.hci_ver = 8,
|
||||
.hci_bus = HCI_UART,
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723ds_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723ds_config" },
|
||||
|
||||
/* 8723DU */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723D, 0x826C),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723d_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723d_config" },
|
||||
|
||||
/* 8821A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa),
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8821a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821a_config" },
|
||||
|
||||
/* 8821C */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc),
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8821c_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821c_config" },
|
||||
|
||||
/* 8761A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xa),
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8761a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761a_config" },
|
||||
|
||||
/* 8761B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb),
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8761b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761b_config" },
|
||||
|
||||
/* 8822C with UART interface */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
|
||||
IC_MATCH_FL_HCIBUS,
|
||||
.lmp_subver = RTL_ROM_LMP_8822B,
|
||||
.hci_rev = 0x000c,
|
||||
.hci_ver = 0x0a,
|
||||
.hci_bus = HCI_UART,
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8822cs_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822cs_config" },
|
||||
|
||||
/* 8822C with USB interface */
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc),
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8822cu_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822cu_config" },
|
||||
|
||||
/* 8822B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb),
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8822b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822b_config" },
|
||||
|
||||
/* 8852A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8852au_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8852au_config" },
|
||||
};
|
||||
|
||||
static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
|
||||
@ -275,6 +258,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
||||
{ RTL_ROM_LMP_8821A, 10 }, /* 8821C */
|
||||
{ RTL_ROM_LMP_8822B, 13 }, /* 8822C */
|
||||
{ RTL_ROM_LMP_8761A, 14 }, /* 8761B */
|
||||
{ RTL_ROM_LMP_8852A, 18 }, /* 8852A */
|
||||
};
|
||||
|
||||
min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
|
||||
@ -563,6 +547,8 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
|
||||
u16 hci_rev, lmp_subver;
|
||||
u8 hci_ver;
|
||||
int ret;
|
||||
u16 opcode;
|
||||
u8 cmd[2];
|
||||
|
||||
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
|
||||
if (!btrtl_dev) {
|
||||
@ -584,6 +570,49 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
|
||||
hci_ver = resp->hci_ver;
|
||||
hci_rev = le16_to_cpu(resp->hci_rev);
|
||||
lmp_subver = le16_to_cpu(resp->lmp_subver);
|
||||
|
||||
if (resp->hci_ver == 0x8 && le16_to_cpu(resp->hci_rev) == 0x826c &&
|
||||
resp->lmp_ver == 0x8 && le16_to_cpu(resp->lmp_subver) == 0xa99e)
|
||||
btrtl_dev->drop_fw = true;
|
||||
|
||||
if (btrtl_dev->drop_fw) {
|
||||
opcode = hci_opcode_pack(0x3f, 0x66);
|
||||
cmd[0] = opcode & 0xff;
|
||||
cmd[1] = opcode >> 8;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
|
||||
if (!skb)
|
||||
goto out_free;
|
||||
|
||||
skb_put_data(skb, cmd, sizeof(cmd));
|
||||
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
|
||||
|
||||
hdev->send(hdev, skb);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
out_free:
|
||||
kfree_skb(skb);
|
||||
|
||||
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
|
||||
@ -654,12 +683,12 @@ int btrtl_download_firmware(struct hci_dev *hdev,
|
||||
|
||||
switch (btrtl_dev->ic_info->lmp_subver) {
|
||||
case RTL_ROM_LMP_8723A:
|
||||
case RTL_ROM_LMP_3499:
|
||||
return btrtl_setup_rtl8723a(hdev, btrtl_dev);
|
||||
case RTL_ROM_LMP_8723B:
|
||||
case RTL_ROM_LMP_8821A:
|
||||
case RTL_ROM_LMP_8761A:
|
||||
case RTL_ROM_LMP_8822B:
|
||||
case RTL_ROM_LMP_8852A:
|
||||
return btrtl_setup_rtl8723b(hdev, btrtl_dev);
|
||||
default:
|
||||
rtl_dev_info(hdev, "assuming no firmware upload needed");
|
||||
@ -835,3 +864,5 @@ MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
|
||||
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");
|
||||
|
@ -60,6 +60,7 @@ static struct usb_driver btusb_driver;
|
||||
#define BTUSB_WIDEBAND_SPEECH 0x400000
|
||||
#define BTUSB_VALID_LE_STATES 0x800000
|
||||
#define BTUSB_QCA_WCN6855 0x1000000
|
||||
#define BTUSB_INTEL_NEWGEN 0x2000000
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
@ -365,7 +366,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x8087, 0x0029), .driver_info = BTUSB_INTEL_NEW |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_NEW |
|
||||
{ USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_NEWGEN |
|
||||
BTUSB_WIDEBAND_SPEECH},
|
||||
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
|
||||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
|
||||
@ -386,6 +387,10 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0bda, 0xb00c), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek 8852AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0bda, 0xc852), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek Bluetooth devices */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
|
||||
.driver_info = BTUSB_REALTEK },
|
||||
@ -394,6 +399,9 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0e8d, 0xe0, 0x01, 0x01),
|
||||
.driver_info = BTUSB_MEDIATEK },
|
||||
|
||||
/* Additional MediaTek MT7615E Bluetooth devices */
|
||||
{ USB_DEVICE(0x13d3, 0x3560), .driver_info = BTUSB_MEDIATEK},
|
||||
|
||||
/* Additional Realtek 8723AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
|
||||
@ -425,8 +433,26 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8822CE Bluetooth devices */
|
||||
{ USB_DEVICE(0x04ca, 0x4005), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3548), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x04ca, 0x4005), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04c5, 0x161f), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0b05, 0x18ef), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3548), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3549), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3553), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3555), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x2ff8, 0x3051), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x1358, 0xc123), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0bda, 0xc123), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Silicon Wave based devices */
|
||||
{ USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE },
|
||||
@ -1763,9 +1789,12 @@ static int btusb_setup_bcm92035(struct hci_dev *hdev)
|
||||
|
||||
static int btusb_setup_csr(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
u16 bcdDevice = le16_to_cpu(data->udev->descriptor.bcdDevice);
|
||||
struct hci_rp_read_local_version *rp;
|
||||
struct sk_buff *skb;
|
||||
bool is_fake = false;
|
||||
int ret;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
@ -1832,6 +1861,12 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
||||
le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_4_0)
|
||||
is_fake = true;
|
||||
|
||||
/* Other clones which beat all the above checks */
|
||||
else if (bcdDevice == 0x0134 &&
|
||||
le16_to_cpu(rp->lmp_subver) == 0x0c5c &&
|
||||
le16_to_cpu(rp->hci_ver) == BLUETOOTH_VER_2_0)
|
||||
is_fake = true;
|
||||
|
||||
if (is_fake) {
|
||||
bt_dev_warn(hdev, "CSR: Unbranded CSR clone detected; adding workarounds...");
|
||||
|
||||
@ -1848,6 +1883,43 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
||||
*/
|
||||
clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
clear_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||
|
||||
/*
|
||||
* Special workaround for clones with a Barrot 8041a02 chip,
|
||||
* these clones are really messed-up:
|
||||
* 1. Their bulk rx endpoint will never report any data unless
|
||||
* the device was suspended at least once (yes really).
|
||||
* 2. They will not wakeup when autosuspended and receiving data
|
||||
* on their bulk rx endpoint from e.g. a keyboard or mouse
|
||||
* (IOW remote-wakeup support is broken for the bulk endpoint).
|
||||
*
|
||||
* To fix 1. enable runtime-suspend, force-suspend the
|
||||
* hci and then wake-it up by disabling runtime-suspend.
|
||||
*
|
||||
* To fix 2. clear the hci's can_wake flag, this way the hci
|
||||
* will still be autosuspended when it is not open.
|
||||
*/
|
||||
if (bcdDevice == 0x8891 &&
|
||||
le16_to_cpu(rp->lmp_subver) == 0x1012 &&
|
||||
le16_to_cpu(rp->hci_rev) == 0x0810 &&
|
||||
le16_to_cpu(rp->hci_ver) == BLUETOOTH_VER_4_0) {
|
||||
bt_dev_warn(hdev, "CSR: detected a fake CSR dongle using a Barrot 8041a02 chip, this chip is very buggy and may have issues\n");
|
||||
|
||||
pm_runtime_allow(&data->udev->dev);
|
||||
|
||||
ret = pm_runtime_suspend(&data->udev->dev);
|
||||
if (ret >= 0)
|
||||
msleep(200);
|
||||
else
|
||||
bt_dev_err(hdev, "Failed to suspend the device for Barrot 8041a02 receive-issue workaround\n");
|
||||
|
||||
pm_runtime_forbid(&data->udev->dev);
|
||||
|
||||
device_set_wakeup_capable(&data->udev->dev, false);
|
||||
/* Re-enable autosuspend if this was requested */
|
||||
if (enable_autosuspend)
|
||||
usb_enable_autosuspend(data->udev);
|
||||
}
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
@ -2359,6 +2431,182 @@ static bool btusb_setup_intel_new_get_fw_name(struct intel_version *ver,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void btusb_setup_intel_newgen_get_fw_name(const struct intel_version_tlv *ver_tlv,
|
||||
char *fw_name, size_t len,
|
||||
const char *suffix)
|
||||
{
|
||||
/* The firmware file name for new generation controllers will be
|
||||
* ibt-<cnvi_top type+cnvi_top step>-<cnvr_top type+cnvr_top step>
|
||||
*/
|
||||
snprintf(fw_name, len, "intel/ibt-%04x-%04x.%s",
|
||||
INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver_tlv->cnvi_top),
|
||||
INTEL_CNVX_TOP_STEP(ver_tlv->cnvi_top)),
|
||||
INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver_tlv->cnvr_top),
|
||||
INTEL_CNVX_TOP_STEP(ver_tlv->cnvr_top)),
|
||||
suffix);
|
||||
}
|
||||
|
||||
static int btusb_intel_download_firmware_newgen(struct hci_dev *hdev,
|
||||
struct intel_version_tlv *ver,
|
||||
u32 *boot_param)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
char fwname[64];
|
||||
int err;
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
|
||||
if (!ver || !boot_param)
|
||||
return -EINVAL;
|
||||
|
||||
/* The hardware platform number has a fixed value of 0x37 and
|
||||
* for now only accept this single value.
|
||||
*/
|
||||
if (INTEL_HW_PLATFORM(ver->cnvi_bt) != 0x37) {
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)",
|
||||
INTEL_HW_PLATFORM(ver->cnvi_bt));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The firmware variant determines if the device is in bootloader
|
||||
* mode or is running operational firmware. The value 0x03 identifies
|
||||
* the bootloader and the value 0x23 identifies the operational
|
||||
* firmware.
|
||||
*
|
||||
* When the operational firmware is already present, then only
|
||||
* the check for valid Bluetooth device address is needed. This
|
||||
* determines if the device will be added as configured or
|
||||
* unconfigured controller.
|
||||
*
|
||||
* It is not possible to use the Secure Boot Parameters in this
|
||||
* case since that command is only available in bootloader mode.
|
||||
*/
|
||||
if (ver->img_type == 0x03) {
|
||||
clear_bit(BTUSB_BOOTLOADER, &data->flags);
|
||||
btintel_check_bdaddr(hdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for supported iBT hardware variants of this firmware
|
||||
* loading method.
|
||||
*
|
||||
* This check has been put in place to ensure correct forward
|
||||
* compatibility options when newer hardware variants come along.
|
||||
*/
|
||||
switch (INTEL_HW_VARIANT(ver->cnvi_bt)) {
|
||||
case 0x17: /* TyP */
|
||||
case 0x18: /* Slr */
|
||||
case 0x19: /* Slr-F */
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)",
|
||||
INTEL_HW_VARIANT(ver->cnvi_bt));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If the device is not in bootloader mode, then the only possible
|
||||
* choice is to return an error and abort the device initialization.
|
||||
*/
|
||||
if (ver->img_type != 0x01) {
|
||||
bt_dev_err(hdev, "Unsupported Intel firmware variant (0x%x)",
|
||||
ver->img_type);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* It is required that every single firmware fragment is acknowledged
|
||||
* with a command complete event. If the boot parameters indicate
|
||||
* that this bootloader does not send them, then abort the setup.
|
||||
*/
|
||||
if (ver->limited_cce != 0x00) {
|
||||
bt_dev_err(hdev, "Unsupported Intel firmware loading method (0x%x)",
|
||||
ver->limited_cce);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Secure boot engine type should be either 1 (ECDSA) or 0 (RSA) */
|
||||
if (ver->sbe_type > 0x01) {
|
||||
bt_dev_err(hdev, "Unsupported Intel secure boot engine type (0x%x)",
|
||||
ver->sbe_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If the OTP has no valid Bluetooth device address, then there will
|
||||
* also be no valid address for the operational firmware.
|
||||
*/
|
||||
if (!bacmp(&ver->otp_bd_addr, BDADDR_ANY)) {
|
||||
bt_dev_info(hdev, "No device address configured");
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
}
|
||||
|
||||
btusb_setup_intel_newgen_get_fw_name(ver, fwname, sizeof(fwname), "sfi");
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to load Intel firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "Found device firmware: %s", fwname);
|
||||
|
||||
if (fw->size < 644) {
|
||||
bt_dev_err(hdev, "Invalid size of firmware file (%zu)",
|
||||
fw->size);
|
||||
err = -EBADF;
|
||||
goto done;
|
||||
}
|
||||
|
||||
set_bit(BTUSB_DOWNLOADING, &data->flags);
|
||||
|
||||
/* Start firmware downloading and get boot parameter */
|
||||
err = btintel_download_firmware_newgen(hdev, fw, boot_param,
|
||||
INTEL_HW_VARIANT(ver->cnvi_bt),
|
||||
ver->sbe_type);
|
||||
if (err < 0) {
|
||||
/* When FW download fails, send Intel Reset to retry
|
||||
* FW download.
|
||||
*/
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
goto done;
|
||||
}
|
||||
set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
|
||||
|
||||
bt_dev_info(hdev, "Waiting for firmware download to complete");
|
||||
|
||||
/* Before switching the device into operational mode and with that
|
||||
* booting the loaded firmware, wait for the bootloader notification
|
||||
* that all fragments have been successfully received.
|
||||
*
|
||||
* When the event processing receives the notification, then the
|
||||
* BTUSB_DOWNLOADING flag will be cleared.
|
||||
*
|
||||
* The firmware loading should not take longer than 5 seconds
|
||||
* and thus just timeout if that happens and fail the setup
|
||||
* of this device.
|
||||
*/
|
||||
err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(5000));
|
||||
if (err == -EINTR) {
|
||||
bt_dev_err(hdev, "Firmware loading interrupted");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Firmware loading timeout");
|
||||
err = -ETIMEDOUT;
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (test_bit(BTUSB_FIRMWARE_FAILED, &data->flags)) {
|
||||
bt_dev_err(hdev, "Firmware loading failed");
|
||||
err = -ENOEXEC;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
release_firmware(fw);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_intel_download_firmware(struct hci_dev *hdev,
|
||||
struct intel_version *ver,
|
||||
struct intel_boot_params *params,
|
||||
@ -2693,6 +2941,134 @@ finish:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_setup_intel_newgen(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
u32 boot_param;
|
||||
char ddcname[64];
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
int err;
|
||||
struct intel_debug_features features;
|
||||
struct intel_version_tlv version;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
/* Set the default boot parameter to 0x0 and it is updated to
|
||||
* SKU specific boot parameter after reading Intel_Write_Boot_Params
|
||||
* command while downloading the firmware.
|
||||
*/
|
||||
boot_param = 0x00000000;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
/* Read the Intel version information to determine if the device
|
||||
* is in bootloader mode or if it already has operational firmware
|
||||
* loaded.
|
||||
*/
|
||||
err = btintel_read_version_tlv(hdev, &version);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Intel Read version failed (%d)", err);
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
btintel_version_info_tlv(hdev, &version);
|
||||
|
||||
err = btusb_intel_download_firmware_newgen(hdev, &version, &boot_param);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* check if controller is already having an operational firmware */
|
||||
if (version.img_type == 0x03)
|
||||
goto finish;
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long)ktime_to_ns(delta) >> 10;
|
||||
|
||||
bt_dev_info(hdev, "Firmware loaded in %llu usecs", duration);
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
set_bit(BTUSB_BOOTING, &data->flags);
|
||||
|
||||
err = btintel_send_intel_reset(hdev, boot_param);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Intel Soft Reset failed (%d)", err);
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* The bootloader will not indicate when the device is ready. This
|
||||
* is done by the operational firmware sending bootup notification.
|
||||
*
|
||||
* Booting into operational firmware should not take longer than
|
||||
* 1 second. However if that happens, then just fail the setup
|
||||
* since something went wrong.
|
||||
*/
|
||||
bt_dev_info(hdev, "Waiting for device to boot");
|
||||
|
||||
err = wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
if (err == -EINTR) {
|
||||
bt_dev_err(hdev, "Device boot interrupted");
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Device boot timeout");
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long)ktime_to_ns(delta) >> 10;
|
||||
|
||||
bt_dev_info(hdev, "Device booted in %llu usecs", duration);
|
||||
|
||||
clear_bit(BTUSB_BOOTLOADER, &data->flags);
|
||||
|
||||
btusb_setup_intel_newgen_get_fw_name(&version, ddcname, sizeof(ddcname),
|
||||
"ddc");
|
||||
/* Once the device is running in operational mode, it needs to
|
||||
* apply the device configuration (DDC) parameters.
|
||||
*
|
||||
* The device can work without DDC parameters, so even if it
|
||||
* fails to load the file, no need to fail the setup.
|
||||
*/
|
||||
btintel_load_ddc_config(hdev, ddcname);
|
||||
|
||||
/* Read the Intel supported features and if new exception formats
|
||||
* supported, need to load the additional DDC config to enable.
|
||||
*/
|
||||
btintel_read_debug_features(hdev, &features);
|
||||
|
||||
/* Set DDC mask for available debug features */
|
||||
btintel_set_debug_features(hdev, &features);
|
||||
|
||||
/* Read the Intel version information after loading the FW */
|
||||
err = btintel_read_version_tlv(hdev, &version);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
btintel_version_info_tlv(hdev, &version);
|
||||
|
||||
finish:
|
||||
/* Set the event mask for Intel specific vendor events. This enables
|
||||
* a few extra events that are useful during general operation. It
|
||||
* does not enable any debugging related events.
|
||||
*
|
||||
* The device will function correctly without these events enabled
|
||||
* and thus no need to fail the setup.
|
||||
*/
|
||||
btintel_set_event_mask(hdev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int btusb_shutdown_intel(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
@ -3067,7 +3443,7 @@ static int btusb_mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to power on data RAM (%d)", err);
|
||||
return err;
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
@ -3444,12 +3820,14 @@ static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,
|
||||
#define QCA_SYSCFG_UPDATED 0x40
|
||||
#define QCA_PATCH_UPDATED 0x80
|
||||
#define QCA_DFU_TIMEOUT 3000
|
||||
#define QCA_FLAG_MULTI_NVM 0x80
|
||||
|
||||
struct qca_version {
|
||||
__le32 rom_version;
|
||||
__le32 patch_version;
|
||||
__le32 ram_version;
|
||||
__le32 ref_clock;
|
||||
__le16 board_id;
|
||||
__le16 flag;
|
||||
__u8 reserved[4];
|
||||
} __packed;
|
||||
|
||||
@ -3632,8 +4010,14 @@ static int btusb_setup_qca_load_nvm(struct hci_dev *hdev,
|
||||
char fwname[64];
|
||||
int err;
|
||||
|
||||
if (((ver->flag >> 8) & 0xff) == QCA_FLAG_MULTI_NVM) {
|
||||
snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x_%04x.bin",
|
||||
le32_to_cpu(ver->rom_version),
|
||||
le16_to_cpu(ver->board_id));
|
||||
} else {
|
||||
snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x.bin",
|
||||
le32_to_cpu(ver->rom_version));
|
||||
}
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err) {
|
||||
@ -3700,6 +4084,11 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver,
|
||||
sizeof(ver));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!(status & QCA_SYSCFG_UPDATED)) {
|
||||
err = btusb_setup_qca_load_nvm(hdev, &ver, info);
|
||||
if (err < 0)
|
||||
@ -4078,6 +4467,24 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_INTEL_NEWGEN) {
|
||||
hdev->manufacturer = 2;
|
||||
hdev->send = btusb_send_frame_intel;
|
||||
hdev->setup = btusb_setup_intel_newgen;
|
||||
hdev->shutdown = btusb_shutdown_intel_new;
|
||||
hdev->hw_error = btintel_hw_error;
|
||||
hdev->set_diag = btintel_set_diag;
|
||||
hdev->set_bdaddr = btintel_set_bdaddr;
|
||||
hdev->cmd_timeout = btusb_intel_cmd_timeout;
|
||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
|
||||
|
||||
data->recv_event = btusb_recv_event_intel;
|
||||
data->recv_bulk = btusb_recv_bulk_intel;
|
||||
set_bit(BTUSB_BOOTLOADER, &data->flags);
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_MARVELL)
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_marvell;
|
||||
|
||||
|
@ -245,6 +245,9 @@ static int h5_close(struct hci_uart *hu)
|
||||
skb_queue_purge(&h5->rel);
|
||||
skb_queue_purge(&h5->unrel);
|
||||
|
||||
kfree_skb(h5->rx_skb);
|
||||
h5->rx_skb = NULL;
|
||||
|
||||
if (h5->vnd && h5->vnd->close)
|
||||
h5->vnd->close(h5);
|
||||
|
||||
@ -1001,6 +1004,7 @@ static struct h5_vnd rtl_vnd = {
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id h5_acpi_match[] = {
|
||||
#ifdef CONFIG_BT_HCIUART_RTL
|
||||
{ "OBDA0623", (kernel_ulong_t)&rtl_vnd },
|
||||
{ "OBDA8723", (kernel_ulong_t)&rtl_vnd },
|
||||
#endif
|
||||
{ },
|
||||
|
@ -626,6 +626,7 @@ static int ll_setup(struct hci_uart *hu)
|
||||
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
|
||||
msleep(5);
|
||||
gpiod_set_value_cansleep(lldev->enable_gpio, 1);
|
||||
mdelay(100);
|
||||
err = serdev_device_wait_for_cts(serdev, true, 200);
|
||||
if (err) {
|
||||
bt_dev_err(hu->hdev, "Failed to get CTS");
|
||||
|
@ -50,6 +50,8 @@
|
||||
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
|
||||
#define CMD_TRANS_TIMEOUT_MS 100
|
||||
#define MEMDUMP_TIMEOUT_MS 8000
|
||||
#define IBS_DISABLE_SSR_TIMEOUT_MS (MEMDUMP_TIMEOUT_MS + 1000)
|
||||
#define FW_DOWNLOAD_TIMEOUT_MS 3000
|
||||
|
||||
/* susclk rate */
|
||||
#define SUSCLK_RATE_32KHZ 32768
|
||||
@ -68,16 +70,18 @@
|
||||
#define QCA_MEMDUMP_BYTE 0xFB
|
||||
|
||||
enum qca_flags {
|
||||
QCA_IBS_ENABLED,
|
||||
QCA_IBS_DISABLED,
|
||||
QCA_DROP_VENDOR_EVENT,
|
||||
QCA_SUSPENDING,
|
||||
QCA_MEMDUMP_COLLECTION,
|
||||
QCA_HW_ERROR_EVENT,
|
||||
QCA_SSR_TRIGGERED
|
||||
QCA_SSR_TRIGGERED,
|
||||
QCA_BT_OFF
|
||||
};
|
||||
|
||||
enum qca_capabilities {
|
||||
QCA_CAP_WIDEBAND_SPEECH = BIT(0),
|
||||
QCA_CAP_VALID_LE_STATES = BIT(1),
|
||||
};
|
||||
|
||||
/* HCI_IBS transmit side sleep protocol states */
|
||||
@ -630,7 +634,7 @@ static void qca_debugfs_init(struct hci_dev *hdev)
|
||||
ibs_dir = debugfs_create_dir("ibs", hdev->debugfs);
|
||||
|
||||
/* read only */
|
||||
mode = S_IRUGO;
|
||||
mode = 0444;
|
||||
debugfs_create_u8("tx_ibs_state", mode, ibs_dir, &qca->tx_ibs_state);
|
||||
debugfs_create_u8("rx_ibs_state", mode, ibs_dir, &qca->rx_ibs_state);
|
||||
debugfs_create_u64("ibs_sent_sleeps", mode, ibs_dir,
|
||||
@ -657,7 +661,7 @@ static void qca_debugfs_init(struct hci_dev *hdev)
|
||||
debugfs_create_u32("vote_off_ms", mode, ibs_dir, &qca->vote_off_ms);
|
||||
|
||||
/* read/write */
|
||||
mode = S_IRUGO | S_IWUSR;
|
||||
mode = 0644;
|
||||
debugfs_create_u32("wake_retrans", mode, ibs_dir, &qca->wake_retrans);
|
||||
debugfs_create_u32("tx_idle_delay", mode, ibs_dir,
|
||||
&qca->tx_idle_delay);
|
||||
@ -869,7 +873,7 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
* Out-Of-Band(GPIOs control) sleep is selected.
|
||||
* Don't wake the device up when suspending.
|
||||
*/
|
||||
if (!test_bit(QCA_IBS_ENABLED, &qca->flags) ||
|
||||
if (test_bit(QCA_IBS_DISABLED, &qca->flags) ||
|
||||
test_bit(QCA_SUSPENDING, &qca->flags)) {
|
||||
skb_queue_tail(&qca->txq, skb);
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
@ -1014,7 +1018,7 @@ static void qca_controller_memdump(struct work_struct *work)
|
||||
* the controller to send the dump is 8 seconds. let us
|
||||
* start timer to handle this asynchronous activity.
|
||||
*/
|
||||
clear_bit(QCA_IBS_ENABLED, &qca->flags);
|
||||
set_bit(QCA_IBS_DISABLED, &qca->flags);
|
||||
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
|
||||
dump = (void *) skb->data;
|
||||
dump_size = __le32_to_cpu(dump->dump_size);
|
||||
@ -1301,7 +1305,7 @@ 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)))
|
||||
msleep(10);
|
||||
usleep_range(1000, 10000);
|
||||
else
|
||||
msleep(300);
|
||||
|
||||
@ -1349,7 +1353,7 @@ static int qca_send_power_pulse(struct hci_uart *hu, bool on)
|
||||
if (on)
|
||||
msleep(100);
|
||||
else
|
||||
msleep(10);
|
||||
usleep_range(1000, 10000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1618,6 +1622,7 @@ static int qca_power_on(struct hci_dev *hdev)
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
enum qca_btsoc_type soc_type = qca_soc_type(hu);
|
||||
struct qca_serdev *qcadev;
|
||||
struct qca_data *qca = hu->priv;
|
||||
int ret = 0;
|
||||
|
||||
/* Non-serdev device usually is powered by external power
|
||||
@ -1637,6 +1642,7 @@ static int qca_power_on(struct hci_dev *hdev)
|
||||
}
|
||||
}
|
||||
|
||||
clear_bit(QCA_BT_OFF, &qca->flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1649,14 +1655,14 @@ static int qca_setup(struct hci_uart *hu)
|
||||
enum qca_btsoc_type soc_type = qca_soc_type(hu);
|
||||
const char *firmware_name = qca_get_firmware_name(hu);
|
||||
int ret;
|
||||
int soc_ver = 0;
|
||||
struct qca_btsoc_version ver;
|
||||
|
||||
ret = qca_check_speeds(hu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Patch downloading has to be done without IBS mode */
|
||||
clear_bit(QCA_IBS_ENABLED, &qca->flags);
|
||||
set_bit(QCA_IBS_DISABLED, &qca->flags);
|
||||
|
||||
/* Enable controller to do both LE scan and BR/EDR inquiry
|
||||
* simultaneously.
|
||||
@ -1671,16 +1677,16 @@ static int qca_setup(struct hci_uart *hu)
|
||||
retry:
|
||||
ret = qca_power_on(hdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
clear_bit(QCA_SSR_TRIGGERED, &qca->flags);
|
||||
|
||||
if (qca_is_wcn399x(soc_type)) {
|
||||
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
|
||||
|
||||
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
|
||||
ret = qca_read_soc_version(hdev, &ver, soc_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
} else {
|
||||
qca_set_speed(hu, QCA_INIT_SPEED);
|
||||
}
|
||||
@ -1690,24 +1696,23 @@ retry:
|
||||
if (speed) {
|
||||
ret = qca_set_speed(hu, QCA_OPER_SPEED);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
qca_baudrate = qca_get_baudrate_value(speed);
|
||||
}
|
||||
|
||||
if (!qca_is_wcn399x(soc_type)) {
|
||||
/* Get QCA version information */
|
||||
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
|
||||
ret = qca_read_soc_version(hdev, &ver, soc_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver);
|
||||
/* Setup patch / NVM configurations */
|
||||
ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver,
|
||||
ret = qca_uart_setup(hdev, qca_baudrate, soc_type, ver,
|
||||
firmware_name);
|
||||
if (!ret) {
|
||||
set_bit(QCA_IBS_ENABLED, &qca->flags);
|
||||
clear_bit(QCA_IBS_DISABLED, &qca->flags);
|
||||
qca_debugfs_init(hdev);
|
||||
hu->hdev->hw_error = qca_hw_error;
|
||||
hu->hdev->cmd_timeout = qca_cmd_timeout;
|
||||
@ -1720,8 +1725,11 @@ retry:
|
||||
* patch/nvm-config is found, so run with original fw/config.
|
||||
*/
|
||||
ret = 0;
|
||||
} else {
|
||||
if (retries < MAX_INIT_RETRIES) {
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret && retries < MAX_INIT_RETRIES) {
|
||||
bt_dev_warn(hdev, "Retry BT power ON:%d", retries);
|
||||
qca_power_shutdown(hu);
|
||||
if (hu->serdev) {
|
||||
serdev_device_close(hu->serdev);
|
||||
@ -1734,7 +1742,6 @@ retry:
|
||||
retries++;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup bdaddr */
|
||||
if (soc_type == QCA_ROME)
|
||||
@ -1780,7 +1787,7 @@ static const struct qca_device_data qca_soc_data_wcn3991 = {
|
||||
{ "vddch0", 450000 },
|
||||
},
|
||||
.num_vregs = 4,
|
||||
.capabilities = QCA_CAP_WIDEBAND_SPEECH,
|
||||
.capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
|
||||
};
|
||||
|
||||
static const struct qca_device_data qca_soc_data_wcn3998 = {
|
||||
@ -1813,7 +1820,7 @@ static void qca_power_shutdown(struct hci_uart *hu)
|
||||
* data in skb's.
|
||||
*/
|
||||
spin_lock_irqsave(&qca->hci_ibs_lock, flags);
|
||||
clear_bit(QCA_IBS_ENABLED, &qca->flags);
|
||||
set_bit(QCA_IBS_DISABLED, &qca->flags);
|
||||
qca_flush(hu);
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
|
||||
@ -1830,6 +1837,8 @@ static void qca_power_shutdown(struct hci_uart *hu)
|
||||
} else if (qcadev->bt_en) {
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 0);
|
||||
}
|
||||
|
||||
set_bit(QCA_BT_OFF, &qca->flags);
|
||||
}
|
||||
|
||||
static int qca_power_off(struct hci_dev *hdev)
|
||||
@ -2017,11 +2026,17 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
hdev->shutdown = qca_power_off;
|
||||
}
|
||||
|
||||
/* Wideband speech support must be set per driver since it can't be
|
||||
* queried via hci.
|
||||
if (data) {
|
||||
/* Wideband speech support must be set per driver since it can't
|
||||
* be queried via hci. Same with the valid le states quirk.
|
||||
*/
|
||||
if (data && (data->capabilities & QCA_CAP_WIDEBAND_SPEECH))
|
||||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
|
||||
if (data->capabilities & QCA_CAP_WIDEBAND_SPEECH)
|
||||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
|
||||
&hdev->quirks);
|
||||
|
||||
if (data->capabilities & QCA_CAP_VALID_LE_STATES)
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2081,11 +2096,34 @@ static int __maybe_unused qca_suspend(struct device *dev)
|
||||
bool tx_pending = false;
|
||||
int ret = 0;
|
||||
u8 cmd;
|
||||
u32 wait_timeout = 0;
|
||||
|
||||
set_bit(QCA_SUSPENDING, &qca->flags);
|
||||
|
||||
/* Device is downloading patch or doesn't support in-band sleep. */
|
||||
if (!test_bit(QCA_IBS_ENABLED, &qca->flags))
|
||||
if (test_bit(QCA_BT_OFF, &qca->flags))
|
||||
return 0;
|
||||
|
||||
if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
|
||||
wait_timeout = test_bit(QCA_SSR_TRIGGERED, &qca->flags) ?
|
||||
IBS_DISABLE_SSR_TIMEOUT_MS :
|
||||
FW_DOWNLOAD_TIMEOUT_MS;
|
||||
|
||||
/* QCA_IBS_DISABLED flag is set to true, During FW download
|
||||
* and during memory dump collection. It is reset to false,
|
||||
* After FW download complete and after memory dump collections.
|
||||
*/
|
||||
wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED,
|
||||
TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout));
|
||||
|
||||
if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
|
||||
bt_dev_err(hu->hdev, "SSR or FW download time out");
|
||||
ret = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* After memory dump collection, Controller is powered off.*/
|
||||
if (test_bit(QCA_BT_OFF, &qca->flags))
|
||||
return 0;
|
||||
|
||||
cancel_work_sync(&qca->ws_awake_device);
|
||||
|
@ -1797,6 +1797,13 @@ struct hci_cp_le_set_adv_set_rand_addr {
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_TRANSMIT_POWER 0x204b
|
||||
struct hci_rp_le_read_transmit_power {
|
||||
__u8 status;
|
||||
__s8 min_le_tx_power;
|
||||
__s8 max_le_tx_power;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_BUFFER_SIZE_V2 0x2060
|
||||
struct hci_rp_le_read_buffer_size_v2 {
|
||||
__u8 status;
|
||||
|
@ -230,6 +230,8 @@ struct adv_info {
|
||||
__u16 scan_rsp_len;
|
||||
__u8 scan_rsp_data[HCI_MAX_AD_LENGTH];
|
||||
__s8 tx_power;
|
||||
__u32 min_interval;
|
||||
__u32 max_interval;
|
||||
bdaddr_t random_addr;
|
||||
bool rpa_expired;
|
||||
struct delayed_work rpa_expired_cb;
|
||||
@ -238,6 +240,8 @@ struct adv_info {
|
||||
#define HCI_MAX_ADV_INSTANCES 5
|
||||
#define HCI_DEFAULT_ADV_DURATION 2
|
||||
|
||||
#define HCI_ADV_TX_POWER_NO_PREFERENCE 0x7F
|
||||
|
||||
struct adv_pattern {
|
||||
struct list_head list;
|
||||
__u8 ad_type;
|
||||
@ -361,6 +365,9 @@ struct hci_dev {
|
||||
__u8 ssp_debug_mode;
|
||||
__u8 hw_error_code;
|
||||
__u32 clock;
|
||||
__u16 advmon_allowlist_duration;
|
||||
__u16 advmon_no_filter_duration;
|
||||
__u8 enable_advmon_interleave_scan;
|
||||
|
||||
__u16 devid_source;
|
||||
__u16 devid_vendor;
|
||||
@ -377,6 +384,8 @@ struct hci_dev {
|
||||
__u16 def_page_timeout;
|
||||
__u16 def_multi_adv_rotation_duration;
|
||||
__u16 def_le_autoconnect_timeout;
|
||||
__s8 min_le_tx_power;
|
||||
__s8 max_le_tx_power;
|
||||
|
||||
__u16 pkt_type;
|
||||
__u16 esco_type;
|
||||
@ -542,6 +551,14 @@ struct hci_dev {
|
||||
struct delayed_work rpa_expired;
|
||||
bdaddr_t rpa;
|
||||
|
||||
enum {
|
||||
INTERLEAVE_SCAN_NONE,
|
||||
INTERLEAVE_SCAN_NO_FILTER,
|
||||
INTERLEAVE_SCAN_ALLOWLIST
|
||||
} interleave_scan_state;
|
||||
|
||||
struct delayed_work interleave_scan;
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_LEDS)
|
||||
struct led_trigger *power_led;
|
||||
#endif
|
||||
@ -1290,7 +1307,11 @@ struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance);
|
||||
int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration);
|
||||
u16 timeout, u16 duration, s8 tx_power,
|
||||
u32 min_interval, u32 max_interval);
|
||||
int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data);
|
||||
int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance);
|
||||
void hci_adv_instances_set_rpa_expired(struct hci_dev *hdev, bool rpa_expired);
|
||||
|
||||
|
@ -574,6 +574,10 @@ struct mgmt_rp_add_advertising {
|
||||
#define MGMT_ADV_FLAG_SEC_CODED BIT(9)
|
||||
#define MGMT_ADV_FLAG_CAN_SET_TX_POWER BIT(10)
|
||||
#define MGMT_ADV_FLAG_HW_OFFLOAD BIT(11)
|
||||
#define MGMT_ADV_PARAM_DURATION BIT(12)
|
||||
#define MGMT_ADV_PARAM_TIMEOUT BIT(13)
|
||||
#define MGMT_ADV_PARAM_INTERVALS BIT(14)
|
||||
#define MGMT_ADV_PARAM_TX_POWER BIT(15)
|
||||
|
||||
#define MGMT_ADV_FLAG_SEC_MASK (MGMT_ADV_FLAG_SEC_1M | MGMT_ADV_FLAG_SEC_2M | \
|
||||
MGMT_ADV_FLAG_SEC_CODED)
|
||||
@ -621,7 +625,7 @@ struct mgmt_cp_set_appearance {
|
||||
#define MGMT_SET_APPEARANCE_SIZE 2
|
||||
|
||||
#define MGMT_OP_GET_PHY_CONFIGURATION 0x0044
|
||||
struct mgmt_rp_get_phy_confguration {
|
||||
struct mgmt_rp_get_phy_configuration {
|
||||
__le32 supported_phys;
|
||||
__le32 configurable_phys;
|
||||
__le32 selected_phys;
|
||||
@ -658,7 +662,7 @@ struct mgmt_rp_get_phy_confguration {
|
||||
MGMT_PHY_LE_CODED_RX)
|
||||
|
||||
#define MGMT_OP_SET_PHY_CONFIGURATION 0x0045
|
||||
struct mgmt_cp_set_phy_confguration {
|
||||
struct mgmt_cp_set_phy_configuration {
|
||||
__le32 selected_phys;
|
||||
} __packed;
|
||||
#define MGMT_SET_PHY_CONFIGURATION_SIZE 4
|
||||
@ -682,11 +686,16 @@ struct mgmt_cp_set_blocked_keys {
|
||||
|
||||
#define MGMT_OP_SET_WIDEBAND_SPEECH 0x0047
|
||||
|
||||
#define MGMT_OP_READ_SECURITY_INFO 0x0048
|
||||
#define MGMT_READ_SECURITY_INFO_SIZE 0
|
||||
struct mgmt_rp_read_security_info {
|
||||
__le16 sec_len;
|
||||
__u8 sec[];
|
||||
#define MGMT_CAP_SEC_FLAGS 0x01
|
||||
#define MGMT_CAP_MAX_ENC_KEY_SIZE 0x02
|
||||
#define MGMT_CAP_SMP_MAX_ENC_KEY_SIZE 0x03
|
||||
#define MGMT_CAP_LE_TX_PWR 0x04
|
||||
|
||||
#define MGMT_OP_READ_CONTROLLER_CAP 0x0048
|
||||
#define MGMT_READ_CONTROLLER_CAP_SIZE 0
|
||||
struct mgmt_rp_read_controller_cap {
|
||||
__le16 cap_len;
|
||||
__u8 cap[0];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_READ_EXP_FEATURES_INFO 0x0049
|
||||
@ -782,6 +791,36 @@ struct mgmt_rp_remove_adv_monitor {
|
||||
__le16 monitor_handle;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_ADD_EXT_ADV_PARAMS 0x0054
|
||||
struct mgmt_cp_add_ext_adv_params {
|
||||
__u8 instance;
|
||||
__le32 flags;
|
||||
__le16 duration;
|
||||
__le16 timeout;
|
||||
__le32 min_interval;
|
||||
__le32 max_interval;
|
||||
__s8 tx_power;
|
||||
} __packed;
|
||||
#define MGMT_ADD_EXT_ADV_PARAMS_MIN_SIZE 18
|
||||
struct mgmt_rp_add_ext_adv_params {
|
||||
__u8 instance;
|
||||
__s8 tx_power;
|
||||
__u8 max_adv_data_len;
|
||||
__u8 max_scan_rsp_len;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_ADD_EXT_ADV_DATA 0x0055
|
||||
struct mgmt_cp_add_ext_adv_data {
|
||||
__u8 instance;
|
||||
__u8 adv_data_len;
|
||||
__u8 scan_rsp_len;
|
||||
__u8 data[];
|
||||
} __packed;
|
||||
#define MGMT_ADD_EXT_ADV_DATA_SIZE 3
|
||||
struct mgmt_rp_add_ext_adv_data {
|
||||
__u8 instance;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||
struct mgmt_ev_cmd_complete {
|
||||
__le16 opcode;
|
||||
|
@ -758,6 +758,9 @@ static void create_le_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
|
||||
conn = hci_lookup_le_connect(hdev);
|
||||
|
||||
if (hdev->adv_instance_cnt)
|
||||
hci_req_resume_adv_instances(hdev);
|
||||
|
||||
if (!status) {
|
||||
hci_connect_le_scan_cleanup(conn);
|
||||
goto done;
|
||||
@ -1067,10 +1070,11 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
* connections most controllers will refuse to connect if
|
||||
* advertising is enabled, and for slave role connections we
|
||||
* anyway have to disable it in order to start directed
|
||||
* advertising.
|
||||
* advertising. Any registered advertisements will be
|
||||
* re-enabled after the connection attempt is finished.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
__hci_req_disable_advertising(&req);
|
||||
__hci_req_pause_adv_instances(&req);
|
||||
|
||||
/* If requested to connect as slave use directed advertising */
|
||||
if (conn->role == HCI_ROLE_SLAVE) {
|
||||
@ -1118,6 +1122,10 @@ create_conn:
|
||||
err = hci_req_run(&req, create_le_conn_complete);
|
||||
if (err) {
|
||||
hci_conn_del(conn);
|
||||
|
||||
if (hdev->adv_instance_cnt)
|
||||
hci_req_resume_adv_instances(hdev);
|
||||
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
@ -741,6 +741,12 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
|
||||
hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
|
||||
}
|
||||
|
||||
if (hdev->commands[38] & 0x80) {
|
||||
/* Read LE Min/Max Tx Power*/
|
||||
hci_req_add(req, HCI_OP_LE_READ_TRANSMIT_POWER,
|
||||
0, NULL);
|
||||
}
|
||||
|
||||
if (hdev->commands[26] & 0x40) {
|
||||
/* Read LE White List Size */
|
||||
hci_req_add(req, HCI_OP_LE_READ_WHITE_LIST_SIZE,
|
||||
@ -763,7 +769,7 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
|
||||
hci_req_add(req, HCI_OP_LE_CLEAR_RESOLV_LIST, 0, NULL);
|
||||
}
|
||||
|
||||
if (hdev->commands[35] & 0x40) {
|
||||
if (hdev->commands[35] & 0x04) {
|
||||
__le16 rpa_timeout = cpu_to_le16(hdev->rpa_timeout);
|
||||
|
||||
/* Set RPA timeout */
|
||||
@ -2951,7 +2957,8 @@ static void adv_instance_rpa_expired(struct work_struct *work)
|
||||
int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration)
|
||||
u16 timeout, u16 duration, s8 tx_power,
|
||||
u32 min_interval, u32 max_interval)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
@ -2979,6 +2986,9 @@ int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
adv_instance->flags = flags;
|
||||
adv_instance->adv_data_len = adv_data_len;
|
||||
adv_instance->scan_rsp_len = scan_rsp_len;
|
||||
adv_instance->min_interval = min_interval;
|
||||
adv_instance->max_interval = max_interval;
|
||||
adv_instance->tx_power = tx_power;
|
||||
|
||||
if (adv_data_len)
|
||||
memcpy(adv_instance->adv_data, adv_data, adv_data_len);
|
||||
@ -2995,8 +3005,6 @@ int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
else
|
||||
adv_instance->duration = duration;
|
||||
|
||||
adv_instance->tx_power = HCI_TX_POWER_INVALID;
|
||||
|
||||
INIT_DELAYED_WORK(&adv_instance->rpa_expired_cb,
|
||||
adv_instance_rpa_expired);
|
||||
|
||||
@ -3005,6 +3013,37 @@ int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
|
||||
/* If advertisement doesn't exist, we can't modify its data */
|
||||
if (!adv_instance)
|
||||
return -ENOENT;
|
||||
|
||||
if (adv_data_len) {
|
||||
memset(adv_instance->adv_data, 0,
|
||||
sizeof(adv_instance->adv_data));
|
||||
memcpy(adv_instance->adv_data, adv_data, adv_data_len);
|
||||
adv_instance->adv_data_len = adv_data_len;
|
||||
}
|
||||
|
||||
if (scan_rsp_len) {
|
||||
memset(adv_instance->scan_rsp_data, 0,
|
||||
sizeof(adv_instance->scan_rsp_data));
|
||||
memcpy(adv_instance->scan_rsp_data,
|
||||
scan_rsp_data, scan_rsp_len);
|
||||
adv_instance->scan_rsp_len = scan_rsp_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
void hci_adv_monitors_clear(struct hci_dev *hdev)
|
||||
{
|
||||
@ -3592,6 +3631,10 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
hdev->cur_adv_instance = 0x00;
|
||||
hdev->adv_instance_timeout = 0;
|
||||
|
||||
hdev->advmon_allowlist_duration = 300;
|
||||
hdev->advmon_no_filter_duration = 500;
|
||||
hdev->enable_advmon_interleave_scan = 0x00; /* Default to disable */
|
||||
|
||||
hdev->sniff_max_interval = 800;
|
||||
hdev->sniff_min_interval = 80;
|
||||
|
||||
@ -3623,6 +3666,8 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
hdev->le_num_of_adv_sets = HCI_MAX_ADV_INSTANCES;
|
||||
hdev->def_multi_adv_rotation_duration = HCI_DEFAULT_ADV_DURATION;
|
||||
hdev->def_le_autoconnect_timeout = HCI_LE_AUTOCONN_TIMEOUT;
|
||||
hdev->min_le_tx_power = HCI_TX_POWER_INVALID;
|
||||
hdev->max_le_tx_power = HCI_TX_POWER_INVALID;
|
||||
|
||||
hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
|
||||
hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT;
|
||||
|
@ -494,6 +494,45 @@ static int auto_accept_delay_get(void *data, u64 *val)
|
||||
DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
|
||||
auto_accept_delay_set, "%llu\n");
|
||||
|
||||
static ssize_t force_bredr_smp_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[3];
|
||||
|
||||
buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP) ? 'Y' : 'N';
|
||||
buf[1] = '\n';
|
||||
buf[2] = '\0';
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
}
|
||||
|
||||
static ssize_t force_bredr_smp_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
bool enable;
|
||||
int err;
|
||||
|
||||
err = kstrtobool_from_user(user_buf, count, &enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = smp_force_bredr(hdev, enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations force_bredr_smp_fops = {
|
||||
.open = simple_open,
|
||||
.read = force_bredr_smp_read,
|
||||
.write = force_bredr_smp_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int idle_timeout_set(void *data, u64 val)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
@ -589,6 +628,17 @@ void hci_debugfs_create_bredr(struct hci_dev *hdev)
|
||||
debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev,
|
||||
&voice_setting_fops);
|
||||
|
||||
/* If the controller does not support BR/EDR Secure Connections
|
||||
* feature, then the BR/EDR SMP channel shall not be present.
|
||||
*
|
||||
* To test this with Bluetooth 4.0 controllers, create a debugfs
|
||||
* switch that allows forcing BR/EDR SMP support and accepting
|
||||
* cross-transport pairing on non-AES encrypted connections.
|
||||
*/
|
||||
if (!lmp_sc_capable(hdev))
|
||||
debugfs_create_file("force_bredr_smp", 0644, hdev->debugfs,
|
||||
hdev, &force_bredr_smp_fops);
|
||||
|
||||
if (lmp_ssp_capable(hdev)) {
|
||||
debugfs_create_file("ssp_debug_mode", 0444, hdev->debugfs,
|
||||
hdev, &ssp_debug_mode_fops);
|
||||
|
@ -1202,6 +1202,20 @@ static void hci_cc_le_set_adv_set_random_addr(struct hci_dev *hdev,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_le_read_transmit_power(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_transmit_power *rp = (void *)skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hdev->min_le_tx_power = rp->min_le_tx_power;
|
||||
hdev->max_le_tx_power = rp->max_le_tx_power;
|
||||
}
|
||||
|
||||
static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
__u8 *sent, status = *((__u8 *) skb->data);
|
||||
@ -1752,6 +1766,7 @@ static void hci_cc_set_ext_adv_param(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
}
|
||||
/* Update adv data as tx power is known now */
|
||||
hci_req_update_adv_data(hdev, hdev->cur_adv_instance);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@ -3581,6 +3596,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
hci_cc_le_set_adv_set_random_addr(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_TRANSMIT_POWER:
|
||||
hci_cc_le_read_transmit_power(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
|
||||
break;
|
||||
@ -4936,15 +4955,15 @@ static void hci_phy_link_complete_evt(struct hci_dev *hdev,
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
||||
if (!hcon) {
|
||||
hci_dev_unlock(hdev);
|
||||
return;
|
||||
}
|
||||
if (!hcon)
|
||||
goto unlock;
|
||||
|
||||
if (!hcon->amp_mgr)
|
||||
goto unlock;
|
||||
|
||||
if (ev->status) {
|
||||
hci_conn_del(hcon);
|
||||
hci_dev_unlock(hdev);
|
||||
return;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon;
|
||||
@ -4961,6 +4980,7 @@ static void hci_phy_link_complete_evt(struct hci_dev *hdev,
|
||||
|
||||
amp_physical_cfm(bredr_hcon, hcon);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@ -5868,21 +5888,19 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
u8 num_reports = skb->data[0];
|
||||
void *ptr = &skb->data[1];
|
||||
struct hci_ev_le_direct_adv_info *ev = (void *)&skb->data[1];
|
||||
|
||||
if (!num_reports || skb->len < num_reports * sizeof(*ev) + 1)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
while (num_reports--) {
|
||||
struct hci_ev_le_direct_adv_info *ev = ptr;
|
||||
|
||||
for (; num_reports; num_reports--, ev++)
|
||||
process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
|
||||
ev->bdaddr_type, &ev->direct_addr,
|
||||
ev->direct_addr_type, ev->rssi, NULL, 0,
|
||||
false);
|
||||
|
||||
ptr += sizeof(*ev);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ static int req_run(struct hci_request *req, hci_req_complete_t complete,
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
BT_DBG("length %u", skb_queue_len(&req->cmd_q));
|
||||
bt_dev_dbg(hdev, "length %u", skb_queue_len(&req->cmd_q));
|
||||
|
||||
/* If an error occurred during request building, remove all HCI
|
||||
* commands queued on the HCI request queue.
|
||||
@ -102,7 +102,7 @@ int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete)
|
||||
static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
BT_DBG("%s result 0x%2.2x", hdev->name, result);
|
||||
bt_dev_dbg(hdev, "result 0x%2.2x", result);
|
||||
|
||||
if (hdev->req_status == HCI_REQ_PEND) {
|
||||
hdev->req_result = result;
|
||||
@ -115,7 +115,7 @@ static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
|
||||
|
||||
void hci_req_sync_cancel(struct hci_dev *hdev, int err)
|
||||
{
|
||||
BT_DBG("%s err 0x%2.2x", hdev->name, err);
|
||||
bt_dev_dbg(hdev, "err 0x%2.2x", err);
|
||||
|
||||
if (hdev->req_status == HCI_REQ_PEND) {
|
||||
hdev->req_result = err;
|
||||
@ -131,7 +131,7 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
struct sk_buff *skb;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
@ -167,7 +167,7 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
skb = hdev->req_skb;
|
||||
hdev->req_skb = NULL;
|
||||
|
||||
BT_DBG("%s end: err %d", hdev->name, err);
|
||||
bt_dev_dbg(hdev, "end: err %d", err);
|
||||
|
||||
if (err < 0) {
|
||||
kfree_skb(skb);
|
||||
@ -196,7 +196,7 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
|
||||
struct hci_request req;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("%s start", hdev->name);
|
||||
bt_dev_dbg(hdev, "start");
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
@ -260,7 +260,7 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
|
||||
hdev->req_skb = NULL;
|
||||
hdev->req_status = hdev->req_result = 0;
|
||||
|
||||
BT_DBG("%s end: err %d", hdev->name, err);
|
||||
bt_dev_dbg(hdev, "end: err %d", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -300,7 +300,7 @@ struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
if (plen)
|
||||
skb_put_data(skb, param, plen);
|
||||
|
||||
BT_DBG("skb len %d", skb->len);
|
||||
bt_dev_dbg(hdev, "skb len %d", skb->len);
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
|
||||
hci_skb_opcode(skb) = opcode;
|
||||
@ -315,7 +315,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct sk_buff *skb;
|
||||
|
||||
BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
|
||||
bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen);
|
||||
|
||||
/* If an error occurred during request building, there is no point in
|
||||
* queueing the HCI command. We can simply return.
|
||||
@ -378,6 +378,53 @@ void __hci_req_write_fast_connectable(struct hci_request *req, bool enable)
|
||||
hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
|
||||
}
|
||||
|
||||
static void start_interleave_scan(struct hci_dev *hdev)
|
||||
{
|
||||
hdev->interleave_scan_state = INTERLEAVE_SCAN_NO_FILTER;
|
||||
queue_delayed_work(hdev->req_workqueue,
|
||||
&hdev->interleave_scan, 0);
|
||||
}
|
||||
|
||||
static bool is_interleave_scanning(struct hci_dev *hdev)
|
||||
{
|
||||
return hdev->interleave_scan_state != INTERLEAVE_SCAN_NONE;
|
||||
}
|
||||
|
||||
static void cancel_interleave_scan(struct hci_dev *hdev)
|
||||
{
|
||||
bt_dev_dbg(hdev, "cancelling interleave scan");
|
||||
|
||||
cancel_delayed_work_sync(&hdev->interleave_scan);
|
||||
|
||||
hdev->interleave_scan_state = INTERLEAVE_SCAN_NONE;
|
||||
}
|
||||
|
||||
/* Return true if interleave_scan wasn't started until exiting this function,
|
||||
* otherwise, return false
|
||||
*/
|
||||
static bool __hci_update_interleaved_scan(struct hci_dev *hdev)
|
||||
{
|
||||
/* If there is at least one ADV monitors and one pending LE connection
|
||||
* or one device to be scanned for, we should alternate between
|
||||
* allowlist scan and one without any filters to save power.
|
||||
*/
|
||||
bool use_interleaving = hci_is_adv_monitoring(hdev) &&
|
||||
!(list_empty(&hdev->pend_le_conns) &&
|
||||
list_empty(&hdev->pend_le_reports));
|
||||
bool is_interleaving = is_interleave_scanning(hdev);
|
||||
|
||||
if (use_interleaving && !is_interleaving) {
|
||||
start_interleave_scan(hdev);
|
||||
bt_dev_dbg(hdev, "starting interleave scan");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!use_interleaving && is_interleaving)
|
||||
cancel_interleave_scan(hdev);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This function controls the background scanning based on hdev->pend_le_conns
|
||||
* list. If there are pending LE connection we start the background scanning,
|
||||
* otherwise we stop it.
|
||||
@ -413,7 +460,7 @@ static void __hci_update_background_scan(struct hci_request *req)
|
||||
*/
|
||||
hci_discovery_filter_clear(hdev);
|
||||
|
||||
BT_DBG("%s ADV monitoring is %s", hdev->name,
|
||||
bt_dev_dbg(hdev, "ADV monitoring is %s",
|
||||
hci_is_adv_monitoring(hdev) ? "on" : "off");
|
||||
|
||||
if (list_empty(&hdev->pend_le_conns) &&
|
||||
@ -430,7 +477,7 @@ static void __hci_update_background_scan(struct hci_request *req)
|
||||
|
||||
hci_req_add_le_scan_disable(req, false);
|
||||
|
||||
BT_DBG("%s stopping background scanning", hdev->name);
|
||||
bt_dev_dbg(hdev, "stopping background scanning");
|
||||
} else {
|
||||
/* If there is at least one pending LE connection, we should
|
||||
* keep the background scan running.
|
||||
@ -450,8 +497,7 @@ static void __hci_update_background_scan(struct hci_request *req)
|
||||
hci_req_add_le_scan_disable(req, false);
|
||||
|
||||
hci_req_add_le_passive_scan(req);
|
||||
|
||||
BT_DBG("%s starting background scanning", hdev->name);
|
||||
bt_dev_dbg(hdev, "starting background scanning");
|
||||
}
|
||||
}
|
||||
|
||||
@ -661,6 +707,9 @@ void hci_req_add_le_scan_disable(struct hci_request *req, bool rpa_le_conn)
|
||||
return;
|
||||
}
|
||||
|
||||
if (hdev->suspended)
|
||||
set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks);
|
||||
|
||||
if (use_ext_scan(hdev)) {
|
||||
struct hci_cp_le_set_ext_scan_enable cp;
|
||||
|
||||
@ -698,7 +747,8 @@ static void del_from_white_list(struct hci_request *req, bdaddr_t *bdaddr,
|
||||
cp.bdaddr_type);
|
||||
hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST, sizeof(cp), &cp);
|
||||
|
||||
if (use_ll_privacy(req->hdev)) {
|
||||
if (use_ll_privacy(req->hdev) &&
|
||||
hci_dev_test_flag(req->hdev, HCI_ENABLE_LL_PRIVACY)) {
|
||||
struct smp_irk *irk;
|
||||
|
||||
irk = hci_find_irk_by_addr(req->hdev, bdaddr, bdaddr_type);
|
||||
@ -732,7 +782,8 @@ static int add_to_white_list(struct hci_request *req,
|
||||
return -1;
|
||||
|
||||
/* White list can not be used with RPAs */
|
||||
if (!allow_rpa && !use_ll_privacy(hdev) &&
|
||||
if (!allow_rpa &&
|
||||
!hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY) &&
|
||||
hci_find_irk_by_addr(hdev, ¶ms->addr, params->addr_type)) {
|
||||
return -1;
|
||||
}
|
||||
@ -750,7 +801,8 @@ static int add_to_white_list(struct hci_request *req,
|
||||
cp.bdaddr_type);
|
||||
hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp);
|
||||
|
||||
if (use_ll_privacy(hdev)) {
|
||||
if (use_ll_privacy(hdev) &&
|
||||
hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY)) {
|
||||
struct smp_irk *irk;
|
||||
|
||||
irk = hci_find_irk_by_addr(hdev, ¶ms->addr,
|
||||
@ -812,7 +864,8 @@ static u8 update_white_list(struct hci_request *req)
|
||||
}
|
||||
|
||||
/* White list can not be used with RPAs */
|
||||
if (!allow_rpa && !use_ll_privacy(hdev) &&
|
||||
if (!allow_rpa &&
|
||||
!hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY) &&
|
||||
hci_find_irk_by_addr(hdev, &b->bdaddr, b->bdaddr_type)) {
|
||||
return 0x00;
|
||||
}
|
||||
@ -844,12 +897,17 @@ static u8 update_white_list(struct hci_request *req)
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
/* Once the controller offloading of advertisement monitor is in place,
|
||||
* the if condition should include the support of MSFT extension
|
||||
* support. If suspend is ongoing, whitelist should be the default to
|
||||
* prevent waking by random advertisements.
|
||||
/* Use the allowlist unless the following conditions are all true:
|
||||
* - We are not currently suspending
|
||||
* - There are 1 or more ADV monitors registered
|
||||
* - Interleaved scanning is not currently using the allowlist
|
||||
*
|
||||
* Once the controller offloading of advertisement monitor is in place,
|
||||
* the above condition should include the support of MSFT extension
|
||||
* support.
|
||||
*/
|
||||
if (!idr_is_empty(&hdev->adv_monitors_idr) && !hdev->suspended)
|
||||
if (!idr_is_empty(&hdev->adv_monitors_idr) && !hdev->suspended &&
|
||||
hdev->interleave_scan_state != INTERLEAVE_SCAN_ALLOWLIST)
|
||||
return 0x00;
|
||||
|
||||
/* Select filter policy to use white list */
|
||||
@ -1002,6 +1060,11 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
|
||||
&own_addr_type))
|
||||
return;
|
||||
|
||||
if (hdev->enable_advmon_interleave_scan &&
|
||||
__hci_update_interleaved_scan(hdev))
|
||||
return;
|
||||
|
||||
bt_dev_dbg(hdev, "interleave state %d", hdev->interleave_scan_state);
|
||||
/* Adding or removing entries from the white list must
|
||||
* happen before enabling scanning. The controller does
|
||||
* not allow white list modification while scanning.
|
||||
@ -1040,22 +1103,23 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
|
||||
own_addr_type, filter_policy, addr_resolv);
|
||||
}
|
||||
|
||||
static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
|
||||
static bool adv_instance_is_scannable(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
/* Instance 0x00 always set local name */
|
||||
if (instance == 0x00)
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
/* TODO: Take into account the "appearance" and "local-name" flags here.
|
||||
* These are currently being ignored as they are not supported.
|
||||
*/
|
||||
return adv_instance->scan_rsp_len;
|
||||
if (adv_instance->flags & MGMT_ADV_FLAG_APPEARANCE ||
|
||||
adv_instance->flags & MGMT_ADV_FLAG_LOCAL_NAME)
|
||||
return true;
|
||||
|
||||
return adv_instance->scan_rsp_len ? true : false;
|
||||
}
|
||||
|
||||
static void hci_req_clear_event_filter(struct hci_request *req)
|
||||
@ -1098,6 +1162,11 @@ static void hci_req_set_event_filter(struct hci_request *req)
|
||||
scan = SCAN_PAGE;
|
||||
}
|
||||
|
||||
if (scan)
|
||||
set_bit(SUSPEND_SCAN_ENABLE, hdev->suspend_tasks);
|
||||
else
|
||||
set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks);
|
||||
|
||||
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
|
||||
}
|
||||
|
||||
@ -1123,9 +1192,9 @@ static void cancel_adv_timeout(struct hci_dev *hdev)
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static void hci_suspend_adv_instances(struct hci_request *req)
|
||||
void __hci_req_pause_adv_instances(struct hci_request *req)
|
||||
{
|
||||
bt_dev_dbg(req->hdev, "Suspending advertising instances");
|
||||
bt_dev_dbg(req->hdev, "Pausing advertising instances");
|
||||
|
||||
/* Call to disable any advertisements active on the controller.
|
||||
* This will succeed even if no advertisements are configured.
|
||||
@ -1138,7 +1207,7 @@ static void hci_suspend_adv_instances(struct hci_request *req)
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static void hci_resume_adv_instances(struct hci_request *req)
|
||||
static void __hci_req_resume_adv_instances(struct hci_request *req)
|
||||
{
|
||||
struct adv_info *adv;
|
||||
|
||||
@ -1161,6 +1230,17 @@ static void hci_resume_adv_instances(struct hci_request *req)
|
||||
}
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
int hci_req_resume_adv_instances(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_request req;
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
__hci_req_resume_adv_instances(&req);
|
||||
|
||||
return hci_req_run(&req, NULL);
|
||||
}
|
||||
|
||||
static void suspend_req_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
{
|
||||
bt_dev_dbg(hdev, "Request complete opcode=0x%x, status=0x%x", opcode,
|
||||
@ -1214,7 +1294,7 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
|
||||
|
||||
/* Pause other advertisements */
|
||||
if (hdev->adv_instance_cnt)
|
||||
hci_suspend_adv_instances(&req);
|
||||
__hci_req_pause_adv_instances(&req);
|
||||
|
||||
hdev->advertising_paused = true;
|
||||
hdev->advertising_old_state = old_state;
|
||||
@ -1223,8 +1303,10 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
|
||||
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan);
|
||||
|
||||
/* Disable LE passive scan if enabled */
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
|
||||
cancel_interleave_scan(hdev);
|
||||
hci_req_add_le_scan_disable(&req, false);
|
||||
}
|
||||
|
||||
/* Mark task needing completion */
|
||||
set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks);
|
||||
@ -1279,7 +1361,7 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
|
||||
|
||||
/* Resume other advertisements */
|
||||
if (hdev->adv_instance_cnt)
|
||||
hci_resume_adv_instances(&req);
|
||||
__hci_req_resume_adv_instances(&req);
|
||||
|
||||
/* Unpause discovery */
|
||||
hdev->discovery_paused = false;
|
||||
@ -1300,23 +1382,9 @@ done:
|
||||
wake_up(&hdev->suspend_wait_q);
|
||||
}
|
||||
|
||||
static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
|
||||
static bool adv_cur_instance_is_scannable(struct hci_dev *hdev)
|
||||
{
|
||||
u8 instance = hdev->cur_adv_instance;
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
/* Instance 0x00 always set local name */
|
||||
if (instance == 0x00)
|
||||
return 1;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
|
||||
/* TODO: Take into account the "appearance" and "local-name" flags here.
|
||||
* These are currently being ignored as they are not supported.
|
||||
*/
|
||||
return adv_instance->scan_rsp_len;
|
||||
return adv_instance_is_scannable(hdev, hdev->cur_adv_instance);
|
||||
}
|
||||
|
||||
void __hci_req_disable_advertising(struct hci_request *req)
|
||||
@ -1428,6 +1496,7 @@ static bool is_advertising_allowed(struct hci_dev *hdev, bool connectable)
|
||||
void __hci_req_enable_advertising(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct adv_info *adv_instance;
|
||||
struct hci_cp_le_set_adv_param cp;
|
||||
u8 own_addr_type, enable = 0x01;
|
||||
bool connectable;
|
||||
@ -1435,6 +1504,7 @@ void __hci_req_enable_advertising(struct hci_request *req)
|
||||
u32 flags;
|
||||
|
||||
flags = get_adv_instance_flags(hdev, hdev->cur_adv_instance);
|
||||
adv_instance = hci_find_adv_instance(hdev, hdev->cur_adv_instance);
|
||||
|
||||
/* If the "connectable" instance flag was not set, then choose between
|
||||
* ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
|
||||
@ -1466,13 +1536,18 @@ void __hci_req_enable_advertising(struct hci_request *req)
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
if (connectable) {
|
||||
cp.type = LE_ADV_IND;
|
||||
|
||||
if (adv_instance) {
|
||||
adv_min_interval = adv_instance->min_interval;
|
||||
adv_max_interval = adv_instance->max_interval;
|
||||
} else {
|
||||
adv_min_interval = hdev->le_adv_min_interval;
|
||||
adv_max_interval = hdev->le_adv_max_interval;
|
||||
}
|
||||
|
||||
if (connectable) {
|
||||
cp.type = LE_ADV_IND;
|
||||
} else {
|
||||
if (get_cur_adv_instance_scan_rsp_len(hdev))
|
||||
if (adv_cur_instance_is_scannable(hdev))
|
||||
cp.type = LE_ADV_SCAN_IND;
|
||||
else
|
||||
cp.type = LE_ADV_NONCONN_IND;
|
||||
@ -1481,9 +1556,6 @@ void __hci_req_enable_advertising(struct hci_request *req)
|
||||
hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) {
|
||||
adv_min_interval = DISCOV_LE_FAST_ADV_INT_MIN;
|
||||
adv_max_interval = DISCOV_LE_FAST_ADV_INT_MAX;
|
||||
} else {
|
||||
adv_min_interval = hdev->le_adv_min_interval;
|
||||
adv_max_interval = hdev->le_adv_max_interval;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1591,14 +1663,11 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
/* Extended scan response data doesn't allow a response to be
|
||||
* set if the instance isn't scannable.
|
||||
*/
|
||||
if (get_adv_instance_scan_rsp_len(hdev, instance))
|
||||
if (instance)
|
||||
len = create_instance_scan_rsp_data(hdev, instance,
|
||||
cp.data);
|
||||
else
|
||||
len = 0;
|
||||
len = create_default_scan_rsp_data(hdev, cp.data);
|
||||
|
||||
if (hdev->scan_rsp_data_len == len &&
|
||||
!memcmp(cp.data, hdev->scan_rsp_data, len))
|
||||
@ -1811,7 +1880,7 @@ void hci_req_disable_address_resolution(struct hci_dev *hdev)
|
||||
|
||||
static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
{
|
||||
BT_DBG("%s status %u", hdev->name, status);
|
||||
bt_dev_dbg(hdev, "status %u", status);
|
||||
}
|
||||
|
||||
void hci_req_reenable_advertising(struct hci_dev *hdev)
|
||||
@ -1848,7 +1917,7 @@ static void adv_timeout_expire(struct work_struct *work)
|
||||
struct hci_request req;
|
||||
u8 instance;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
@ -1871,6 +1940,62 @@ unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int hci_req_add_le_interleaved_scan(struct hci_request *req,
|
||||
unsigned long opt)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
int ret = 0;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
||||
hci_req_add_le_scan_disable(req, false);
|
||||
hci_req_add_le_passive_scan(req);
|
||||
|
||||
switch (hdev->interleave_scan_state) {
|
||||
case INTERLEAVE_SCAN_ALLOWLIST:
|
||||
bt_dev_dbg(hdev, "next state: allowlist");
|
||||
hdev->interleave_scan_state = INTERLEAVE_SCAN_NO_FILTER;
|
||||
break;
|
||||
case INTERLEAVE_SCAN_NO_FILTER:
|
||||
bt_dev_dbg(hdev, "next state: no filter");
|
||||
hdev->interleave_scan_state = INTERLEAVE_SCAN_ALLOWLIST;
|
||||
break;
|
||||
case INTERLEAVE_SCAN_NONE:
|
||||
BT_ERR("unexpected error");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void interleave_scan_work(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
interleave_scan.work);
|
||||
u8 status;
|
||||
unsigned long timeout;
|
||||
|
||||
if (hdev->interleave_scan_state == INTERLEAVE_SCAN_ALLOWLIST) {
|
||||
timeout = msecs_to_jiffies(hdev->advmon_allowlist_duration);
|
||||
} else if (hdev->interleave_scan_state == INTERLEAVE_SCAN_NO_FILTER) {
|
||||
timeout = msecs_to_jiffies(hdev->advmon_no_filter_duration);
|
||||
} else {
|
||||
bt_dev_err(hdev, "unexpected error");
|
||||
return;
|
||||
}
|
||||
|
||||
hci_req_sync(hdev, hci_req_add_le_interleaved_scan, 0,
|
||||
HCI_CMD_TIMEOUT, &status);
|
||||
|
||||
/* Don't continue interleaving if it was canceled */
|
||||
if (is_interleave_scanning(hdev))
|
||||
queue_delayed_work(hdev->req_workqueue,
|
||||
&hdev->interleave_scan, timeout);
|
||||
}
|
||||
|
||||
int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
||||
bool use_rpa, struct adv_info *adv_instance,
|
||||
u8 *own_addr_type, bdaddr_t *rand_addr)
|
||||
@ -2006,9 +2131,15 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
/* In ext adv set param interval is 3 octets */
|
||||
if (adv_instance) {
|
||||
hci_cpu_to_le24(adv_instance->min_interval, cp.min_interval);
|
||||
hci_cpu_to_le24(adv_instance->max_interval, cp.max_interval);
|
||||
cp.tx_power = adv_instance->tx_power;
|
||||
} else {
|
||||
hci_cpu_to_le24(hdev->le_adv_min_interval, cp.min_interval);
|
||||
hci_cpu_to_le24(hdev->le_adv_max_interval, cp.max_interval);
|
||||
cp.tx_power = HCI_ADV_TX_POWER_NO_PREFERENCE;
|
||||
}
|
||||
|
||||
secondary_adv = (flags & MGMT_ADV_FLAG_SEC_MASK);
|
||||
|
||||
@ -2017,7 +2148,7 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
||||
cp.evt_properties = cpu_to_le16(LE_EXT_ADV_CONN_IND);
|
||||
else
|
||||
cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_IND);
|
||||
} else if (get_adv_instance_scan_rsp_len(hdev, instance)) {
|
||||
} else if (adv_instance_is_scannable(hdev, instance)) {
|
||||
if (secondary_adv)
|
||||
cp.evt_properties = cpu_to_le16(LE_EXT_ADV_SCAN_IND);
|
||||
else
|
||||
@ -2031,7 +2162,6 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
||||
|
||||
cp.own_addr_type = own_addr_type;
|
||||
cp.channel_map = hdev->le_adv_channel_map;
|
||||
cp.tx_power = 127;
|
||||
cp.handle = instance;
|
||||
|
||||
if (flags & MGMT_ADV_FLAG_SEC_2M) {
|
||||
@ -2332,7 +2462,7 @@ static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV) ||
|
||||
hci_lookup_le_connect(hdev)) {
|
||||
BT_DBG("Deferring random address update");
|
||||
bt_dev_dbg(hdev, "Deferring random address update");
|
||||
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
|
||||
return;
|
||||
}
|
||||
@ -2557,7 +2687,7 @@ void __hci_req_update_class(struct hci_request *req)
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 cod[3];
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
if (!hdev_is_powered(hdev))
|
||||
return;
|
||||
@ -2726,7 +2856,7 @@ void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
|
||||
static void abort_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
{
|
||||
if (status)
|
||||
BT_DBG("Failed to abort connection: status 0x%2.2x", status);
|
||||
bt_dev_dbg(hdev, "Failed to abort connection: status 0x%2.2x", status);
|
||||
}
|
||||
|
||||
int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
||||
@ -2789,7 +2919,7 @@ static int bredr_inquiry(struct hci_request *req, unsigned long opt)
|
||||
const u8 liac[3] = { 0x00, 0x8b, 0x9e };
|
||||
struct hci_cp_inquiry cp;
|
||||
|
||||
BT_DBG("%s", req->hdev->name);
|
||||
bt_dev_dbg(req->hdev, "");
|
||||
|
||||
hci_dev_lock(req->hdev);
|
||||
hci_inquiry_cache_flush(req->hdev);
|
||||
@ -2815,7 +2945,7 @@ static void le_scan_disable_work(struct work_struct *work)
|
||||
le_scan_disable.work);
|
||||
u8 status;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
||||
return;
|
||||
@ -2911,7 +3041,7 @@ static void le_scan_restart_work(struct work_struct *work)
|
||||
unsigned long timeout, duration, scan_start, now;
|
||||
u8 status;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_req_sync(hdev, le_scan_restart, 0, HCI_CMD_TIMEOUT, &status);
|
||||
if (status) {
|
||||
@ -2965,14 +3095,16 @@ static int active_scan(struct hci_request *req, unsigned long opt)
|
||||
bool addr_resolv = false;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
/* If controller is scanning, it means the background scanning is
|
||||
* running. Thus, we should temporarily stop it in order to set the
|
||||
* discovery scanning parameters.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
|
||||
hci_req_add_le_scan_disable(req, false);
|
||||
cancel_interleave_scan(hdev);
|
||||
}
|
||||
|
||||
/* All active scans will be done with either a resolvable private
|
||||
* address (when privacy feature has been enabled) or non-resolvable
|
||||
@ -2993,7 +3125,7 @@ static int interleaved_discov(struct hci_request *req, unsigned long opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", req->hdev->name);
|
||||
bt_dev_dbg(req->hdev, "");
|
||||
|
||||
err = active_scan(req, opt);
|
||||
if (err)
|
||||
@ -3006,7 +3138,7 @@ static void start_discovery(struct hci_dev *hdev, u8 *status)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
BT_DBG("%s type %u", hdev->name, hdev->discovery.type);
|
||||
bt_dev_dbg(hdev, "type %u", hdev->discovery.type);
|
||||
|
||||
switch (hdev->discovery.type) {
|
||||
case DISCOV_TYPE_BREDR:
|
||||
@ -3054,7 +3186,7 @@ static void start_discovery(struct hci_dev *hdev, u8 *status)
|
||||
if (*status)
|
||||
return;
|
||||
|
||||
BT_DBG("%s timeout %u ms", hdev->name, jiffies_to_msecs(timeout));
|
||||
bt_dev_dbg(hdev, "timeout %u ms", jiffies_to_msecs(timeout));
|
||||
|
||||
/* When service discovery is used and the controller has a
|
||||
* strict duplicate filter, it is important to remember the
|
||||
@ -3079,7 +3211,7 @@ bool hci_req_stop_discovery(struct hci_request *req)
|
||||
struct inquiry_entry *e;
|
||||
bool ret = false;
|
||||
|
||||
BT_DBG("%s state %u", hdev->name, hdev->discovery.state);
|
||||
bt_dev_dbg(hdev, "state %u", hdev->discovery.state);
|
||||
|
||||
if (d->state == DISCOVERY_FINDING || d->state == DISCOVERY_STOPPING) {
|
||||
if (test_bit(HCI_INQUIRY, &hdev->flags))
|
||||
@ -3159,7 +3291,7 @@ static void discov_off(struct work_struct *work)
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
discov_off.work);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
@ -3298,6 +3430,7 @@ void hci_request_setup(struct hci_dev *hdev)
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
|
||||
INIT_DELAYED_WORK(&hdev->adv_instance_expire, adv_timeout_expire);
|
||||
INIT_DELAYED_WORK(&hdev->interleave_scan, interleave_scan_work);
|
||||
}
|
||||
|
||||
void hci_request_cancel_all(struct hci_dev *hdev)
|
||||
@ -3317,4 +3450,6 @@ void hci_request_cancel_all(struct hci_dev *hdev)
|
||||
cancel_delayed_work_sync(&hdev->adv_instance_expire);
|
||||
hdev->adv_instance_timeout = 0;
|
||||
}
|
||||
|
||||
cancel_interleave_scan(hdev);
|
||||
}
|
||||
|
@ -71,6 +71,8 @@ void hci_req_add_le_passive_scan(struct hci_request *req);
|
||||
void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next);
|
||||
|
||||
void hci_req_disable_address_resolution(struct hci_dev *hdev);
|
||||
void __hci_req_pause_adv_instances(struct hci_request *req);
|
||||
int hci_req_resume_adv_instances(struct hci_dev *hdev);
|
||||
void hci_req_reenable_advertising(struct hci_dev *hdev);
|
||||
void __hci_req_enable_advertising(struct hci_request *req);
|
||||
void __hci_req_disable_advertising(struct hci_request *req);
|
||||
|
@ -1290,7 +1290,7 @@ static int hidp_session_thread(void *arg)
|
||||
|
||||
/* cleanup runtime environment */
|
||||
remove_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
|
||||
remove_wait_queue(sk_sleep(session->intr_sock->sk), &ctrl_wait);
|
||||
remove_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
|
||||
wake_up_interruptible(&session->report_queue);
|
||||
hidp_del_timer(session);
|
||||
|
||||
|
@ -1515,8 +1515,14 @@ static bool l2cap_check_enc_key_size(struct hci_conn *hcon)
|
||||
* that have no key size requirements. Ensure that the link is
|
||||
* actually encrypted before enforcing a key size.
|
||||
*/
|
||||
int min_key_size = hcon->hdev->min_enc_key_size;
|
||||
|
||||
/* On FIPS security level, key size must be 16 bytes */
|
||||
if (hcon->sec_level == BT_SECURITY_FIPS)
|
||||
min_key_size = 16;
|
||||
|
||||
return (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags) ||
|
||||
hcon->enc_key_size >= hcon->hdev->min_enc_key_size);
|
||||
hcon->enc_key_size >= min_key_size);
|
||||
}
|
||||
|
||||
static void l2cap_do_start(struct l2cap_chan *chan)
|
||||
@ -3627,7 +3633,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
|
||||
if (hint)
|
||||
break;
|
||||
result = L2CAP_CONF_UNKNOWN;
|
||||
*((u8 *) ptr++) = type;
|
||||
l2cap_add_conf_opt(&ptr, (u8)type, sizeof(u8), type, endptr - ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@
|
||||
#include "msft.h"
|
||||
|
||||
#define MGMT_VERSION 1
|
||||
#define MGMT_REVISION 18
|
||||
#define MGMT_REVISION 19
|
||||
|
||||
static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_READ_INDEX_LIST,
|
||||
@ -110,7 +110,7 @@ static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_SET_APPEARANCE,
|
||||
MGMT_OP_SET_BLOCKED_KEYS,
|
||||
MGMT_OP_SET_WIDEBAND_SPEECH,
|
||||
MGMT_OP_READ_SECURITY_INFO,
|
||||
MGMT_OP_READ_CONTROLLER_CAP,
|
||||
MGMT_OP_READ_EXP_FEATURES_INFO,
|
||||
MGMT_OP_SET_EXP_FEATURE,
|
||||
MGMT_OP_READ_DEF_SYSTEM_CONFIG,
|
||||
@ -122,6 +122,8 @@ static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_READ_ADV_MONITOR_FEATURES,
|
||||
MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
|
||||
MGMT_OP_REMOVE_ADV_MONITOR,
|
||||
MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
};
|
||||
|
||||
static const u16 mgmt_events[] = {
|
||||
@ -174,7 +176,7 @@ static const u16 mgmt_untrusted_commands[] = {
|
||||
MGMT_OP_READ_CONFIG_INFO,
|
||||
MGMT_OP_READ_EXT_INDEX_LIST,
|
||||
MGMT_OP_READ_EXT_INFO,
|
||||
MGMT_OP_READ_SECURITY_INFO,
|
||||
MGMT_OP_READ_CONTROLLER_CAP,
|
||||
MGMT_OP_READ_EXP_FEATURES_INFO,
|
||||
MGMT_OP_READ_DEF_SYSTEM_CONFIG,
|
||||
MGMT_OP_READ_DEF_RUNTIME_CONFIG,
|
||||
@ -3387,7 +3389,7 @@ static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
static int get_phy_configuration(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct mgmt_rp_get_phy_confguration rp;
|
||||
struct mgmt_rp_get_phy_configuration rp;
|
||||
|
||||
bt_dev_dbg(hdev, "sock %p", sk);
|
||||
|
||||
@ -3451,7 +3453,7 @@ unlock:
|
||||
static int set_phy_configuration(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct mgmt_cp_set_phy_confguration *cp = data;
|
||||
struct mgmt_cp_set_phy_configuration *cp = data;
|
||||
struct hci_cp_le_set_default_phy cp_phy;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct hci_request req;
|
||||
@ -3708,13 +3710,14 @@ unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int read_security_info(struct sock *sk, struct hci_dev *hdev,
|
||||
static int read_controller_cap(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 data_len)
|
||||
{
|
||||
char buf[16];
|
||||
struct mgmt_rp_read_security_info *rp = (void *)buf;
|
||||
u16 sec_len = 0;
|
||||
char buf[20];
|
||||
struct mgmt_rp_read_controller_cap *rp = (void *)buf;
|
||||
u16 cap_len = 0;
|
||||
u8 flags = 0;
|
||||
u8 tx_power_range[2];
|
||||
|
||||
bt_dev_dbg(hdev, "sock %p", sk);
|
||||
|
||||
@ -3738,23 +3741,37 @@ static int read_security_info(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
flags |= 0x08; /* Encryption key size enforcement (LE) */
|
||||
|
||||
sec_len = eir_append_data(rp->sec, sec_len, 0x01, &flags, 1);
|
||||
cap_len = eir_append_data(rp->cap, cap_len, MGMT_CAP_SEC_FLAGS,
|
||||
&flags, 1);
|
||||
|
||||
/* When the Read Simple Pairing Options command is supported, then
|
||||
* also max encryption key size information is provided.
|
||||
*/
|
||||
if (hdev->commands[41] & 0x08)
|
||||
sec_len = eir_append_le16(rp->sec, sec_len, 0x02,
|
||||
cap_len = eir_append_le16(rp->cap, cap_len,
|
||||
MGMT_CAP_MAX_ENC_KEY_SIZE,
|
||||
hdev->max_enc_key_size);
|
||||
|
||||
sec_len = eir_append_le16(rp->sec, sec_len, 0x03, SMP_MAX_ENC_KEY_SIZE);
|
||||
cap_len = eir_append_le16(rp->cap, cap_len,
|
||||
MGMT_CAP_SMP_MAX_ENC_KEY_SIZE,
|
||||
SMP_MAX_ENC_KEY_SIZE);
|
||||
|
||||
rp->sec_len = cpu_to_le16(sec_len);
|
||||
/* Append the min/max LE tx power parameters if we were able to fetch
|
||||
* it from the controller
|
||||
*/
|
||||
if (hdev->commands[38] & 0x80) {
|
||||
memcpy(&tx_power_range[0], &hdev->min_le_tx_power, 1);
|
||||
memcpy(&tx_power_range[1], &hdev->max_le_tx_power, 1);
|
||||
cap_len = eir_append_data(rp->cap, cap_len, MGMT_CAP_LE_TX_PWR,
|
||||
tx_power_range, 2);
|
||||
}
|
||||
|
||||
rp->cap_len = cpu_to_le16(cap_len);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_SECURITY_INFO, 0,
|
||||
rp, sizeof(*rp) + sec_len);
|
||||
return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_CONTROLLER_CAP, 0,
|
||||
rp, sizeof(*rp) + cap_len);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_FEATURE_DEBUG
|
||||
@ -7203,6 +7220,10 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev)
|
||||
flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
|
||||
flags |= MGMT_ADV_FLAG_APPEARANCE;
|
||||
flags |= MGMT_ADV_FLAG_LOCAL_NAME;
|
||||
flags |= MGMT_ADV_PARAM_DURATION;
|
||||
flags |= MGMT_ADV_PARAM_TIMEOUT;
|
||||
flags |= MGMT_ADV_PARAM_INTERVALS;
|
||||
flags |= MGMT_ADV_PARAM_TX_POWER;
|
||||
|
||||
/* In extended adv TX_POWER returned from Set Adv Param
|
||||
* will be always valid.
|
||||
@ -7377,6 +7398,31 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool requested_adv_flags_are_valid(struct hci_dev *hdev, u32 adv_flags)
|
||||
{
|
||||
u32 supported_flags, phy_flags;
|
||||
|
||||
/* The current implementation only supports a subset of the specified
|
||||
* flags. Also need to check mutual exclusiveness of sec flags.
|
||||
*/
|
||||
supported_flags = get_supported_adv_flags(hdev);
|
||||
phy_flags = adv_flags & MGMT_ADV_FLAG_SEC_MASK;
|
||||
if (adv_flags & ~supported_flags ||
|
||||
((phy_flags && (phy_flags ^ (phy_flags & -phy_flags)))))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool adv_busy(struct hci_dev *hdev)
|
||||
{
|
||||
return (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) ||
|
||||
pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) ||
|
||||
pending_find(MGMT_OP_SET_LE, hdev) ||
|
||||
pending_find(MGMT_OP_ADD_EXT_ADV_PARAMS, hdev) ||
|
||||
pending_find(MGMT_OP_ADD_EXT_ADV_DATA, hdev));
|
||||
}
|
||||
|
||||
static void add_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
@ -7391,6 +7437,8 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev);
|
||||
if (!cmd)
|
||||
cmd = pending_find(MGMT_OP_ADD_EXT_ADV_DATA, hdev);
|
||||
|
||||
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
|
||||
if (!adv_instance->pending)
|
||||
@ -7435,7 +7483,6 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
struct mgmt_cp_add_advertising *cp = data;
|
||||
struct mgmt_rp_add_advertising rp;
|
||||
u32 flags;
|
||||
u32 supported_flags, phy_flags;
|
||||
u8 status;
|
||||
u16 timeout, duration;
|
||||
unsigned int prev_instance_cnt = hdev->adv_instance_cnt;
|
||||
@ -7471,13 +7518,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
timeout = __le16_to_cpu(cp->timeout);
|
||||
duration = __le16_to_cpu(cp->duration);
|
||||
|
||||
/* The current implementation only supports a subset of the specified
|
||||
* flags. Also need to check mutual exclusiveness of sec flags.
|
||||
*/
|
||||
supported_flags = get_supported_adv_flags(hdev);
|
||||
phy_flags = flags & MGMT_ADV_FLAG_SEC_MASK;
|
||||
if (flags & ~supported_flags ||
|
||||
((phy_flags && (phy_flags ^ (phy_flags & -phy_flags)))))
|
||||
if (!requested_adv_flags_are_valid(hdev, flags))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
@ -7489,9 +7530,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) ||
|
||||
pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) ||
|
||||
pending_find(MGMT_OP_SET_LE, hdev)) {
|
||||
if (adv_busy(hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto unlock;
|
||||
@ -7509,7 +7548,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
cp->adv_data_len, cp->data,
|
||||
cp->scan_rsp_len,
|
||||
cp->data + cp->adv_data_len,
|
||||
timeout, duration);
|
||||
timeout, duration,
|
||||
HCI_ADV_TX_POWER_NO_PREFERENCE,
|
||||
hdev->le_adv_min_interval,
|
||||
hdev->le_adv_max_interval);
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_FAILED);
|
||||
@ -7582,6 +7624,338 @@ unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void add_ext_adv_params_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct mgmt_cp_add_ext_adv_params *cp;
|
||||
struct mgmt_rp_add_ext_adv_params rp;
|
||||
struct adv_info *adv_instance;
|
||||
u32 flags;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = pending_find(MGMT_OP_ADD_EXT_ADV_PARAMS, hdev);
|
||||
if (!cmd)
|
||||
goto unlock;
|
||||
|
||||
cp = cmd->param;
|
||||
adv_instance = hci_find_adv_instance(hdev, cp->instance);
|
||||
if (!adv_instance)
|
||||
goto unlock;
|
||||
|
||||
rp.instance = cp->instance;
|
||||
rp.tx_power = adv_instance->tx_power;
|
||||
|
||||
/* While we're at it, inform userspace of the available space for this
|
||||
* advertisement, given the flags that will be used.
|
||||
*/
|
||||
flags = __le32_to_cpu(cp->flags);
|
||||
rp.max_adv_data_len = tlv_data_max_len(hdev, flags, true);
|
||||
rp.max_scan_rsp_len = tlv_data_max_len(hdev, flags, false);
|
||||
|
||||
if (status) {
|
||||
/* If this advertisement was previously advertising and we
|
||||
* failed to update it, we signal that it has been removed and
|
||||
* delete its structure
|
||||
*/
|
||||
if (!adv_instance->pending)
|
||||
mgmt_advertising_removed(cmd->sk, hdev, cp->instance);
|
||||
|
||||
hci_remove_adv_instance(hdev, cp->instance);
|
||||
|
||||
mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode,
|
||||
mgmt_status(status));
|
||||
|
||||
} else {
|
||||
mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
|
||||
mgmt_status(status), &rp, sizeof(rp));
|
||||
}
|
||||
|
||||
unlock:
|
||||
if (cmd)
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 data_len)
|
||||
{
|
||||
struct mgmt_cp_add_ext_adv_params *cp = data;
|
||||
struct mgmt_rp_add_ext_adv_params rp;
|
||||
struct mgmt_pending_cmd *cmd = NULL;
|
||||
struct adv_info *adv_instance;
|
||||
struct hci_request req;
|
||||
u32 flags, min_interval, max_interval;
|
||||
u16 timeout, duration;
|
||||
u8 status;
|
||||
s8 tx_power;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
status = mgmt_le_support(hdev);
|
||||
if (status)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
status);
|
||||
|
||||
if (cp->instance < 1 || cp->instance > hdev->le_num_of_adv_sets)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
/* The purpose of breaking add_advertising into two separate MGMT calls
|
||||
* for params and data is to allow more parameters to be added to this
|
||||
* structure in the future. For this reason, we verify that we have the
|
||||
* bare minimum structure we know of when the interface was defined. Any
|
||||
* extra parameters we don't know about will be ignored in this request.
|
||||
*/
|
||||
if (data_len < MGMT_ADD_EXT_ADV_PARAMS_MIN_SIZE)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
flags = __le32_to_cpu(cp->flags);
|
||||
|
||||
if (!requested_adv_flags_are_valid(hdev, flags))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* In new interface, we require that we are powered to register */
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_STATUS_REJECTED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (adv_busy(hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Parse defined parameters from request, use defaults otherwise */
|
||||
timeout = (flags & MGMT_ADV_PARAM_TIMEOUT) ?
|
||||
__le16_to_cpu(cp->timeout) : 0;
|
||||
|
||||
duration = (flags & MGMT_ADV_PARAM_DURATION) ?
|
||||
__le16_to_cpu(cp->duration) :
|
||||
hdev->def_multi_adv_rotation_duration;
|
||||
|
||||
min_interval = (flags & MGMT_ADV_PARAM_INTERVALS) ?
|
||||
__le32_to_cpu(cp->min_interval) :
|
||||
hdev->le_adv_min_interval;
|
||||
|
||||
max_interval = (flags & MGMT_ADV_PARAM_INTERVALS) ?
|
||||
__le32_to_cpu(cp->max_interval) :
|
||||
hdev->le_adv_max_interval;
|
||||
|
||||
tx_power = (flags & MGMT_ADV_PARAM_TX_POWER) ?
|
||||
cp->tx_power :
|
||||
HCI_ADV_TX_POWER_NO_PREFERENCE;
|
||||
|
||||
/* Create advertising instance with no advertising or response data */
|
||||
err = hci_add_adv_instance(hdev, cp->instance, flags,
|
||||
0, NULL, 0, NULL, timeout, duration,
|
||||
tx_power, min_interval, max_interval);
|
||||
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_STATUS_FAILED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hdev->cur_adv_instance = cp->instance;
|
||||
/* Submit request for advertising params if ext adv available */
|
||||
if (ext_adv_capable(hdev)) {
|
||||
hci_req_init(&req, hdev);
|
||||
adv_instance = hci_find_adv_instance(hdev, cp->instance);
|
||||
|
||||
/* Updating parameters of an active instance will return a
|
||||
* Command Disallowed error, so we must first disable the
|
||||
* instance if it is active.
|
||||
*/
|
||||
if (!adv_instance->pending)
|
||||
__hci_req_disable_ext_adv_instance(&req, cp->instance);
|
||||
|
||||
__hci_req_setup_ext_adv_instance(&req, cp->instance);
|
||||
|
||||
err = hci_req_run(&req, add_ext_adv_params_complete);
|
||||
|
||||
if (!err)
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
hdev, data, data_len);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
hci_remove_adv_instance(hdev, cp->instance);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
} else {
|
||||
rp.instance = cp->instance;
|
||||
rp.tx_power = HCI_ADV_TX_POWER_NO_PREFERENCE;
|
||||
rp.max_adv_data_len = tlv_data_max_len(hdev, flags, true);
|
||||
rp.max_scan_rsp_len = tlv_data_max_len(hdev, flags, false);
|
||||
err = mgmt_cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int add_ext_adv_data(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 data_len)
|
||||
{
|
||||
struct mgmt_cp_add_ext_adv_data *cp = data;
|
||||
struct mgmt_rp_add_ext_adv_data rp;
|
||||
u8 schedule_instance = 0;
|
||||
struct adv_info *next_instance;
|
||||
struct adv_info *adv_instance;
|
||||
int err = 0;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct hci_request req;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, cp->instance);
|
||||
|
||||
if (!adv_instance) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* In new interface, we require that we are powered to register */
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
MGMT_STATUS_REJECTED);
|
||||
goto clear_new_instance;
|
||||
}
|
||||
|
||||
if (adv_busy(hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto clear_new_instance;
|
||||
}
|
||||
|
||||
/* Validate new data */
|
||||
if (!tlv_data_is_valid(hdev, adv_instance->flags, cp->data,
|
||||
cp->adv_data_len, true) ||
|
||||
!tlv_data_is_valid(hdev, adv_instance->flags, cp->data +
|
||||
cp->adv_data_len, cp->scan_rsp_len, false)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
goto clear_new_instance;
|
||||
}
|
||||
|
||||
/* Set the data in the advertising instance */
|
||||
hci_set_adv_instance_data(hdev, cp->instance, cp->adv_data_len,
|
||||
cp->data, cp->scan_rsp_len,
|
||||
cp->data + cp->adv_data_len);
|
||||
|
||||
/* We're good to go, update advertising data, parameters, and start
|
||||
* advertising.
|
||||
*/
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_NAME, 0, NULL);
|
||||
|
||||
if (ext_adv_capable(hdev)) {
|
||||
__hci_req_update_adv_data(&req, cp->instance);
|
||||
__hci_req_update_scan_rsp_data(&req, cp->instance);
|
||||
__hci_req_enable_ext_advertising(&req, cp->instance);
|
||||
|
||||
} else {
|
||||
/* If using software rotation, determine next instance to use */
|
||||
|
||||
if (hdev->cur_adv_instance == cp->instance) {
|
||||
/* If the currently advertised instance is being changed
|
||||
* then cancel the current advertising and schedule the
|
||||
* next instance. If there is only one instance then the
|
||||
* overridden advertising data will be visible right
|
||||
* away
|
||||
*/
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
next_instance = hci_get_next_instance(hdev,
|
||||
cp->instance);
|
||||
if (next_instance)
|
||||
schedule_instance = next_instance->instance;
|
||||
} else if (!hdev->adv_instance_timeout) {
|
||||
/* Immediately advertise the new instance if no other
|
||||
* instance is currently being advertised.
|
||||
*/
|
||||
schedule_instance = cp->instance;
|
||||
}
|
||||
|
||||
/* If the HCI_ADVERTISING flag is set or there is no instance to
|
||||
* be advertised then we have no HCI communication to make.
|
||||
* Simply return.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!schedule_instance) {
|
||||
if (adv_instance->pending) {
|
||||
mgmt_advertising_added(sk, hdev, cp->instance);
|
||||
adv_instance->pending = false;
|
||||
}
|
||||
rp.instance = cp->instance;
|
||||
err = mgmt_cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
MGMT_STATUS_SUCCESS, &rp,
|
||||
sizeof(rp));
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = __hci_req_schedule_adv_instance(&req, schedule_instance,
|
||||
true);
|
||||
}
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_ADD_EXT_ADV_DATA, hdev, data,
|
||||
data_len);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
goto clear_new_instance;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
err = hci_req_run(&req, add_advertising_complete);
|
||||
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
MGMT_STATUS_FAILED);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto clear_new_instance;
|
||||
}
|
||||
|
||||
/* We were successful in updating data, so trigger advertising_added
|
||||
* event if this is an instance that wasn't previously advertising. If
|
||||
* a failure occurs in the requests we initiated, we will remove the
|
||||
* instance again in add_advertising_complete
|
||||
*/
|
||||
if (adv_instance->pending)
|
||||
mgmt_advertising_added(sk, hdev, cp->instance);
|
||||
|
||||
goto unlock;
|
||||
|
||||
clear_new_instance:
|
||||
hci_remove_adv_instance(hdev, cp->instance);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void remove_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
@ -7834,7 +8208,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
|
||||
{ set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
{ set_wideband_speech, MGMT_SETTING_SIZE },
|
||||
{ read_security_info, MGMT_READ_SECURITY_INFO_SIZE,
|
||||
{ read_controller_cap, MGMT_READ_CONTROLLER_CAP_SIZE,
|
||||
HCI_MGMT_UNTRUSTED },
|
||||
{ read_exp_features_info, MGMT_READ_EXP_FEATURES_INFO_SIZE,
|
||||
HCI_MGMT_UNTRUSTED |
|
||||
@ -7856,6 +8230,10 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
|
||||
{ add_adv_patterns_monitor,MGMT_ADD_ADV_PATTERNS_MONITOR_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
{ remove_adv_monitor, MGMT_REMOVE_ADV_MONITOR_SIZE },
|
||||
{ add_ext_adv_params, MGMT_ADD_EXT_ADV_PARAMS_MIN_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
{ add_ext_adv_data, MGMT_ADD_EXT_ADV_DATA_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
};
|
||||
|
||||
void mgmt_index_added(struct hci_dev *hdev)
|
||||
|
@ -11,74 +11,119 @@
|
||||
#include "mgmt_util.h"
|
||||
#include "mgmt_config.h"
|
||||
|
||||
#define HDEV_PARAM_U16(_param_code_, _param_name_) \
|
||||
#define HDEV_PARAM_U16(_param_name_) \
|
||||
struct {\
|
||||
struct mgmt_tlv entry; \
|
||||
__le16 value; \
|
||||
} __packed _param_name_
|
||||
|
||||
#define HDEV_PARAM_U8(_param_name_) \
|
||||
struct {\
|
||||
struct mgmt_tlv entry; \
|
||||
__u8 value; \
|
||||
} __packed _param_name_
|
||||
|
||||
#define TLV_SET_U16(_param_code_, _param_name_) \
|
||||
{ \
|
||||
{ cpu_to_le16(_param_code_), sizeof(__u16) }, \
|
||||
{ cpu_to_le16(hdev->_param_name_) } \
|
||||
cpu_to_le16(hdev->_param_name_) \
|
||||
}
|
||||
|
||||
#define HDEV_PARAM_U16_JIFFIES_TO_MSECS(_param_code_, _param_name_) \
|
||||
#define TLV_SET_U8(_param_code_, _param_name_) \
|
||||
{ \
|
||||
{ cpu_to_le16(_param_code_), sizeof(__u8) }, \
|
||||
hdev->_param_name_ \
|
||||
}
|
||||
|
||||
#define TLV_SET_U16_JIFFIES_TO_MSECS(_param_code_, _param_name_) \
|
||||
{ \
|
||||
{ cpu_to_le16(_param_code_), sizeof(__u16) }, \
|
||||
{ cpu_to_le16(jiffies_to_msecs(hdev->_param_name_)) } \
|
||||
cpu_to_le16(jiffies_to_msecs(hdev->_param_name_)) \
|
||||
}
|
||||
|
||||
int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 data_len)
|
||||
{
|
||||
struct {
|
||||
struct mgmt_tlv entry;
|
||||
union {
|
||||
/* This is a simplification for now since all values
|
||||
* are 16 bits. In the future, this code may need
|
||||
* refactoring to account for variable length values
|
||||
* and properly calculate the required buffer size.
|
||||
*/
|
||||
__le16 value;
|
||||
};
|
||||
} __packed params[] = {
|
||||
int ret;
|
||||
struct mgmt_rp_read_def_system_config {
|
||||
/* Please see mgmt-api.txt for documentation of these values */
|
||||
HDEV_PARAM_U16(0x0000, def_page_scan_type),
|
||||
HDEV_PARAM_U16(0x0001, def_page_scan_int),
|
||||
HDEV_PARAM_U16(0x0002, def_page_scan_window),
|
||||
HDEV_PARAM_U16(0x0003, def_inq_scan_type),
|
||||
HDEV_PARAM_U16(0x0004, def_inq_scan_int),
|
||||
HDEV_PARAM_U16(0x0005, def_inq_scan_window),
|
||||
HDEV_PARAM_U16(0x0006, def_br_lsto),
|
||||
HDEV_PARAM_U16(0x0007, def_page_timeout),
|
||||
HDEV_PARAM_U16(0x0008, sniff_min_interval),
|
||||
HDEV_PARAM_U16(0x0009, sniff_max_interval),
|
||||
HDEV_PARAM_U16(0x000a, le_adv_min_interval),
|
||||
HDEV_PARAM_U16(0x000b, le_adv_max_interval),
|
||||
HDEV_PARAM_U16(0x000c, def_multi_adv_rotation_duration),
|
||||
HDEV_PARAM_U16(0x000d, le_scan_interval),
|
||||
HDEV_PARAM_U16(0x000e, le_scan_window),
|
||||
HDEV_PARAM_U16(0x000f, le_scan_int_suspend),
|
||||
HDEV_PARAM_U16(0x0010, le_scan_window_suspend),
|
||||
HDEV_PARAM_U16(0x0011, le_scan_int_discovery),
|
||||
HDEV_PARAM_U16(0x0012, le_scan_window_discovery),
|
||||
HDEV_PARAM_U16(0x0013, le_scan_int_adv_monitor),
|
||||
HDEV_PARAM_U16(0x0014, le_scan_window_adv_monitor),
|
||||
HDEV_PARAM_U16(0x0015, le_scan_int_connect),
|
||||
HDEV_PARAM_U16(0x0016, le_scan_window_connect),
|
||||
HDEV_PARAM_U16(0x0017, le_conn_min_interval),
|
||||
HDEV_PARAM_U16(0x0018, le_conn_max_interval),
|
||||
HDEV_PARAM_U16(0x0019, le_conn_latency),
|
||||
HDEV_PARAM_U16(0x001a, le_supv_timeout),
|
||||
HDEV_PARAM_U16_JIFFIES_TO_MSECS(0x001b,
|
||||
HDEV_PARAM_U16(def_page_scan_type);
|
||||
HDEV_PARAM_U16(def_page_scan_int);
|
||||
HDEV_PARAM_U16(def_page_scan_window);
|
||||
HDEV_PARAM_U16(def_inq_scan_type);
|
||||
HDEV_PARAM_U16(def_inq_scan_int);
|
||||
HDEV_PARAM_U16(def_inq_scan_window);
|
||||
HDEV_PARAM_U16(def_br_lsto);
|
||||
HDEV_PARAM_U16(def_page_timeout);
|
||||
HDEV_PARAM_U16(sniff_min_interval);
|
||||
HDEV_PARAM_U16(sniff_max_interval);
|
||||
HDEV_PARAM_U16(le_adv_min_interval);
|
||||
HDEV_PARAM_U16(le_adv_max_interval);
|
||||
HDEV_PARAM_U16(def_multi_adv_rotation_duration);
|
||||
HDEV_PARAM_U16(le_scan_interval);
|
||||
HDEV_PARAM_U16(le_scan_window);
|
||||
HDEV_PARAM_U16(le_scan_int_suspend);
|
||||
HDEV_PARAM_U16(le_scan_window_suspend);
|
||||
HDEV_PARAM_U16(le_scan_int_discovery);
|
||||
HDEV_PARAM_U16(le_scan_window_discovery);
|
||||
HDEV_PARAM_U16(le_scan_int_adv_monitor);
|
||||
HDEV_PARAM_U16(le_scan_window_adv_monitor);
|
||||
HDEV_PARAM_U16(le_scan_int_connect);
|
||||
HDEV_PARAM_U16(le_scan_window_connect);
|
||||
HDEV_PARAM_U16(le_conn_min_interval);
|
||||
HDEV_PARAM_U16(le_conn_max_interval);
|
||||
HDEV_PARAM_U16(le_conn_latency);
|
||||
HDEV_PARAM_U16(le_supv_timeout);
|
||||
HDEV_PARAM_U16(def_le_autoconnect_timeout);
|
||||
HDEV_PARAM_U16(advmon_allowlist_duration);
|
||||
HDEV_PARAM_U16(advmon_no_filter_duration);
|
||||
HDEV_PARAM_U8(enable_advmon_interleave_scan);
|
||||
} __packed rp = {
|
||||
TLV_SET_U16(0x0000, def_page_scan_type),
|
||||
TLV_SET_U16(0x0001, def_page_scan_int),
|
||||
TLV_SET_U16(0x0002, def_page_scan_window),
|
||||
TLV_SET_U16(0x0003, def_inq_scan_type),
|
||||
TLV_SET_U16(0x0004, def_inq_scan_int),
|
||||
TLV_SET_U16(0x0005, def_inq_scan_window),
|
||||
TLV_SET_U16(0x0006, def_br_lsto),
|
||||
TLV_SET_U16(0x0007, def_page_timeout),
|
||||
TLV_SET_U16(0x0008, sniff_min_interval),
|
||||
TLV_SET_U16(0x0009, sniff_max_interval),
|
||||
TLV_SET_U16(0x000a, le_adv_min_interval),
|
||||
TLV_SET_U16(0x000b, le_adv_max_interval),
|
||||
TLV_SET_U16(0x000c, def_multi_adv_rotation_duration),
|
||||
TLV_SET_U16(0x000d, le_scan_interval),
|
||||
TLV_SET_U16(0x000e, le_scan_window),
|
||||
TLV_SET_U16(0x000f, le_scan_int_suspend),
|
||||
TLV_SET_U16(0x0010, le_scan_window_suspend),
|
||||
TLV_SET_U16(0x0011, le_scan_int_discovery),
|
||||
TLV_SET_U16(0x0012, le_scan_window_discovery),
|
||||
TLV_SET_U16(0x0013, le_scan_int_adv_monitor),
|
||||
TLV_SET_U16(0x0014, le_scan_window_adv_monitor),
|
||||
TLV_SET_U16(0x0015, le_scan_int_connect),
|
||||
TLV_SET_U16(0x0016, le_scan_window_connect),
|
||||
TLV_SET_U16(0x0017, le_conn_min_interval),
|
||||
TLV_SET_U16(0x0018, le_conn_max_interval),
|
||||
TLV_SET_U16(0x0019, le_conn_latency),
|
||||
TLV_SET_U16(0x001a, le_supv_timeout),
|
||||
TLV_SET_U16_JIFFIES_TO_MSECS(0x001b,
|
||||
def_le_autoconnect_timeout),
|
||||
TLV_SET_U16(0x001d, advmon_allowlist_duration),
|
||||
TLV_SET_U16(0x001e, advmon_no_filter_duration),
|
||||
TLV_SET_U8(0x001f, enable_advmon_interleave_scan),
|
||||
};
|
||||
struct mgmt_rp_read_def_system_config *rp = (void *)params;
|
||||
|
||||
bt_dev_dbg(hdev, "sock %p", sk);
|
||||
|
||||
return mgmt_cmd_complete(sk, hdev->id,
|
||||
ret = mgmt_cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_READ_DEF_SYSTEM_CONFIG,
|
||||
0, rp, sizeof(params));
|
||||
0, &rp, sizeof(rp));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define TO_TLV(x) ((struct mgmt_tlv *)(x))
|
||||
#define TLV_GET_LE16(tlv) le16_to_cpu(*((__le16 *)(TO_TLV(tlv)->value)))
|
||||
#define TLV_GET_U8(tlv) (*((__u8 *)(TO_TLV(tlv)->value)))
|
||||
|
||||
int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 data_len)
|
||||
@ -95,6 +140,7 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
/* First pass to validate the tlv */
|
||||
while (buffer_left >= sizeof(struct mgmt_tlv)) {
|
||||
const u8 len = TO_TLV(buffer)->length;
|
||||
size_t exp_type_len;
|
||||
const u16 exp_len = sizeof(struct mgmt_tlv) +
|
||||
len;
|
||||
const u16 type = le16_to_cpu(TO_TLV(buffer)->type);
|
||||
@ -138,19 +184,27 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
case 0x0019:
|
||||
case 0x001a:
|
||||
case 0x001b:
|
||||
if (len != sizeof(u16)) {
|
||||
case 0x001d:
|
||||
case 0x001e:
|
||||
exp_type_len = sizeof(u16);
|
||||
break;
|
||||
case 0x001f:
|
||||
exp_type_len = sizeof(u8);
|
||||
break;
|
||||
default:
|
||||
exp_type_len = 0;
|
||||
bt_dev_warn(hdev, "unsupported parameter %u", type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (exp_type_len && len != exp_type_len) {
|
||||
bt_dev_warn(hdev, "invalid length %d, exp %zu for type %d",
|
||||
len, sizeof(u16), type);
|
||||
len, exp_type_len, type);
|
||||
|
||||
return mgmt_cmd_status(sk, hdev->id,
|
||||
MGMT_OP_SET_DEF_SYSTEM_CONFIG,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bt_dev_warn(hdev, "unsupported parameter %u", type);
|
||||
break;
|
||||
}
|
||||
|
||||
buffer_left -= exp_len;
|
||||
buffer += exp_len;
|
||||
@ -251,6 +305,15 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
hdev->def_le_autoconnect_timeout =
|
||||
msecs_to_jiffies(TLV_GET_LE16(buffer));
|
||||
break;
|
||||
case 0x0001d:
|
||||
hdev->advmon_allowlist_duration = TLV_GET_LE16(buffer);
|
||||
break;
|
||||
case 0x0001e:
|
||||
hdev->advmon_no_filter_duration = TLV_GET_LE16(buffer);
|
||||
break;
|
||||
case 0x0001f:
|
||||
hdev->enable_advmon_interleave_scan = TLV_GET_U8(buffer);
|
||||
break;
|
||||
default:
|
||||
bt_dev_warn(hdev, "unsupported parameter %u", type);
|
||||
break;
|
||||
|
@ -1003,6 +1003,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
case BT_SNDMTU:
|
||||
case BT_RCVMTU:
|
||||
if (sk->sk_state != BT_CONNECTED) {
|
||||
err = -ENOTCONN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(sco_pi(sk)->conn->mtu, (u32 __user *)optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
@ -3353,31 +3353,8 @@ static void smp_del_chan(struct l2cap_chan *chan)
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
static ssize_t force_bredr_smp_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
int smp_force_bredr(struct hci_dev *hdev, bool enable)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[3];
|
||||
|
||||
buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP) ? 'Y': 'N';
|
||||
buf[1] = '\n';
|
||||
buf[2] = '\0';
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
}
|
||||
|
||||
static ssize_t force_bredr_smp_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
bool enable;
|
||||
int err;
|
||||
|
||||
err = kstrtobool_from_user(user_buf, count, &enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (enable == hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
|
||||
return -EALREADY;
|
||||
|
||||
@ -3399,16 +3376,9 @@ static ssize_t force_bredr_smp_write(struct file *file,
|
||||
|
||||
hci_dev_change_flag(hdev, HCI_FORCE_BREDR_SMP);
|
||||
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations force_bredr_smp_fops = {
|
||||
.open = simple_open,
|
||||
.read = force_bredr_smp_read,
|
||||
.write = force_bredr_smp_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
int smp_register(struct hci_dev *hdev)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
@ -3433,17 +3403,7 @@ int smp_register(struct hci_dev *hdev)
|
||||
|
||||
hdev->smp_data = chan;
|
||||
|
||||
/* If the controller does not support BR/EDR Secure Connections
|
||||
* feature, then the BR/EDR SMP channel shall not be present.
|
||||
*
|
||||
* To test this with Bluetooth 4.0 controllers, create a debugfs
|
||||
* switch that allows forcing BR/EDR SMP support and accepting
|
||||
* cross-transport pairing on non-AES encrypted connections.
|
||||
*/
|
||||
if (!lmp_sc_capable(hdev)) {
|
||||
debugfs_create_file("force_bredr_smp", 0644, hdev->debugfs,
|
||||
hdev, &force_bredr_smp_fops);
|
||||
|
||||
/* Flag can be already set here (due to power toggle) */
|
||||
if (!hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
|
||||
return 0;
|
||||
|
@ -193,6 +193,8 @@ bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
|
||||
int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
|
||||
int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]);
|
||||
|
||||
int smp_force_bredr(struct hci_dev *hdev, bool enable);
|
||||
|
||||
int smp_register(struct hci_dev *hdev);
|
||||
void smp_unregister(struct hci_dev *hdev);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user