forked from Minki/linux
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-09-29 Here's the main bluetooth-next pull request for 5.10: - Multiple fixes to suspend/resume handling - Added mgmt events for controller suspend/resume state - Improved extended advertising support - btintel: Enhanced support for next generation controllers - Added Qualcomm Bluetooth SoC WCN6855 support - Several other smaller fixes & improvements ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
2bd056f550
@ -18,7 +18,11 @@
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
|
||||
#define BDADDR_INTEL (&(bdaddr_t){{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
|
||||
#define RSA_HEADER_LEN 644
|
||||
#define CSS_HEADER_OFFSET 8
|
||||
#define ECDSA_OFFSET 644
|
||||
#define ECDSA_HEADER_LEN 320
|
||||
|
||||
int btintel_check_bdaddr(struct hci_dev *hdev)
|
||||
{
|
||||
@ -360,6 +364,144 @@ int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_read_version);
|
||||
|
||||
void btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
|
||||
{
|
||||
const char *variant;
|
||||
|
||||
switch (version->img_type) {
|
||||
case 0x01:
|
||||
variant = "Bootloader";
|
||||
bt_dev_info(hdev, "Device revision is %u", version->dev_rev_id);
|
||||
bt_dev_info(hdev, "Secure boot is %s",
|
||||
version->secure_boot ? "enabled" : "disabled");
|
||||
bt_dev_info(hdev, "OTP lock is %s",
|
||||
version->otp_lock ? "enabled" : "disabled");
|
||||
bt_dev_info(hdev, "API lock is %s",
|
||||
version->api_lock ? "enabled" : "disabled");
|
||||
bt_dev_info(hdev, "Debug lock is %s",
|
||||
version->debug_lock ? "enabled" : "disabled");
|
||||
bt_dev_info(hdev, "Minimum firmware build %u week %u %u",
|
||||
version->min_fw_build_nn, version->min_fw_build_cw,
|
||||
2000 + version->min_fw_build_yy);
|
||||
break;
|
||||
case 0x03:
|
||||
variant = "Firmware";
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported image type(%02x)", version->img_type);
|
||||
goto done;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant,
|
||||
2000 + (version->timestamp >> 8), version->timestamp & 0xff,
|
||||
version->build_type, version->build_num);
|
||||
|
||||
done:
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
|
||||
|
||||
int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
const u8 param[1] = { 0xFF };
|
||||
|
||||
if (!version)
|
||||
return -EINVAL;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
|
||||
PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
if (skb->data[0]) {
|
||||
bt_dev_err(hdev, "Intel Read Version command failed (%02x)",
|
||||
skb->data[0]);
|
||||
kfree_skb(skb);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Consume Command Complete Status field */
|
||||
skb_pull(skb, 1);
|
||||
|
||||
/* Event parameters contatin multiple TLVs. Read each of them
|
||||
* and only keep the required data. Also, it use existing legacy
|
||||
* version field like hw_platform, hw_variant, and fw_variant
|
||||
* to keep the existing setup flow
|
||||
*/
|
||||
while (skb->len) {
|
||||
struct intel_tlv *tlv;
|
||||
|
||||
tlv = (struct intel_tlv *)skb->data;
|
||||
switch (tlv->type) {
|
||||
case INTEL_TLV_CNVI_TOP:
|
||||
version->cnvi_top = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_CNVR_TOP:
|
||||
version->cnvr_top = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_CNVI_BT:
|
||||
version->cnvi_bt = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_CNVR_BT:
|
||||
version->cnvr_bt = get_unaligned_le32(tlv->val);
|
||||
break;
|
||||
case INTEL_TLV_DEV_REV_ID:
|
||||
version->dev_rev_id = 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);
|
||||
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);
|
||||
break;
|
||||
case INTEL_TLV_SECURE_BOOT:
|
||||
version->secure_boot = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_OTP_LOCK:
|
||||
version->otp_lock = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_API_LOCK:
|
||||
version->api_lock = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_DEBUG_LOCK:
|
||||
version->debug_lock = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_MIN_FW:
|
||||
version->min_fw_build_nn = tlv->val[0];
|
||||
version->min_fw_build_cw = tlv->val[1];
|
||||
version->min_fw_build_yy = tlv->val[2];
|
||||
break;
|
||||
case INTEL_TLV_LIMITED_CCE:
|
||||
version->limited_cce = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_SBE_TYPE:
|
||||
version->sbe_type = tlv->val[0];
|
||||
break;
|
||||
case INTEL_TLV_OTP_BDADDR:
|
||||
memcpy(&version->otp_bd_addr, tlv->val, tlv->len);
|
||||
break;
|
||||
default:
|
||||
/* Ignore rest of information */
|
||||
break;
|
||||
}
|
||||
/* consume the current tlv and move to next*/
|
||||
skb_pull(skb, tlv->len + sizeof(*tlv));
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_read_version_tlv);
|
||||
|
||||
/* ------- REGMAP IBT SUPPORT ------- */
|
||||
|
||||
#define IBT_REG_MODE_8BIT 0x00
|
||||
@ -626,12 +768,10 @@ int btintel_read_boot_params(struct hci_dev *hdev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_read_boot_params);
|
||||
|
||||
int btintel_download_firmware(struct hci_dev *hdev, const struct firmware *fw,
|
||||
u32 *boot_param)
|
||||
static int btintel_sfi_rsa_header_secure_send(struct hci_dev *hdev,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
int err;
|
||||
const u8 *fw_ptr;
|
||||
u32 frag_len;
|
||||
|
||||
/* Start the firmware download transaction with the Init fragment
|
||||
* represented by the 128 bytes of CSS header.
|
||||
@ -660,8 +800,56 @@ int btintel_download_firmware(struct hci_dev *hdev, const struct firmware *fw,
|
||||
goto done;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data + 644;
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btintel_sfi_ecdsa_header_secure_send(struct hci_dev *hdev,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Start the firmware download transaction with the Init fragment
|
||||
* represented by the 128 bytes of CSS header.
|
||||
*/
|
||||
err = btintel_secure_send(hdev, 0x00, 128, fw->data + 644);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send firmware header (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Send the 96 bytes of public key information from the firmware
|
||||
* as the PKey fragment.
|
||||
*/
|
||||
err = btintel_secure_send(hdev, 0x03, 96, fw->data + 644 + 128);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send firmware pkey (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Send the 96 bytes of signature information from the firmware
|
||||
* as the Sign fragment
|
||||
*/
|
||||
err = btintel_secure_send(hdev, 0x02, 96, fw->data + 644 + 224);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send firmware signature (%d)",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btintel_download_firmware_payload(struct hci_dev *hdev,
|
||||
const struct firmware *fw,
|
||||
u32 *boot_param, size_t offset)
|
||||
{
|
||||
int err;
|
||||
const u8 *fw_ptr;
|
||||
u32 frag_len;
|
||||
|
||||
fw_ptr = fw->data + offset;
|
||||
frag_len = 0;
|
||||
err = -EINVAL;
|
||||
|
||||
while (fw_ptr - fw->data < fw->size) {
|
||||
struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);
|
||||
@ -707,8 +895,99 @@ int btintel_download_firmware(struct hci_dev *hdev, const struct firmware *fw,
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
int btintel_download_firmware(struct hci_dev *hdev,
|
||||
const struct firmware *fw,
|
||||
u32 *boot_param)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = btintel_sfi_rsa_header_secure_send(hdev, fw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return btintel_download_firmware_payload(hdev, fw, boot_param,
|
||||
RSA_HEADER_LEN);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_download_firmware);
|
||||
|
||||
int btintel_download_firmware_newgen(struct hci_dev *hdev,
|
||||
const struct firmware *fw, u32 *boot_param,
|
||||
u8 hw_variant, u8 sbe_type)
|
||||
{
|
||||
int err;
|
||||
u32 css_header_ver;
|
||||
|
||||
/* iBT hardware variants 0x0b, 0x0c, 0x11, 0x12, 0x13, 0x14 support
|
||||
* only RSA secure boot engine. Hence, the corresponding sfi file will
|
||||
* have RSA header of 644 bytes followed by Command Buffer.
|
||||
*
|
||||
* iBT hardware variants 0x17, 0x18 onwards support both RSA and ECDSA
|
||||
* secure boot engine. As a result, the corresponding sfi file will
|
||||
* have RSA header of 644, ECDSA header of 320 bytes followed by
|
||||
* Command Buffer.
|
||||
*
|
||||
* CSS Header byte positions 0x08 to 0x0B represent the CSS Header
|
||||
* version: RSA(0x00010000) , ECDSA (0x00020000)
|
||||
*/
|
||||
css_header_ver = get_unaligned_le32(fw->data + CSS_HEADER_OFFSET);
|
||||
if (css_header_ver != 0x00010000) {
|
||||
bt_dev_err(hdev, "Invalid CSS Header version");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hw_variant <= 0x14) {
|
||||
if (sbe_type != 0x00) {
|
||||
bt_dev_err(hdev, "Invalid SBE type for hardware variant (%d)",
|
||||
hw_variant);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = btintel_sfi_rsa_header_secure_send(hdev, fw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = btintel_download_firmware_payload(hdev, fw, boot_param, RSA_HEADER_LEN);
|
||||
if (err)
|
||||
return err;
|
||||
} else if (hw_variant >= 0x17) {
|
||||
/* Check if CSS header for ECDSA follows the RSA header */
|
||||
if (fw->data[ECDSA_OFFSET] != 0x06)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if the CSS Header version is ECDSA(0x00020000) */
|
||||
css_header_ver = get_unaligned_le32(fw->data + ECDSA_OFFSET + CSS_HEADER_OFFSET);
|
||||
if (css_header_ver != 0x00020000) {
|
||||
bt_dev_err(hdev, "Invalid CSS Header version");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sbe_type == 0x00) {
|
||||
err = btintel_sfi_rsa_header_secure_send(hdev, fw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = btintel_download_firmware_payload(hdev, fw,
|
||||
boot_param,
|
||||
RSA_HEADER_LEN + ECDSA_HEADER_LEN);
|
||||
if (err)
|
||||
return err;
|
||||
} else if (sbe_type == 0x01) {
|
||||
err = btintel_sfi_ecdsa_header_secure_send(hdev, fw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = btintel_download_firmware_payload(hdev, fw,
|
||||
boot_param,
|
||||
RSA_HEADER_LEN + ECDSA_HEADER_LEN);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_download_firmware_newgen);
|
||||
|
||||
void btintel_reset_to_bootloader(struct hci_dev *hdev)
|
||||
{
|
||||
struct intel_reset params;
|
||||
|
@ -6,6 +6,72 @@
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
/* List of tlv type */
|
||||
enum {
|
||||
INTEL_TLV_CNVI_TOP = 0x10,
|
||||
INTEL_TLV_CNVR_TOP,
|
||||
INTEL_TLV_CNVI_BT,
|
||||
INTEL_TLV_CNVR_BT,
|
||||
INTEL_TLV_CNVI_OTP,
|
||||
INTEL_TLV_CNVR_OTP,
|
||||
INTEL_TLV_DEV_REV_ID,
|
||||
INTEL_TLV_USB_VENDOR_ID,
|
||||
INTEL_TLV_USB_PRODUCT_ID,
|
||||
INTEL_TLV_PCIE_VENDOR_ID,
|
||||
INTEL_TLV_PCIE_DEVICE_ID,
|
||||
INTEL_TLV_PCIE_SUBSYSTEM_ID,
|
||||
INTEL_TLV_IMAGE_TYPE,
|
||||
INTEL_TLV_TIME_STAMP,
|
||||
INTEL_TLV_BUILD_TYPE,
|
||||
INTEL_TLV_BUILD_NUM,
|
||||
INTEL_TLV_FW_BUILD_PRODUCT,
|
||||
INTEL_TLV_FW_BUILD_HW,
|
||||
INTEL_TLV_FW_STEP,
|
||||
INTEL_TLV_BT_SPEC,
|
||||
INTEL_TLV_MFG_NAME,
|
||||
INTEL_TLV_HCI_REV,
|
||||
INTEL_TLV_LMP_SUBVER,
|
||||
INTEL_TLV_OTP_PATCH_VER,
|
||||
INTEL_TLV_SECURE_BOOT,
|
||||
INTEL_TLV_KEY_FROM_HDR,
|
||||
INTEL_TLV_OTP_LOCK,
|
||||
INTEL_TLV_API_LOCK,
|
||||
INTEL_TLV_DEBUG_LOCK,
|
||||
INTEL_TLV_MIN_FW,
|
||||
INTEL_TLV_LIMITED_CCE,
|
||||
INTEL_TLV_SBE_TYPE,
|
||||
INTEL_TLV_OTP_BDADDR,
|
||||
INTEL_TLV_UNLOCKED_STATE
|
||||
};
|
||||
|
||||
struct intel_tlv {
|
||||
u8 type;
|
||||
u8 len;
|
||||
u8 val[0];
|
||||
} __packed;
|
||||
|
||||
struct intel_version_tlv {
|
||||
u32 cnvi_top;
|
||||
u32 cnvr_top;
|
||||
u32 cnvi_bt;
|
||||
u32 cnvr_bt;
|
||||
u16 dev_rev_id;
|
||||
u8 img_type;
|
||||
u16 timestamp;
|
||||
u8 build_type;
|
||||
u32 build_num;
|
||||
u8 secure_boot;
|
||||
u8 otp_lock;
|
||||
u8 api_lock;
|
||||
u8 debug_lock;
|
||||
u8 min_fw_build_nn;
|
||||
u8 min_fw_build_cw;
|
||||
u8 min_fw_build_yy;
|
||||
u8 limited_cce;
|
||||
u8 sbe_type;
|
||||
bdaddr_t otp_bd_addr;
|
||||
};
|
||||
|
||||
struct intel_version {
|
||||
u8 status;
|
||||
u8 hw_platform;
|
||||
@ -77,12 +143,14 @@ int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
|
||||
void btintel_hw_error(struct hci_dev *hdev, u8 code);
|
||||
|
||||
void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
|
||||
void btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version);
|
||||
int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
|
||||
const void *param);
|
||||
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
|
||||
int btintel_set_event_mask(struct hci_dev *hdev, bool debug);
|
||||
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
|
||||
int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver);
|
||||
int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver);
|
||||
|
||||
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
|
||||
u16 opcode_write);
|
||||
@ -91,6 +159,10 @@ int btintel_read_boot_params(struct hci_dev *hdev,
|
||||
struct intel_boot_params *params);
|
||||
int btintel_download_firmware(struct hci_dev *dev, const struct firmware *fw,
|
||||
u32 *boot_param);
|
||||
int btintel_download_firmware_newgen(struct hci_dev *hdev,
|
||||
const struct firmware *fw,
|
||||
u32 *boot_param, u8 hw_variant,
|
||||
u8 sbe_type);
|
||||
void btintel_reset_to_bootloader(struct hci_dev *hdev);
|
||||
int btintel_read_debug_features(struct hci_dev *hdev,
|
||||
struct intel_debug_features *features);
|
||||
@ -137,6 +209,11 @@ static inline void btintel_version_info(struct hci_dev *hdev,
|
||||
{
|
||||
}
|
||||
|
||||
static inline void btintel_version_info_tlv(struct hci_dev *hdev,
|
||||
struct intel_version_tlv *version)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type,
|
||||
u32 plen, const void *param)
|
||||
{
|
||||
@ -165,6 +242,12 @@ static inline int btintel_read_version(struct hci_dev *hdev,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int btintel_read_version_tlv(struct hci_dev *hdev,
|
||||
struct intel_version_tlv *ver)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
|
||||
u16 opcode_read,
|
||||
u16 opcode_write)
|
||||
@ -191,6 +274,14 @@ static inline int btintel_download_firmware(struct hci_dev *dev,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int btintel_download_firmware_newgen(struct hci_dev *hdev,
|
||||
const struct firmware *fw,
|
||||
u32 *boot_param,
|
||||
u8 hw_variant, u8 sbe_type)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void btintel_reset_to_bootloader(struct hci_dev *hdev)
|
||||
{
|
||||
}
|
||||
|
@ -215,30 +215,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
|
||||
.fw_dump_end = 0xea,
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_card_reg btmrvl_reg_8977 = {
|
||||
.cfg = 0x00,
|
||||
.host_int_mask = 0x08,
|
||||
.host_intstatus = 0x0c,
|
||||
.card_status = 0x5c,
|
||||
.sq_read_base_addr_a0 = 0xf8,
|
||||
.sq_read_base_addr_a1 = 0xf9,
|
||||
.card_revision = 0xc8,
|
||||
.card_fw_status0 = 0xe8,
|
||||
.card_fw_status1 = 0xe9,
|
||||
.card_rx_len = 0xea,
|
||||
.card_rx_unit = 0xeb,
|
||||
.io_port_0 = 0xe4,
|
||||
.io_port_1 = 0xe5,
|
||||
.io_port_2 = 0xe6,
|
||||
.int_read_to_clear = true,
|
||||
.host_int_rsr = 0x04,
|
||||
.card_misc_cfg = 0xD8,
|
||||
.fw_dump_ctrl = 0xf0,
|
||||
.fw_dump_start = 0xf1,
|
||||
.fw_dump_end = 0xf8,
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_card_reg btmrvl_reg_8987 = {
|
||||
static const struct btmrvl_sdio_card_reg btmrvl_reg_89xx = {
|
||||
.cfg = 0x00,
|
||||
.host_int_mask = 0x08,
|
||||
.host_intstatus = 0x0c,
|
||||
@ -261,29 +238,6 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8987 = {
|
||||
.fw_dump_end = 0xf8,
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_card_reg btmrvl_reg_8997 = {
|
||||
.cfg = 0x00,
|
||||
.host_int_mask = 0x08,
|
||||
.host_intstatus = 0x0c,
|
||||
.card_status = 0x5c,
|
||||
.sq_read_base_addr_a0 = 0xf8,
|
||||
.sq_read_base_addr_a1 = 0xf9,
|
||||
.card_revision = 0xc8,
|
||||
.card_fw_status0 = 0xe8,
|
||||
.card_fw_status1 = 0xe9,
|
||||
.card_rx_len = 0xea,
|
||||
.card_rx_unit = 0xeb,
|
||||
.io_port_0 = 0xe4,
|
||||
.io_port_1 = 0xe5,
|
||||
.io_port_2 = 0xe6,
|
||||
.int_read_to_clear = true,
|
||||
.host_int_rsr = 0x04,
|
||||
.card_misc_cfg = 0xD8,
|
||||
.fw_dump_ctrl = 0xf0,
|
||||
.fw_dump_start = 0xf1,
|
||||
.fw_dump_end = 0xf8,
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
|
||||
.helper = "mrvl/sd8688_helper.bin",
|
||||
.firmware = "mrvl/sd8688.bin",
|
||||
@ -332,7 +286,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
|
||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8977 = {
|
||||
.helper = NULL,
|
||||
.firmware = "mrvl/sdsd8977_combo_v2.bin",
|
||||
.reg = &btmrvl_reg_8977,
|
||||
.reg = &btmrvl_reg_89xx,
|
||||
.support_pscan_win_report = true,
|
||||
.sd_blksz_fw_dl = 256,
|
||||
.supports_fw_dump = true,
|
||||
@ -341,7 +295,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8977 = {
|
||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8987 = {
|
||||
.helper = NULL,
|
||||
.firmware = "mrvl/sd8987_uapsta.bin",
|
||||
.reg = &btmrvl_reg_8987,
|
||||
.reg = &btmrvl_reg_89xx,
|
||||
.support_pscan_win_report = true,
|
||||
.sd_blksz_fw_dl = 256,
|
||||
.supports_fw_dump = true,
|
||||
@ -350,7 +304,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8987 = {
|
||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = {
|
||||
.helper = NULL,
|
||||
.firmware = "mrvl/sdsd8997_combo_v4.bin",
|
||||
.reg = &btmrvl_reg_8997,
|
||||
.reg = &btmrvl_reg_89xx,
|
||||
.support_pscan_win_report = true,
|
||||
.sd_blksz_fw_dl = 256,
|
||||
.supports_fw_dump = true,
|
||||
|
@ -496,7 +496,7 @@ static void btmtksdio_interrupt(struct sdio_func *func)
|
||||
sdio_claim_host(bdev->func);
|
||||
|
||||
/* Disable interrupt */
|
||||
sdio_writel(func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0);
|
||||
sdio_writel(func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
|
||||
|
||||
int_status = sdio_readl(func, MTK_REG_CHISR, NULL);
|
||||
|
||||
@ -530,7 +530,7 @@ static void btmtksdio_interrupt(struct sdio_func *func)
|
||||
}
|
||||
|
||||
/* Enable interrupt */
|
||||
sdio_writel(func, C_INT_EN_SET, MTK_REG_CHLPCR, 0);
|
||||
sdio_writel(func, C_INT_EN_SET, MTK_REG_CHLPCR, NULL);
|
||||
|
||||
pm_runtime_mark_last_busy(bdev->dev);
|
||||
pm_runtime_put_autosuspend(bdev->dev);
|
||||
|
@ -59,6 +59,7 @@ static struct usb_driver btusb_driver;
|
||||
#define BTUSB_MEDIATEK 0x200000
|
||||
#define BTUSB_WIDEBAND_SPEECH 0x400000
|
||||
#define BTUSB_VALID_LE_STATES 0x800000
|
||||
#define BTUSB_QCA_WCN6855 0x1000000
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
@ -254,24 +255,46 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
/* QCA ROME chipset */
|
||||
{ USB_DEVICE(0x0cf3, 0x535b), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe009), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe010), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe301), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0489, 0xe092), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0489, 0xe09f), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0489, 0xe0a2), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x04ca, 0x301a), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x04ca, 0x3021), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x13d3, 0x3491), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x13d3, 0x3496), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0x535b), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cf3, 0xe009), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cf3, 0xe010), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cf3, 0xe301), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0489, 0xe092), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0489, 0xe09f), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0489, 0xe0a2), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04ca, 0x301a), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04ca, 0x3021), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3491), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3496), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* QCA WCN6855 chipset */
|
||||
{ USB_DEVICE(0x0cf3, 0xe600), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Broadcom BCM2035 */
|
||||
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
|
||||
@ -2338,10 +2361,10 @@ static bool btusb_setup_intel_new_get_fw_name(struct intel_version *ver,
|
||||
|
||||
static int btusb_intel_download_firmware(struct hci_dev *hdev,
|
||||
struct intel_version *ver,
|
||||
struct intel_boot_params *params)
|
||||
struct intel_boot_params *params,
|
||||
u32 *boot_param)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
u32 boot_param;
|
||||
char fwname[64];
|
||||
int err;
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
@ -2479,7 +2502,7 @@ static int btusb_intel_download_firmware(struct hci_dev *hdev,
|
||||
set_bit(BTUSB_DOWNLOADING, &data->flags);
|
||||
|
||||
/* Start firmware downloading and get boot parameter */
|
||||
err = btintel_download_firmware(hdev, fw, &boot_param);
|
||||
err = btintel_download_firmware(hdev, fw, boot_param);
|
||||
if (err < 0) {
|
||||
/* When FW download fails, send Intel Reset to retry
|
||||
* FW download.
|
||||
@ -2561,7 +2584,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = btusb_intel_download_firmware(hdev, &ver, ¶ms);
|
||||
err = btusb_intel_download_firmware(hdev, &ver, ¶ms, &boot_param);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -2896,6 +2919,7 @@ static int btusb_mtk_submit_wmt_recv_urb(struct hci_dev *hdev)
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
kfree(dr);
|
||||
usb_free_urb(urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -3390,6 +3414,27 @@ static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,
|
||||
const bdaddr_t *bdaddr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 buf[6];
|
||||
long ret;
|
||||
|
||||
memcpy(buf, bdaddr, sizeof(bdaddr_t));
|
||||
|
||||
skb = __hci_cmd_sync_ev(hdev, 0xfc14, sizeof(buf), buf,
|
||||
HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "Change address command failed (%ld)", ret);
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define QCA_DFU_PACKET_LEN 4096
|
||||
|
||||
#define QCA_GET_TARGET_VERSION 0x09
|
||||
@ -3409,7 +3454,8 @@ struct qca_version {
|
||||
} __packed;
|
||||
|
||||
struct qca_rampatch_version {
|
||||
__le16 rom_version;
|
||||
__le16 rom_version_high;
|
||||
__le16 rom_version_low;
|
||||
__le16 patch_version;
|
||||
} __packed;
|
||||
|
||||
@ -3421,12 +3467,14 @@ struct qca_device_info {
|
||||
};
|
||||
|
||||
static const struct qca_device_info qca_devices_table[] = {
|
||||
{ 0x00000100, 20, 4, 10 }, /* Rome 1.0 */
|
||||
{ 0x00000101, 20, 4, 10 }, /* Rome 1.1 */
|
||||
{ 0x00000200, 28, 4, 18 }, /* Rome 2.0 */
|
||||
{ 0x00000201, 28, 4, 18 }, /* Rome 2.1 */
|
||||
{ 0x00000300, 28, 4, 18 }, /* Rome 3.0 */
|
||||
{ 0x00000302, 28, 4, 18 }, /* Rome 3.2 */
|
||||
{ 0x00000100, 20, 4, 8 }, /* Rome 1.0 */
|
||||
{ 0x00000101, 20, 4, 8 }, /* Rome 1.1 */
|
||||
{ 0x00000200, 28, 4, 16 }, /* Rome 2.0 */
|
||||
{ 0x00000201, 28, 4, 16 }, /* Rome 2.1 */
|
||||
{ 0x00000300, 28, 4, 16 }, /* Rome 3.0 */
|
||||
{ 0x00000302, 28, 4, 16 }, /* Rome 3.2 */
|
||||
{ 0x00130100, 40, 4, 16 }, /* WCN6855 1.0 */
|
||||
{ 0x00130200, 40, 4, 16 }, /* WCN6855 2.0 */
|
||||
};
|
||||
|
||||
static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request,
|
||||
@ -3528,8 +3576,8 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
|
||||
{
|
||||
struct qca_rampatch_version *rver;
|
||||
const struct firmware *fw;
|
||||
u32 ver_rom, ver_patch;
|
||||
u16 rver_rom, rver_patch;
|
||||
u32 ver_rom, ver_patch, rver_rom;
|
||||
u16 rver_rom_low, rver_rom_high, rver_patch;
|
||||
char fwname[64];
|
||||
int err;
|
||||
|
||||
@ -3548,9 +3596,16 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
|
||||
bt_dev_info(hdev, "using rampatch file: %s", fwname);
|
||||
|
||||
rver = (struct qca_rampatch_version *)(fw->data + info->ver_offset);
|
||||
rver_rom = le16_to_cpu(rver->rom_version);
|
||||
rver_rom_low = le16_to_cpu(rver->rom_version_low);
|
||||
rver_patch = le16_to_cpu(rver->patch_version);
|
||||
|
||||
if (ver_rom & ~0xffffU) {
|
||||
rver_rom_high = le16_to_cpu(rver->rom_version_high);
|
||||
rver_rom = le32_to_cpu(rver_rom_high << 16 | rver_rom_low);
|
||||
} else {
|
||||
rver_rom = rver_rom_low;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "QCA: patch rome 0x%x build 0x%x, "
|
||||
"firmware rome 0x%x build 0x%x",
|
||||
rver_rom, rver_patch, ver_rom, ver_patch);
|
||||
@ -3624,9 +3679,6 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
||||
return err;
|
||||
|
||||
ver_rom = le32_to_cpu(ver.rom_version);
|
||||
/* Don't care about high ROM versions */
|
||||
if (ver_rom & ~0xffffU)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(qca_devices_table); i++) {
|
||||
if (ver_rom == qca_devices_table[i].rom_version)
|
||||
@ -4062,6 +4114,13 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
btusb_check_needs_reset_resume(intf);
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_QCA_WCN6855) {
|
||||
data->setup_on_usb = btusb_setup_qca;
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_wcn6855;
|
||||
hdev->cmd_timeout = btusb_qca_cmd_timeout;
|
||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_AMP) {
|
||||
/* AMP controllers do not support SCO packets */
|
||||
data->isoc = NULL;
|
||||
|
@ -793,8 +793,6 @@ static int h5_serdev_probe(struct serdev_device *serdev)
|
||||
if (!h5)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(HCI_UART_RESET_ON_INIT, &h5->serdev_hu.hdev_flags);
|
||||
|
||||
h5->hu = &h5->serdev_hu;
|
||||
h5->serdev_hu.serdev = serdev;
|
||||
serdev_device_set_drvdata(serdev, h5);
|
||||
|
@ -288,7 +288,7 @@ static irqreturn_t intel_irq(int irq, void *dev_id)
|
||||
|
||||
static int intel_set_power(struct hci_uart *hu, bool powered)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct intel_device *idev;
|
||||
int err = -ENODEV;
|
||||
|
||||
if (!hu->tty->dev)
|
||||
@ -296,10 +296,7 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
|
||||
|
||||
mutex_lock(&intel_device_list_lock);
|
||||
|
||||
list_for_each(p, &intel_device_list) {
|
||||
struct intel_device *idev = list_entry(p, struct intel_device,
|
||||
list);
|
||||
|
||||
list_for_each_entry(idev, &intel_device_list, list) {
|
||||
/* tty device and pdev device should share the same parent
|
||||
* which is the UART port.
|
||||
*/
|
||||
@ -362,19 +359,16 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
|
||||
|
||||
static void intel_busy_work(struct work_struct *work)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct intel_data *intel = container_of(work, struct intel_data,
|
||||
busy_work);
|
||||
struct intel_device *idev;
|
||||
|
||||
if (!intel->hu->tty->dev)
|
||||
return;
|
||||
|
||||
/* Link is busy, delay the suspend */
|
||||
mutex_lock(&intel_device_list_lock);
|
||||
list_for_each(p, &intel_device_list) {
|
||||
struct intel_device *idev = list_entry(p, struct intel_device,
|
||||
list);
|
||||
|
||||
list_for_each_entry(idev, &intel_device_list, list) {
|
||||
if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) {
|
||||
pm_runtime_get(&idev->pdev->dev);
|
||||
pm_runtime_mark_last_busy(&idev->pdev->dev);
|
||||
@ -533,7 +527,7 @@ static int intel_setup(struct hci_uart *hu)
|
||||
struct sk_buff *skb;
|
||||
struct intel_version ver;
|
||||
struct intel_boot_params params;
|
||||
struct list_head *p;
|
||||
struct intel_device *idev;
|
||||
const struct firmware *fw;
|
||||
char fwname[64];
|
||||
u32 boot_param;
|
||||
@ -693,14 +687,11 @@ static int intel_setup(struct hci_uart *hu)
|
||||
case 0x0b: /* SfP */
|
||||
case 0x0c: /* WsP */
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u.sfi",
|
||||
le16_to_cpu(ver.hw_variant),
|
||||
le16_to_cpu(params.dev_revid));
|
||||
ver.hw_variant, le16_to_cpu(params.dev_revid));
|
||||
break;
|
||||
case 0x12: /* ThP */
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u-%u.sfi",
|
||||
le16_to_cpu(ver.hw_variant),
|
||||
le16_to_cpu(ver.hw_revision),
|
||||
le16_to_cpu(ver.fw_revision));
|
||||
ver.hw_variant, ver.hw_revision, ver.fw_revision);
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
|
||||
@ -722,14 +713,11 @@ static int intel_setup(struct hci_uart *hu)
|
||||
case 0x0b: /* SfP */
|
||||
case 0x0c: /* WsP */
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u.ddc",
|
||||
le16_to_cpu(ver.hw_variant),
|
||||
le16_to_cpu(params.dev_revid));
|
||||
ver.hw_variant, le16_to_cpu(params.dev_revid));
|
||||
break;
|
||||
case 0x12: /* ThP */
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u-%u.ddc",
|
||||
le16_to_cpu(ver.hw_variant),
|
||||
le16_to_cpu(ver.hw_revision),
|
||||
le16_to_cpu(ver.fw_revision));
|
||||
ver.hw_variant, ver.hw_revision, ver.fw_revision);
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
|
||||
@ -839,13 +827,11 @@ done:
|
||||
* until further LPM TX notification.
|
||||
*/
|
||||
mutex_lock(&intel_device_list_lock);
|
||||
list_for_each(p, &intel_device_list) {
|
||||
struct intel_device *dev = list_entry(p, struct intel_device,
|
||||
list);
|
||||
list_for_each_entry(idev, &intel_device_list, list) {
|
||||
if (!hu->tty->dev)
|
||||
break;
|
||||
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
|
||||
if (device_may_wakeup(&dev->pdev->dev)) {
|
||||
if (hu->tty->dev->parent == idev->pdev->dev.parent) {
|
||||
if (device_may_wakeup(&idev->pdev->dev)) {
|
||||
set_bit(STATE_LPM_ENABLED, &intel->flags);
|
||||
set_bit(STATE_TX_ACTIVE, &intel->flags);
|
||||
}
|
||||
@ -999,7 +985,7 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count)
|
||||
static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
{
|
||||
struct intel_data *intel = hu->priv;
|
||||
struct list_head *p;
|
||||
struct intel_device *idev;
|
||||
|
||||
BT_DBG("hu %p skb %p", hu, skb);
|
||||
|
||||
@ -1010,10 +996,7 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
* completed before enqueuing any packet.
|
||||
*/
|
||||
mutex_lock(&intel_device_list_lock);
|
||||
list_for_each(p, &intel_device_list) {
|
||||
struct intel_device *idev = list_entry(p, struct intel_device,
|
||||
list);
|
||||
|
||||
list_for_each_entry(idev, &intel_device_list, list) {
|
||||
if (hu->tty->dev->parent == idev->pdev->dev.parent) {
|
||||
pm_runtime_get_sync(&idev->pdev->dev);
|
||||
pm_runtime_mark_last_busy(&idev->pdev->dev);
|
||||
@ -1076,7 +1059,8 @@ static const struct hci_uart_proto intel_proto = {
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id intel_acpi_match[] = {
|
||||
{ "INT33E1", 0 },
|
||||
{ },
|
||||
{ "INT33E3", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, intel_acpi_match);
|
||||
#endif
|
||||
@ -1138,9 +1122,9 @@ static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
|
||||
static const struct acpi_gpio_params host_wake_gpios = { 1, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_hci_intel_gpios[] = {
|
||||
{ "reset-gpios", &reset_gpios, 1 },
|
||||
{ "host-wake-gpios", &host_wake_gpios, 1 },
|
||||
{ },
|
||||
{ "reset-gpios", &reset_gpios, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
|
||||
{ "host-wake-gpios", &host_wake_gpios, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int intel_probe(struct platform_device *pdev)
|
||||
|
@ -538,6 +538,7 @@ static void hci_uart_tty_close(struct tty_struct *tty)
|
||||
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
percpu_up_write(&hu->proto_lock);
|
||||
|
||||
cancel_work_sync(&hu->init_ready);
|
||||
cancel_work_sync(&hu->write_work);
|
||||
|
||||
if (hdev) {
|
||||
|
@ -693,8 +693,6 @@ static int qca_close(struct hci_uart *hu)
|
||||
destroy_workqueue(qca->workqueue);
|
||||
qca->hu = NULL;
|
||||
|
||||
qca_power_shutdown(hu);
|
||||
|
||||
kfree_skb(qca->rx_skb);
|
||||
|
||||
hu->priv = NULL;
|
||||
@ -2007,8 +2005,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
|
||||
if (err) {
|
||||
BT_ERR("Rome serdev registration failed");
|
||||
if (qcadev->susclk)
|
||||
clk_disable_unprepare(qcadev->susclk);
|
||||
clk_disable_unprepare(qcadev->susclk);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@ -2032,8 +2029,9 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
static void qca_serdev_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
|
||||
struct qca_power *power = qcadev->bt_power;
|
||||
|
||||
if (qca_is_wcn399x(qcadev->btsoc_type))
|
||||
if (qca_is_wcn399x(qcadev->btsoc_type) && power->vregs_on)
|
||||
qca_power_shutdown(&qcadev->serdev_hu);
|
||||
else if (qcadev->susclk)
|
||||
clk_disable_unprepare(qcadev->susclk);
|
||||
|
@ -113,8 +113,22 @@ static int hci_uart_flush(struct hci_dev *hdev)
|
||||
/* Initialize device */
|
||||
static int hci_uart_open(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
int err;
|
||||
|
||||
BT_DBG("%s %p", hdev->name, hdev);
|
||||
|
||||
/* When Quirk HCI_QUIRK_NON_PERSISTENT_SETUP is set by
|
||||
* driver, BT SoC is completely turned OFF during
|
||||
* BT OFF. Upon next BT ON UART port should be opened.
|
||||
*/
|
||||
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
|
||||
err = serdev_device_open(hu->serdev);
|
||||
if (err)
|
||||
return err;
|
||||
set_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
}
|
||||
|
||||
/* Undo clearing this from hci_uart_close() */
|
||||
hdev->flush = hci_uart_flush;
|
||||
|
||||
@ -124,11 +138,25 @@ static int hci_uart_open(struct hci_dev *hdev)
|
||||
/* Close device */
|
||||
static int hci_uart_close(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
|
||||
BT_DBG("hdev %p", hdev);
|
||||
|
||||
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
|
||||
return 0;
|
||||
|
||||
hci_uart_flush(hdev);
|
||||
hdev->flush = NULL;
|
||||
|
||||
/* When QUIRK HCI_QUIRK_NON_PERSISTENT_SETUP is set by driver,
|
||||
* BT SOC is completely powered OFF during BT OFF, holding port
|
||||
* open may drain the battery.
|
||||
*/
|
||||
if (test_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks)) {
|
||||
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
serdev_device_close(hu->serdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -354,7 +382,7 @@ void hci_uart_unregister_device(struct hci_uart *hu)
|
||||
{
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
|
||||
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
cancel_work_sync(&hu->init_ready);
|
||||
if (test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
@ -362,6 +390,10 @@ void hci_uart_unregister_device(struct hci_uart *hu)
|
||||
cancel_work_sync(&hu->write_work);
|
||||
|
||||
hu->proto->close(hu);
|
||||
serdev_device_close(hu->serdev);
|
||||
|
||||
if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
|
||||
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
serdev_device_close(hu->serdev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hci_uart_unregister_device);
|
||||
|
@ -484,6 +484,9 @@ struct hci_dev {
|
||||
enum suspended_state suspend_state;
|
||||
bool scanning_paused;
|
||||
bool suspended;
|
||||
u8 wake_reason;
|
||||
bdaddr_t wake_addr;
|
||||
u8 wake_addr_type;
|
||||
|
||||
wait_queue_head_t suspend_wait_q;
|
||||
DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS);
|
||||
@ -1750,6 +1753,9 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||
void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||
u8 addr_type, s8 rssi, u8 *name, u8 name_len);
|
||||
void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
|
||||
void mgmt_suspending(struct hci_dev *hdev, u8 state);
|
||||
void mgmt_resuming(struct hci_dev *hdev, u8 reason, bdaddr_t *bdaddr,
|
||||
u8 addr_type);
|
||||
bool mgmt_powering_down(struct hci_dev *hdev);
|
||||
void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent);
|
||||
void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk, bool persistent);
|
||||
|
@ -665,6 +665,8 @@ struct l2cap_ops {
|
||||
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
|
||||
unsigned long hdr_len,
|
||||
unsigned long len, int nb);
|
||||
int (*filter) (struct l2cap_chan * chan,
|
||||
struct sk_buff *skb);
|
||||
};
|
||||
|
||||
struct l2cap_conn {
|
||||
|
@ -572,6 +572,8 @@ struct mgmt_rp_add_advertising {
|
||||
#define MGMT_ADV_FLAG_SEC_1M BIT(7)
|
||||
#define MGMT_ADV_FLAG_SEC_2M BIT(8)
|
||||
#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_FLAG_SEC_MASK (MGMT_ADV_FLAG_SEC_1M | MGMT_ADV_FLAG_SEC_2M | \
|
||||
MGMT_ADV_FLAG_SEC_CODED)
|
||||
@ -840,6 +842,7 @@ struct mgmt_ev_device_connected {
|
||||
#define MGMT_DEV_DISCONN_LOCAL_HOST 0x02
|
||||
#define MGMT_DEV_DISCONN_REMOTE 0x03
|
||||
#define MGMT_DEV_DISCONN_AUTH_FAILURE 0x04
|
||||
#define MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND 0x05
|
||||
|
||||
#define MGMT_EV_DEVICE_DISCONNECTED 0x000C
|
||||
struct mgmt_ev_device_disconnected {
|
||||
@ -1028,3 +1031,18 @@ struct mgmt_ev_adv_monitor_added {
|
||||
struct mgmt_ev_adv_monitor_removed {
|
||||
__le16 monitor_handle;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CONTROLLER_SUSPEND 0x002d
|
||||
struct mgmt_ev_controller_suspend {
|
||||
__u8 suspend_state;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CONTROLLER_RESUME 0x002e
|
||||
struct mgmt_ev_controller_resume {
|
||||
__u8 wake_reason;
|
||||
struct mgmt_addr_info addr;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_WAKE_REASON_NON_BT_WAKE 0x0
|
||||
#define MGMT_WAKE_REASON_UNEXPECTED 0x1
|
||||
#define MGMT_WAKE_REASON_REMOTE_WAKE 0x2
|
||||
|
@ -64,7 +64,6 @@ source "net/bluetooth/hidp/Kconfig"
|
||||
config BT_HS
|
||||
bool "Bluetooth High Speed (HS) features"
|
||||
depends on BT_BREDR
|
||||
default y
|
||||
help
|
||||
Bluetooth High Speed includes support for off-loading
|
||||
Bluetooth connections via 802.11 (wifi) physical layer
|
||||
|
@ -226,6 +226,9 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
struct a2mp_info_req req;
|
||||
|
||||
found = true;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.id = cl->id;
|
||||
a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr),
|
||||
sizeof(req), &req);
|
||||
@ -305,6 +308,8 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
if (!hdev || hdev->dev_type != HCI_AMP) {
|
||||
struct a2mp_info_rsp rsp;
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
rsp.id = req->id;
|
||||
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
||||
|
||||
@ -348,6 +353,8 @@ static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
if (!ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.id = rsp->id;
|
||||
a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req),
|
||||
&req);
|
||||
@ -376,6 +383,8 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
struct a2mp_amp_assoc_rsp rsp;
|
||||
rsp.id = req->id;
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
if (tmp) {
|
||||
rsp.status = A2MP_STATUS_COLLISION_OCCURED;
|
||||
amp_mgr_put(tmp);
|
||||
@ -464,7 +473,6 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
struct a2mp_cmd *hdr)
|
||||
{
|
||||
struct a2mp_physlink_req *req = (void *) skb->data;
|
||||
|
||||
struct a2mp_physlink_rsp rsp;
|
||||
struct hci_dev *hdev;
|
||||
struct hci_conn *hcon;
|
||||
@ -475,6 +483,8 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
|
||||
BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id);
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
rsp.local_id = req->remote_id;
|
||||
rsp.remote_id = req->local_id;
|
||||
|
||||
@ -553,6 +563,8 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
|
||||
BT_DBG("local_id %d remote_id %d", req->local_id, req->remote_id);
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
rsp.local_id = req->remote_id;
|
||||
rsp.remote_id = req->local_id;
|
||||
rsp.status = A2MP_STATUS_SUCCESS;
|
||||
@ -675,6 +687,8 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
if (err) {
|
||||
struct a2mp_cmd_rej rej;
|
||||
|
||||
memset(&rej, 0, sizeof(rej));
|
||||
|
||||
rej.reason = cpu_to_le16(0);
|
||||
hdr = (void *) skb->data;
|
||||
|
||||
@ -898,6 +912,8 @@ void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
|
||||
|
||||
BT_DBG("%s mgr %p", hdev->name, mgr);
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
rsp.id = hdev->id;
|
||||
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
||||
|
||||
@ -995,6 +1011,8 @@ void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status)
|
||||
if (!mgr)
|
||||
return;
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
hs_hcon = hci_conn_hash_lookup_state(hdev, AMP_LINK, BT_CONNECT);
|
||||
if (!hs_hcon) {
|
||||
rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
|
||||
@ -1027,6 +1045,8 @@ void a2mp_discover_amp(struct l2cap_chan *chan)
|
||||
|
||||
mgr->bredr_chan = chan;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
|
||||
req.ext_feat = 0;
|
||||
a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req);
|
||||
|
@ -2963,7 +2963,7 @@ int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
sizeof(adv_instance->scan_rsp_data));
|
||||
} else {
|
||||
if (hdev->adv_instance_cnt >= hdev->le_num_of_adv_sets ||
|
||||
instance < 1 || instance > HCI_MAX_ADV_INSTANCES)
|
||||
instance < 1 || instance > hdev->le_num_of_adv_sets)
|
||||
return -EOVERFLOW;
|
||||
|
||||
adv_instance = kzalloc(sizeof(*adv_instance), GFP_KERNEL);
|
||||
@ -3061,6 +3061,7 @@ static int free_adv_monitor(int id, void *ptr, void *data)
|
||||
|
||||
idr_remove(&hdev->adv_monitors_idr, monitor->handle);
|
||||
hci_free_adv_monitor(monitor);
|
||||
hdev->adv_monitors_cnt--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3077,6 +3078,7 @@ int hci_remove_adv_monitor(struct hci_dev *hdev, u16 handle)
|
||||
|
||||
idr_remove(&hdev->adv_monitors_idr, monitor->handle);
|
||||
hci_free_adv_monitor(monitor);
|
||||
hdev->adv_monitors_cnt--;
|
||||
} else {
|
||||
/* Remove all monitors if handle is 0. */
|
||||
idr_for_each(&hdev->adv_monitors_idr, &free_adv_monitor, hdev);
|
||||
@ -3442,6 +3444,16 @@ void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_suspend_clear_tasks(struct hci_dev *hdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < __SUSPEND_NUM_TASKS; i++)
|
||||
clear_bit(i, hdev->suspend_tasks);
|
||||
|
||||
wake_up(&hdev->suspend_wait_q);
|
||||
}
|
||||
|
||||
static int hci_suspend_wait_event(struct hci_dev *hdev)
|
||||
{
|
||||
#define WAKE_COND \
|
||||
@ -3487,12 +3499,24 @@ static int hci_change_suspend_state(struct hci_dev *hdev,
|
||||
return hci_suspend_wait_event(hdev);
|
||||
}
|
||||
|
||||
static void hci_clear_wake_reason(struct hci_dev *hdev)
|
||||
{
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hdev->wake_reason = 0;
|
||||
bacpy(&hdev->wake_addr, BDADDR_ANY);
|
||||
hdev->wake_addr_type = 0;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct hci_dev *hdev =
|
||||
container_of(nb, struct hci_dev, suspend_notifier);
|
||||
int ret = 0;
|
||||
u8 state = BT_RUNNING;
|
||||
|
||||
/* If powering down, wait for completion. */
|
||||
if (mgmt_powering_down(hdev)) {
|
||||
@ -3513,15 +3537,27 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
|
||||
* - Second, program event filter/whitelist and enable scan
|
||||
*/
|
||||
ret = hci_change_suspend_state(hdev, BT_SUSPEND_DISCONNECT);
|
||||
if (!ret)
|
||||
state = BT_SUSPEND_DISCONNECT;
|
||||
|
||||
/* Only configure whitelist if disconnect succeeded and wake
|
||||
* isn't being prevented.
|
||||
*/
|
||||
if (!ret && !(hdev->prevent_wake && hdev->prevent_wake(hdev)))
|
||||
if (!ret && !(hdev->prevent_wake && hdev->prevent_wake(hdev))) {
|
||||
ret = hci_change_suspend_state(hdev,
|
||||
BT_SUSPEND_CONFIGURE_WAKE);
|
||||
if (!ret)
|
||||
state = BT_SUSPEND_CONFIGURE_WAKE;
|
||||
}
|
||||
|
||||
hci_clear_wake_reason(hdev);
|
||||
mgmt_suspending(hdev, state);
|
||||
|
||||
} else if (action == PM_POST_SUSPEND) {
|
||||
ret = hci_change_suspend_state(hdev, BT_RUNNING);
|
||||
|
||||
mgmt_resuming(hdev, hdev->wake_reason, &hdev->wake_addr,
|
||||
hdev->wake_addr_type);
|
||||
}
|
||||
|
||||
done:
|
||||
@ -3784,6 +3820,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
||||
|
||||
cancel_work_sync(&hdev->power_on);
|
||||
|
||||
hci_suspend_clear_tasks(hdev);
|
||||
unregister_pm_notifier(&hdev->suspend_notifier);
|
||||
cancel_work_sync(&hdev->suspend_prepare);
|
||||
|
||||
|
@ -2569,7 +2569,6 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_conn_complete *ev = (void *) skb->data;
|
||||
struct inquiry_entry *ie;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
@ -2578,13 +2577,19 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
|
||||
if (!conn) {
|
||||
/* Connection may not exist if auto-connected. Check the inquiry
|
||||
* cache to see if we've already discovered this bdaddr before.
|
||||
* If found and link is an ACL type, create a connection class
|
||||
/* Connection may not exist if auto-connected. Check the bredr
|
||||
* allowlist to see if this device is allowed to auto connect.
|
||||
* If link is an ACL type, create a connection class
|
||||
* automatically.
|
||||
*
|
||||
* Auto-connect will only occur if the event filter is
|
||||
* programmed with a given address. Right now, event filter is
|
||||
* only used during suspend.
|
||||
*/
|
||||
ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
|
||||
if (ie && ev->link_type == ACL_LINK) {
|
||||
if (ev->link_type == ACL_LINK &&
|
||||
hci_bdaddr_list_lookup_with_flags(&hdev->whitelist,
|
||||
&ev->bdaddr,
|
||||
BDADDR_BREDR)) {
|
||||
conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr,
|
||||
HCI_ROLE_SLAVE);
|
||||
if (!conn) {
|
||||
@ -6012,6 +6017,75 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hci_store_wake_reason(struct hci_dev *hdev, u8 event,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_advertising_info *adv;
|
||||
struct hci_ev_le_direct_adv_info *direct_adv;
|
||||
struct hci_ev_le_ext_adv_report *ext_adv;
|
||||
const struct hci_ev_conn_complete *conn_complete = (void *)skb->data;
|
||||
const struct hci_ev_conn_request *conn_request = (void *)skb->data;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* If we are currently suspended and this is the first BT event seen,
|
||||
* save the wake reason associated with the event.
|
||||
*/
|
||||
if (!hdev->suspended || hdev->wake_reason)
|
||||
goto unlock;
|
||||
|
||||
/* Default to remote wake. Values for wake_reason are documented in the
|
||||
* Bluez mgmt api docs.
|
||||
*/
|
||||
hdev->wake_reason = MGMT_WAKE_REASON_REMOTE_WAKE;
|
||||
|
||||
/* Once configured for remote wakeup, we should only wake up for
|
||||
* reconnections. It's useful to see which device is waking us up so
|
||||
* keep track of the bdaddr of the connection event that woke us up.
|
||||
*/
|
||||
if (event == HCI_EV_CONN_REQUEST) {
|
||||
bacpy(&hdev->wake_addr, &conn_complete->bdaddr);
|
||||
hdev->wake_addr_type = BDADDR_BREDR;
|
||||
} else if (event == HCI_EV_CONN_COMPLETE) {
|
||||
bacpy(&hdev->wake_addr, &conn_request->bdaddr);
|
||||
hdev->wake_addr_type = BDADDR_BREDR;
|
||||
} else if (event == HCI_EV_LE_META) {
|
||||
struct hci_ev_le_meta *le_ev = (void *)skb->data;
|
||||
u8 subevent = le_ev->subevent;
|
||||
u8 *ptr = &skb->data[sizeof(*le_ev)];
|
||||
u8 num_reports = *ptr;
|
||||
|
||||
if ((subevent == HCI_EV_LE_ADVERTISING_REPORT ||
|
||||
subevent == HCI_EV_LE_DIRECT_ADV_REPORT ||
|
||||
subevent == HCI_EV_LE_EXT_ADV_REPORT) &&
|
||||
num_reports) {
|
||||
adv = (void *)(ptr + 1);
|
||||
direct_adv = (void *)(ptr + 1);
|
||||
ext_adv = (void *)(ptr + 1);
|
||||
|
||||
switch (subevent) {
|
||||
case HCI_EV_LE_ADVERTISING_REPORT:
|
||||
bacpy(&hdev->wake_addr, &adv->bdaddr);
|
||||
hdev->wake_addr_type = adv->bdaddr_type;
|
||||
break;
|
||||
case HCI_EV_LE_DIRECT_ADV_REPORT:
|
||||
bacpy(&hdev->wake_addr, &direct_adv->bdaddr);
|
||||
hdev->wake_addr_type = direct_adv->bdaddr_type;
|
||||
break;
|
||||
case HCI_EV_LE_EXT_ADV_REPORT:
|
||||
bacpy(&hdev->wake_addr, &ext_adv->bdaddr);
|
||||
hdev->wake_addr_type = ext_adv->bdaddr_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_event_hdr *hdr = (void *) skb->data;
|
||||
@ -6045,6 +6119,9 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
skb_pull(skb, HCI_EVENT_HDR_SIZE);
|
||||
|
||||
/* Store wake reason if we're suspended */
|
||||
hci_store_wake_reason(hdev, event, skb);
|
||||
|
||||
switch (event) {
|
||||
case HCI_EV_INQUIRY_COMPLETE:
|
||||
hci_inquiry_complete_evt(hdev, skb);
|
||||
|
@ -1027,6 +1027,9 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
|
||||
} else if (hci_is_le_conn_scanning(hdev)) {
|
||||
window = hdev->le_scan_window_connect;
|
||||
interval = hdev->le_scan_int_connect;
|
||||
} else if (hci_is_adv_monitoring(hdev)) {
|
||||
window = hdev->le_scan_window_adv_monitor;
|
||||
interval = hdev->le_scan_int_adv_monitor;
|
||||
} else {
|
||||
window = hdev->le_scan_window;
|
||||
interval = hdev->le_scan_interval;
|
||||
@ -1111,6 +1114,53 @@ static void hci_req_config_le_suspend_scan(struct hci_request *req)
|
||||
set_bit(SUSPEND_SCAN_ENABLE, req->hdev->suspend_tasks);
|
||||
}
|
||||
|
||||
static void cancel_adv_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
if (hdev->adv_instance_timeout) {
|
||||
hdev->adv_instance_timeout = 0;
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static void hci_suspend_adv_instances(struct hci_request *req)
|
||||
{
|
||||
bt_dev_dbg(req->hdev, "Suspending advertising instances");
|
||||
|
||||
/* Call to disable any advertisements active on the controller.
|
||||
* This will succeed even if no advertisements are configured.
|
||||
*/
|
||||
__hci_req_disable_advertising(req);
|
||||
|
||||
/* If we are using software rotation, pause the loop */
|
||||
if (!ext_adv_capable(req->hdev))
|
||||
cancel_adv_timeout(req->hdev);
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static void hci_resume_adv_instances(struct hci_request *req)
|
||||
{
|
||||
struct adv_info *adv;
|
||||
|
||||
bt_dev_dbg(req->hdev, "Resuming advertising instances");
|
||||
|
||||
if (ext_adv_capable(req->hdev)) {
|
||||
/* Call for each tracked instance to be re-enabled */
|
||||
list_for_each_entry(adv, &req->hdev->adv_instances, list) {
|
||||
__hci_req_enable_ext_advertising(req,
|
||||
adv->instance);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Schedule for most recent instance to be restarted and begin
|
||||
* the software rotation loop
|
||||
*/
|
||||
__hci_req_schedule_adv_instance(req,
|
||||
req->hdev->cur_adv_instance,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -1153,7 +1203,7 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
|
||||
hdev->discovery_paused = true;
|
||||
hdev->discovery_old_state = old_state;
|
||||
|
||||
/* Stop advertising */
|
||||
/* Stop directed advertising */
|
||||
old_state = hci_dev_test_flag(hdev, HCI_ADVERTISING);
|
||||
if (old_state) {
|
||||
set_bit(SUSPEND_PAUSE_ADVERTISING, hdev->suspend_tasks);
|
||||
@ -1162,6 +1212,10 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
|
||||
&hdev->discov_off, 0);
|
||||
}
|
||||
|
||||
/* Pause other advertisements */
|
||||
if (hdev->adv_instance_cnt)
|
||||
hci_suspend_adv_instances(&req);
|
||||
|
||||
hdev->advertising_paused = true;
|
||||
hdev->advertising_old_state = old_state;
|
||||
/* Disable page scan */
|
||||
@ -1212,7 +1266,7 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
|
||||
/* Reset passive/background scanning to normal */
|
||||
hci_req_config_le_suspend_scan(&req);
|
||||
|
||||
/* Unpause advertising */
|
||||
/* Unpause directed advertising */
|
||||
hdev->advertising_paused = false;
|
||||
if (hdev->advertising_old_state) {
|
||||
set_bit(SUSPEND_UNPAUSE_ADVERTISING,
|
||||
@ -1223,6 +1277,10 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
|
||||
hdev->advertising_old_state = 0;
|
||||
}
|
||||
|
||||
/* Resume other advertisements */
|
||||
if (hdev->adv_instance_cnt)
|
||||
hci_resume_adv_instances(&req);
|
||||
|
||||
/* Unpause discovery */
|
||||
hdev->discovery_paused = false;
|
||||
if (hdev->discovery_old_state != DISCOVERY_STOPPED &&
|
||||
@ -1533,11 +1591,14 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
if (instance)
|
||||
/* 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))
|
||||
len = create_instance_scan_rsp_data(hdev, instance,
|
||||
cp.data);
|
||||
else
|
||||
len = create_default_scan_rsp_data(hdev, cp.data);
|
||||
len = 0;
|
||||
|
||||
if (hdev->scan_rsp_data_len == len &&
|
||||
!memcmp(cp.data, hdev->scan_rsp_data, len))
|
||||
@ -1824,7 +1885,13 @@ int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
||||
if (use_rpa) {
|
||||
int to;
|
||||
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
/* If Controller supports LL Privacy use own address type is
|
||||
* 0x03
|
||||
*/
|
||||
if (use_ll_privacy(hdev))
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM_RESOLVED;
|
||||
else
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
|
||||
if (adv_instance) {
|
||||
if (!adv_instance->rpa_expired &&
|
||||
@ -2183,14 +2250,6 @@ int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cancel_adv_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
if (hdev->adv_instance_timeout) {
|
||||
hdev->adv_instance_timeout = 0;
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
}
|
||||
}
|
||||
|
||||
/* For a single instance:
|
||||
* - force == true: The instance will be removed even when its remaining
|
||||
* lifetime is not zero.
|
||||
|
@ -7301,9 +7301,10 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if ((chan->mode == L2CAP_MODE_ERTM ||
|
||||
chan->mode == L2CAP_MODE_STREAMING) && sk_filter(chan->data, skb))
|
||||
goto drop;
|
||||
if (chan->ops->filter) {
|
||||
if (chan->ops->filter(chan, skb))
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (!control->sframe) {
|
||||
int err;
|
||||
|
@ -1521,8 +1521,6 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
|
||||
|
||||
parent = bt_sk(sk)->parent;
|
||||
|
||||
sock_set_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
switch (chan->state) {
|
||||
case BT_OPEN:
|
||||
case BT_BOUND:
|
||||
@ -1549,8 +1547,11 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
/* Only zap after cleanup to avoid use after free race */
|
||||
sock_set_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
}
|
||||
|
||||
static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
|
||||
@ -1663,6 +1664,19 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan)
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
|
||||
static int l2cap_sock_filter(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
{
|
||||
struct sock *sk = chan->data;
|
||||
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_ERTM:
|
||||
case L2CAP_MODE_STREAMING:
|
||||
return sk_filter(sk, skb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct l2cap_ops l2cap_chan_ops = {
|
||||
.name = "L2CAP Socket Interface",
|
||||
.new_connection = l2cap_sock_new_connection_cb,
|
||||
@ -1678,6 +1692,7 @@ static const struct l2cap_ops l2cap_chan_ops = {
|
||||
.get_sndtimeo = l2cap_sock_get_sndtimeo_cb,
|
||||
.get_peer_pid = l2cap_sock_get_peer_pid_cb,
|
||||
.alloc_skb = l2cap_sock_alloc_skb_cb,
|
||||
.filter = l2cap_sock_filter,
|
||||
};
|
||||
|
||||
static void l2cap_sock_destruct(struct sock *sk)
|
||||
|
@ -163,6 +163,8 @@ static const u16 mgmt_events[] = {
|
||||
MGMT_EV_PHY_CONFIGURATION_CHANGED,
|
||||
MGMT_EV_EXP_FEATURE_CHANGED,
|
||||
MGMT_EV_DEVICE_FLAGS_CHANGED,
|
||||
MGMT_EV_CONTROLLER_SUSPEND,
|
||||
MGMT_EV_CONTROLLER_RESUME,
|
||||
};
|
||||
|
||||
static const u16 mgmt_untrusted_commands[] = {
|
||||
@ -782,7 +784,8 @@ static u32 get_supported_settings(struct hci_dev *hdev)
|
||||
|
||||
if (lmp_ssp_capable(hdev)) {
|
||||
settings |= MGMT_SETTING_SSP;
|
||||
settings |= MGMT_SETTING_HS;
|
||||
if (IS_ENABLED(CONFIG_BT_HS))
|
||||
settings |= MGMT_SETTING_HS;
|
||||
}
|
||||
|
||||
if (lmp_sc_capable(hdev))
|
||||
@ -1815,6 +1818,10 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
|
||||
bt_dev_dbg(hdev, "sock %p", sk);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_BT_HS))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
status = mgmt_bredr_support(hdev);
|
||||
if (status)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status);
|
||||
@ -4157,7 +4164,7 @@ static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
|
||||
{
|
||||
struct adv_monitor *monitor = NULL;
|
||||
struct mgmt_rp_read_adv_monitor_features *rp = NULL;
|
||||
int handle;
|
||||
int handle, err;
|
||||
size_t rp_size = 0;
|
||||
__u32 supported = 0;
|
||||
__u16 num_handles = 0;
|
||||
@ -4192,9 +4199,13 @@ static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
|
||||
if (num_handles)
|
||||
memcpy(&rp->handles, &handles, (num_handles * sizeof(u16)));
|
||||
|
||||
return mgmt_cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_READ_ADV_MONITOR_FEATURES,
|
||||
MGMT_STATUS_SUCCESS, rp, rp_size);
|
||||
err = mgmt_cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_READ_ADV_MONITOR_FEATURES,
|
||||
MGMT_STATUS_SUCCESS, rp, rp_size);
|
||||
|
||||
kfree(rp);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
|
||||
@ -7202,6 +7213,8 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev)
|
||||
|
||||
if (ext_adv_capable(hdev)) {
|
||||
flags |= MGMT_ADV_FLAG_SEC_1M;
|
||||
flags |= MGMT_ADV_FLAG_HW_OFFLOAD;
|
||||
flags |= MGMT_ADV_FLAG_CAN_SET_TX_POWER;
|
||||
|
||||
if (hdev->le_features[1] & HCI_LE_PHY_2M)
|
||||
flags |= MGMT_ADV_FLAG_SEC_2M;
|
||||
@ -7250,7 +7263,7 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
rp->supported_flags = cpu_to_le32(supported_flags);
|
||||
rp->max_adv_data_len = HCI_MAX_AD_LENGTH;
|
||||
rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH;
|
||||
rp->max_instances = HCI_MAX_ADV_INSTANCES;
|
||||
rp->max_instances = hdev->le_num_of_adv_sets;
|
||||
rp->num_instances = hdev->adv_instance_cnt;
|
||||
|
||||
instance = rp->instance;
|
||||
@ -7446,7 +7459,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (cp->instance < 1 || cp->instance > HCI_MAX_ADV_INSTANCES)
|
||||
if (cp->instance < 1 || cp->instance > hdev->le_num_of_adv_sets)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
@ -7699,7 +7712,7 @@ static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev,
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
|
||||
MGMT_STATUS_REJECTED);
|
||||
|
||||
if (cp->instance < 1 || cp->instance > HCI_MAX_ADV_INSTANCES)
|
||||
if (cp->instance < 1 || cp->instance > hdev->le_num_of_adv_sets)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
@ -8262,6 +8275,10 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
ev.addr.type = link_to_bdaddr(link_type, addr_type);
|
||||
ev.reason = reason;
|
||||
|
||||
/* Report disconnects due to suspend */
|
||||
if (hdev->suspended)
|
||||
ev.reason = MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND;
|
||||
|
||||
mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
|
||||
|
||||
if (sk)
|
||||
@ -8868,6 +8885,30 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
|
||||
mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
|
||||
void mgmt_suspending(struct hci_dev *hdev, u8 state)
|
||||
{
|
||||
struct mgmt_ev_controller_suspend ev;
|
||||
|
||||
ev.suspend_state = state;
|
||||
mgmt_event(MGMT_EV_CONTROLLER_SUSPEND, hdev, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
|
||||
void mgmt_resuming(struct hci_dev *hdev, u8 reason, bdaddr_t *bdaddr,
|
||||
u8 addr_type)
|
||||
{
|
||||
struct mgmt_ev_controller_resume ev;
|
||||
|
||||
ev.wake_reason = reason;
|
||||
if (bdaddr) {
|
||||
bacpy(&ev.addr.bdaddr, bdaddr);
|
||||
ev.addr.type = addr_type;
|
||||
} else {
|
||||
memset(&ev.addr, 0, sizeof(ev.addr));
|
||||
}
|
||||
|
||||
mgmt_event(MGMT_EV_CONTROLLER_RESUME, hdev, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
|
||||
static struct hci_mgmt_chan chan = {
|
||||
.channel = HCI_CHANNEL_CONTROL,
|
||||
.handler_count = ARRAY_SIZE(mgmt_handlers),
|
||||
|
@ -1001,6 +1001,12 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case BT_SNDMTU:
|
||||
case BT_RCVMTU:
|
||||
if (put_user(sco_pi(sk)->conn->mtu, (u32 __user *)optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user