diff options
author | David S. Miller <davem@davemloft.net> | 2015-04-09 18:31:50 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-04-09 18:31:50 -0400 |
commit | c3d0dac6935e388547d7b7721e80b9c996eb90a1 (patch) | |
tree | ebb93d6dcc9c32bdd660b75f9904becbeb774fd1 | |
parent | b52e69217b5a02469f8431934f59c0d7103dd32f (diff) | |
parent | 23310f6f3a8f30f4b6794e941763da4708f7471a (diff) | |
download | linux-stable-c3d0dac6935e388547d7b7721e80b9c996eb90a1.tar.gz linux-stable-c3d0dac6935e388547d7b7721e80b9c996eb90a1.tar.bz2 linux-stable-c3d0dac6935e388547d7b7721e80b9c996eb90a1.zip |
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says:
====================
pull request: bluetooth-next 2015-04-09
We've had enough new patches during the past week (especially from
Marcel) that it'd be good to still get these queued for 4.1.
The majority of the changes are from Marcel with lots of cleanup &
refactoring patches for the HCI UART driver. Marcel also split out some
Broadcom & Intel vendor specific functionality into two new btintel &
btbcm modules.
In addition to the HCI driver changes there's the completion of our
local OOB data interface for pairing, added support for requesting
remote LE features when connecting, as well as a couple of minor fixes
for mac802154.
Please let me know if there are any issues pulling. Thanks.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/bluetooth/Kconfig | 41 | ||||
-rw-r--r-- | drivers/bluetooth/Makefile | 4 | ||||
-rw-r--r-- | drivers/bluetooth/btbcm.c | 387 | ||||
-rw-r--r-- | drivers/bluetooth/btbcm.h | 54 | ||||
-rw-r--r-- | drivers/bluetooth/btintel.c | 101 | ||||
-rw-r--r-- | drivers/bluetooth/btintel.h | 89 | ||||
-rw-r--r-- | drivers/bluetooth/btusb.c | 469 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ath.c | 49 | ||||
-rw-r--r-- | drivers/bluetooth/hci_bcm.c | 153 | ||||
-rw-r--r-- | drivers/bluetooth/hci_bcsp.c | 18 | ||||
-rw-r--r-- | drivers/bluetooth/hci_h4.c | 132 | ||||
-rw-r--r-- | drivers/bluetooth/hci_h5.c | 16 | ||||
-rw-r--r-- | drivers/bluetooth/hci_intel.c | 31 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ldisc.c | 86 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ll.c | 16 | ||||
-rw-r--r-- | drivers/bluetooth/hci_uart.h | 55 | ||||
-rw-r--r-- | drivers/net/ieee802154/at86rf230.c | 2 | ||||
-rw-r--r-- | include/net/bluetooth/hci.h | 19 | ||||
-rw-r--r-- | include/net/bluetooth/hci_core.h | 3 | ||||
-rw-r--r-- | include/net/mac802154.h | 4 | ||||
-rw-r--r-- | net/bluetooth/hci_core.c | 148 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 107 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 154 |
23 files changed, 1481 insertions, 657 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 364f080768d0..ed5c2738bea2 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -2,9 +2,17 @@ menu "Bluetooth device drivers" depends on BT +config BT_INTEL + tristate + +config BT_BCM + tristate + select FW_LOADER + config BT_HCIBTUSB tristate "HCI USB driver" depends on USB + select BT_INTEL help Bluetooth HCI USB driver. This driver is required if you want to use Bluetooth devices with @@ -13,6 +21,17 @@ config BT_HCIBTUSB Say Y here to compile support for Bluetooth USB devices into the kernel or say M to compile it as module (btusb). +config BT_HCIBTUSB_BCM + bool "Broadcom protocol support" + depends on BT_HCIBTUSB + select BT_BCM + default y + help + The Broadcom protocol support enables firmware and patchram + download support for Broadcom Bluetooth controllers. + + Say Y here to compile support for Broadcom protocol. + config BT_HCIBTSDIO tristate "HCI SDIO driver" depends on MMC @@ -62,6 +81,7 @@ config BT_HCIUART_BCSP config BT_HCIUART_ATH3K bool "Atheros AR300x serial support" depends on BT_HCIUART + select BT_HCIUART_H4 help HCIATH3K (HCI Atheros AR300x) is a serial protocol for communication between host and Atheros AR300x Bluetooth devices. @@ -94,6 +114,27 @@ config BT_HCIUART_3WIRE Say Y here to compile support for Three-wire UART protocol. +config BT_HCIUART_INTEL + bool "Intel protocol support" + depends on BT_HCIUART + select BT_INTEL + help + The Intel protocol support enables Bluetooth HCI over serial + port interface for Intel Bluetooth controllers. + + Say Y here to compile support for Intel protocol. + +config BT_HCIUART_BCM + bool "Broadcom protocol support" + depends on BT_HCIUART + select BT_HCIUART_H4 + select BT_BCM + help + The Broadcom protocol support enables Bluetooth HCI over serial + port interface for Broadcom Bluetooth controllers. + + Say Y here to compile support for Broadcom protocol. + config BT_HCIBCM203X tristate "HCI BCM203x USB driver" depends on USB diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 9fe8a875a827..dd0d9c40b999 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -15,10 +15,12 @@ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o obj-$(CONFIG_BT_HCIBTUSB) += btusb.o obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o +obj-$(CONFIG_BT_INTEL) += btintel.o obj-$(CONFIG_BT_ATH3K) += ath3k.o obj-$(CONFIG_BT_MRVL) += btmrvl.o obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o obj-$(CONFIG_BT_WILINK) += btwilink.o +obj-$(CONFIG_BT_BCM) += btbcm.o btmrvl-y := btmrvl_main.o btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o @@ -29,6 +31,8 @@ hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o +hci_uart-$(CONFIG_BT_HCIUART_INTEL) += hci_intel.o +hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o hci_uart-objs := $(hci_uart-y) ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c new file mode 100644 index 000000000000..d0741f3ed7ec --- /dev/null +++ b/drivers/bluetooth/btbcm.c @@ -0,0 +1,387 @@ +/* + * + * Bluetooth support for Broadcom devices + * + * Copyright (C) 2015 Intel Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/firmware.h> +#include <asm/unaligned.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "btbcm.h" + +#define VERSION "0.1" + +#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}}) + +int btbcm_check_bdaddr(struct hci_dev *hdev) +{ + struct hci_rp_read_bd_addr *bda; + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + int err = PTR_ERR(skb); + BT_ERR("%s: BCM: Reading device address failed (%d)", + hdev->name, err); + return err; + } + + if (skb->len != sizeof(*bda)) { + BT_ERR("%s: BCM: Device address length mismatch", hdev->name); + kfree_skb(skb); + return -EIO; + } + + bda = (struct hci_rp_read_bd_addr *)skb->data; + if (bda->status) { + BT_ERR("%s: BCM: Device address result failed (%02x)", + hdev->name, bda->status); + kfree_skb(skb); + return -bt_to_errno(bda->status); + } + + /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller + * with no configured address. + */ + if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) { + BT_INFO("%s: BCM: Using default device address (%pMR)", + hdev->name, &bda->bdaddr); + set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + } + + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btbcm_check_bdaddr); + +int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + struct sk_buff *skb; + int err; + + skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + BT_ERR("%s: BCM: Change address command failed (%d)", + hdev->name, err); + return err; + } + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btbcm_set_bdaddr); + +static int btbcm_reset(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + int err = PTR_ERR(skb); + BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err); + return err; + } + kfree_skb(skb); + + return 0; +} + +static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: BCM: Reading local version info failed (%ld)", + hdev->name, PTR_ERR(skb)); + return skb; + } + + if (skb->len != sizeof(struct hci_rp_read_local_version)) { + BT_ERR("%s: BCM: Local version length mismatch", hdev->name); + kfree_skb(skb); + return ERR_PTR(-EIO); + } + + return skb; +} + +static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: BCM: Read verbose config info failed (%ld)", + hdev->name, PTR_ERR(skb)); + return skb; + } + + if (skb->len != 7) { + BT_ERR("%s: BCM: Verbose config length mismatch", hdev->name); + kfree_skb(skb); + return ERR_PTR(-EIO); + } + + return skb; +} + +static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: BCM: Read USB product info failed (%ld)", + hdev->name, PTR_ERR(skb)); + return skb; + } + + if (skb->len != 5) { + BT_ERR("%s: BCM: USB product length mismatch", hdev->name); + kfree_skb(skb); + return ERR_PTR(-EIO); + } + + return skb; +} + +static const struct { + u16 subver; + const char *name; +} bcm_uart_subver_table[] = { + { 0x410e, "BCM43341B0" }, /* 002.001.014 */ + { } +}; + +static const struct { + u16 subver; + const char *name; +} bcm_usb_subver_table[] = { + { 0x210b, "BCM43142A0" }, /* 001.001.011 */ + { 0x2112, "BCM4314A0" }, /* 001.001.018 */ + { 0x2118, "BCM20702A0" }, /* 001.001.024 */ + { 0x2126, "BCM4335A0" }, /* 001.001.038 */ + { 0x220e, "BCM20702A1" }, /* 001.002.014 */ + { 0x230f, "BCM4354A2" }, /* 001.003.015 */ + { 0x4106, "BCM4335B0" }, /* 002.001.006 */ + { 0x410e, "BCM20702B0" }, /* 002.001.014 */ + { 0x6109, "BCM4335C0" }, /* 003.001.009 */ + { 0x610c, "BCM4354" }, /* 003.001.012 */ + { } +}; + +int btbcm_setup_patchram(struct hci_dev *hdev) +{ + const struct hci_command_hdr *cmd; + const struct firmware *fw; + const u8 *fw_ptr; + size_t fw_size; + char fw_name[64]; + u16 opcode, subver, rev, pid, vid; + const char *hw_name = NULL; + struct sk_buff *skb; + struct hci_rp_read_local_version *ver; + int i, err; + + /* Reset */ + err = btbcm_reset(hdev); + if (err) + return err; + + /* Read Local Version Info */ + skb = btbcm_read_local_version(hdev); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ver = (struct hci_rp_read_local_version *)skb->data; + rev = le16_to_cpu(ver->hci_rev); + subver = le16_to_cpu(ver->lmp_subver); + kfree_skb(skb); + + /* Read Verbose Config Version Info */ + skb = btbcm_read_verbose_config(hdev); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]); + kfree_skb(skb); + + switch ((rev & 0xf000) >> 12) { + case 0: + for (i = 0; bcm_uart_subver_table[i].name; i++) { + if (subver == bcm_uart_subver_table[i].subver) { + hw_name = bcm_uart_subver_table[i].name; + break; + } + } + + snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd", + hw_name ? : "BCM"); + break; + case 1: + case 2: + /* Read USB Product Info */ + skb = btbcm_read_usb_product(hdev); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + vid = get_unaligned_le16(skb->data + 1); + pid = get_unaligned_le16(skb->data + 3); + kfree_skb(skb); + + for (i = 0; bcm_usb_subver_table[i].name; i++) { + if (subver == bcm_usb_subver_table[i].subver) { + hw_name = bcm_usb_subver_table[i].name; + break; + } + } + + snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd", + hw_name ? : "BCM", vid, pid); + break; + default: + return 0; + } + + BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, + hw_name ? : "BCM", (subver & 0x7000) >> 13, + (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); + + err = request_firmware(&fw, fw_name, &hdev->dev); + if (err < 0) { + BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name); + return 0; + } + + /* Start Download */ + skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + BT_ERR("%s: BCM: Download Minidrv command failed (%d)", + hdev->name, err); + goto reset; + } + kfree_skb(skb); + + /* 50 msec delay after Download Minidrv completes */ + msleep(50); + + fw_ptr = fw->data; + fw_size = fw->size; + + while (fw_size >= sizeof(*cmd)) { + const u8 *cmd_param; + + cmd = (struct hci_command_hdr *)fw_ptr; + fw_ptr += sizeof(*cmd); + fw_size -= sizeof(*cmd); + + if (fw_size < cmd->plen) { + BT_ERR("%s: BCM: patch %s is corrupted", hdev->name, + fw_name); + err = -EINVAL; + goto reset; + } + + cmd_param = fw_ptr; + fw_ptr += cmd->plen; + fw_size -= cmd->plen; + + opcode = le16_to_cpu(cmd->opcode); + + skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + BT_ERR("%s: BCM: patch command %04x failed (%d)", + hdev->name, opcode, err); + goto reset; + } + kfree_skb(skb); + } + + /* 250 msec delay after Launch Ram completes */ + msleep(250); + +reset: + /* Reset */ + err = btbcm_reset(hdev); + if (err) + goto done; + + /* Read Local Version Info */ + skb = btbcm_read_local_version(hdev); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + + ver = (struct hci_rp_read_local_version *)skb->data; + rev = le16_to_cpu(ver->hci_rev); + subver = le16_to_cpu(ver->lmp_subver); + kfree_skb(skb); + + BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, + hw_name ? : "BCM", (subver & 0x7000) >> 13, + (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); + + btbcm_check_bdaddr(hdev); + + set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + +done: + release_firmware(fw); + + return err; +} +EXPORT_SYMBOL_GPL(btbcm_setup_patchram); + +int btbcm_setup_apple(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + /* Read Verbose Config Version Info */ + skb = btbcm_read_verbose_config(hdev); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1], + get_unaligned_le16(skb->data + 5)); + kfree_skb(skb); + + set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + + return 0; +} +EXPORT_SYMBOL_GPL(btbcm_setup_apple); + +MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); +MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h new file mode 100644 index 000000000000..34268ae3eb46 --- /dev/null +++ b/drivers/bluetooth/btbcm.h @@ -0,0 +1,54 @@ +/* + * + * Bluetooth support for Broadcom devices + * + * Copyright (C) 2015 Intel Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if IS_ENABLED(CONFIG_BT_BCM) + +int btbcm_check_bdaddr(struct hci_dev *hdev); +int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); + +int btbcm_setup_patchram(struct hci_dev *hdev); +int btbcm_setup_apple(struct hci_dev *hdev); + +#else + +static inline int btbcm_check_bdaddr(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} + +static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + return -EOPNOTSUPP; +} + +static inline int btbcm_setup_patchram(struct hci_dev *hdev) +{ + return 0; +} + +static inline int btbcm_setup_apple(struct hci_dev *hdev) +{ + return 0; +} + +#endif diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c new file mode 100644 index 000000000000..2d43d4279b00 --- /dev/null +++ b/drivers/bluetooth/btintel.c @@ -0,0 +1,101 @@ +/* + * + * Bluetooth support for Intel devices + * + * Copyright (C) 2015 Intel Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "btintel.h" + +#define VERSION "0.1" + +#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}}) + +int btintel_check_bdaddr(struct hci_dev *hdev) +{ + struct hci_rp_read_bd_addr *bda; + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + int err = PTR_ERR(skb); + BT_ERR("%s: Reading Intel device address failed (%d)", + hdev->name, err); + return err; + } + + if (skb->len != sizeof(*bda)) { + BT_ERR("%s: Intel device address length mismatch", hdev->name); + kfree_skb(skb); + return -EIO; + } + + bda = (struct hci_rp_read_bd_addr *)skb->data; + if (bda->status) { + BT_ERR("%s: Intel device address result failed (%02x)", + hdev->name, bda->status); + kfree_skb(skb); + return -bt_to_errno(bda->status); + } + + /* For some Intel based controllers, the default Bluetooth device + * address 00:03:19:9E:8B:00 can be found. These controllers are + * fully operational, but have the danger of duplicate addresses + * and that in turn can cause problems with Bluetooth operation. + */ + if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) { + BT_ERR("%s: Found Intel default device address (%pMR)", + hdev->name, &bda->bdaddr); + set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + } + + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btintel_check_bdaddr); + +int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + struct sk_buff *skb; + int err; + + skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + BT_ERR("%s: Changing Intel device address failed (%d)", + hdev->name, err); + return err; + } + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btintel_set_bdaddr); + +MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); +MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h new file mode 100644 index 000000000000..4bda6ab34f60 --- /dev/null +++ b/drivers/bluetooth/btintel.h @@ -0,0 +1,89 @@ +/* + * + * Bluetooth support for Intel devices + * + * Copyright (C) 2015 Intel Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +struct intel_version { + u8 status; + u8 hw_platform; + u8 hw_variant; + u8 hw_revision; + u8 fw_variant; + u8 fw_revision; + u8 fw_build_num; + u8 fw_build_ww; + u8 fw_build_yy; + u8 fw_patch_num; +} __packed; + +struct intel_boot_params { + __u8 status; + __u8 otp_format; + __u8 otp_content; + __u8 otp_patch; + __le16 dev_revid; + __u8 secure_boot; + __u8 key_from_hdr; + __u8 key_type; + __u8 otp_lock; + __u8 api_lock; + __u8 debug_lock; + bdaddr_t otp_bdaddr; + __u8 min_fw_build_nn; + __u8 min_fw_build_cw; + __u8 min_fw_build_yy; + __u8 limited_cce; + __u8 unlocked_state; +} __packed; + +struct intel_bootup { + __u8 zero; + __u8 num_cmds; + __u8 source; + __u8 reset_type; + __u8 reset_reason; + __u8 ddc_status; +} __packed; + +struct intel_secure_send_result { + __u8 result; + __le16 opcode; + __u8 status; +} __packed; + +#if IS_ENABLED(CONFIG_BT_INTEL) + +int btintel_check_bdaddr(struct hci_dev *hdev); +int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); + +#else + +static inline int btintel_check_bdaddr(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} + +static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + return -EOPNOTSUPP; +} + +#endif diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 6e4ff16e487b..de7b236eeae7 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -24,12 +24,14 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/firmware.h> -#include <asm/unaligned.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> -#define VERSION "0.7" +#include "btintel.h" +#include "btbcm.h" + +#define VERSION "0.8" static bool disable_scofix; static bool force_scofix; @@ -1343,39 +1345,6 @@ static int btusb_setup_csr(struct hci_dev *hdev) return ret; } -struct intel_version { - u8 status; - u8 hw_platform; - u8 hw_variant; - u8 hw_revision; - u8 fw_variant; - u8 fw_revision; - u8 fw_build_num; - u8 fw_build_ww; - u8 fw_build_yy; - u8 fw_patch_num; -} __packed; - -struct intel_boot_params { - __u8 status; - __u8 otp_format; - __u8 otp_content; - __u8 otp_patch; - __le16 dev_revid; - __u8 secure_boot; - __u8 key_from_hdr; - __u8 key_type; - __u8 otp_lock; - __u8 api_lock; - __u8 debug_lock; - bdaddr_t otp_bdaddr; - __u8 min_fw_build_nn; - __u8 min_fw_build_cw; - __u8 min_fw_build_yy; - __u8 limited_cce; - __u8 unlocked_state; -} __packed; - static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev, struct intel_version *ver) { @@ -1532,51 +1501,6 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev, return 0; } -#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}}) - -static int btusb_check_bdaddr_intel(struct hci_dev *hdev) -{ - struct sk_buff *skb; - struct hci_rp_read_bd_addr *rp; - - skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, - HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s reading Intel device address failed (%ld)", - hdev->name, PTR_ERR(skb)); - return PTR_ERR(skb); - } - - if (skb->len != sizeof(*rp)) { - BT_ERR("%s Intel device address length mismatch", hdev->name); - kfree_skb(skb); - return -EIO; - } - - rp = (struct hci_rp_read_bd_addr *)skb->data; - if (rp->status) { - BT_ERR("%s Intel device address result failed (%02x)", - hdev->name, rp->status); - kfree_skb(skb); - return -bt_to_errno(rp->status); - } - - /* For some Intel based controllers, the default Bluetooth device - * address 00:03:19:9E:8B:00 can be found. These controllers are - * fully operational, but have the danger of duplicate addresses - * and that in turn can cause problems with Bluetooth operation. - */ - if (!bacmp(&rp->bdaddr, BDADDR_INTEL)) { - BT_ERR("%s found Intel default device address (%pMR)", - hdev->name, &rp->bdaddr); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); - } - - kfree_skb(skb); - - return 0; -} - static int btusb_setup_intel(struct hci_dev *hdev) { struct sk_buff *skb; @@ -1649,7 +1573,7 @@ static int btusb_setup_intel(struct hci_dev *hdev) BT_INFO("%s: Intel device is already patched. patch num: %02x", hdev->name, ver->fw_patch_num); kfree_skb(skb); - btusb_check_bdaddr_intel(hdev); + btintel_check_bdaddr(hdev); return 0; } @@ -1662,7 +1586,7 @@ static int btusb_setup_intel(struct hci_dev *hdev) fw = btusb_setup_intel_get_fw(hdev, ver); if (!fw) { kfree_skb(skb); - btusb_check_bdaddr_intel(hdev); + btintel_check_bdaddr(hdev); return 0; } fw_ptr = fw->data; @@ -1743,7 +1667,7 @@ static int btusb_setup_intel(struct hci_dev *hdev) BT_INFO("%s: Intel Bluetooth firmware patch completed and activated", hdev->name); - btusb_check_bdaddr_intel(hdev); + btintel_check_bdaddr(hdev); return 0; exit_mfg_disable: @@ -1759,7 +1683,7 @@ exit_mfg_disable: BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name); - btusb_check_bdaddr_intel(hdev); + btintel_check_bdaddr(hdev); return 0; exit_mfg_deactivate: @@ -1780,7 +1704,7 @@ exit_mfg_deactivate: BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated", hdev->name); - btusb_check_bdaddr_intel(hdev); + btintel_check_bdaddr(hdev); return 0; } @@ -1822,6 +1746,38 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer, return btusb_recv_bulk(data, buffer, count); } +static void btusb_intel_bootup(struct btusb_data *data, const void *ptr, + unsigned int len) +{ + const struct intel_bootup *evt = ptr; + + if (len != sizeof(*evt)) + return; + + if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) { + smp_mb__after_atomic(); + wake_up_bit(&data->flags, BTUSB_BOOTING); + } +} + +static void btusb_intel_secure_send_result(struct btusb_data *data, + const void *ptr, unsigned int len) +{ + const struct intel_secure_send_result *evt = ptr; + + if (len != sizeof(*evt)) + return; + + if (evt->result) + set_bit(BTUSB_FIRMWARE_FAILED, &data->flags); + + if (test_and_clear_bit(BTUSB_DOWNLOADING, &data->flags) && + test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) { + smp_mb__after_atomic(); + wake_up_bit(&data->flags, BTUSB_DOWNLOADING); + } +} + static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) { struct btusb_data *data = hci_get_drvdata(hdev); @@ -1829,32 +1785,27 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(BTUSB_BOOTLOADER, &data->flags)) { struct hci_event_hdr *hdr = (void *)skb->data; - /* When the firmware loading completes the device sends - * out a vendor specific event indicating the result of - * the firmware loading. - */ - if (skb->len == 7 && hdr->evt == 0xff && hdr->plen == 0x05 && - skb->data[2] == 0x06) { - if (skb->data[3] != 0x00) - test_bit(BTUSB_FIRMWARE_FAILED, &data->flags); - - if (test_and_clear_bit(BTUSB_DOWNLOADING, - &data->flags) && - test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) { - smp_mb__after_atomic(); - wake_up_bit(&data->flags, BTUSB_DOWNLOADING); - } - } - - /* When switching to the operational firmware the device - * sends a vendor specific event indicating that the bootup - * completed. - */ - if (skb->len == 9 && hdr->evt == 0xff && hdr->plen == 0x07 && - skb->data[2] == 0x02) { - if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) { - smp_mb__after_atomic(); - wake_up_bit(&data->flags, BTUSB_BOOTING); + if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && + hdr->plen > 0) { + const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; + unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; + + switch (skb->data[2]) { + case 0x02: + /* When switching to the operational firmware + * the device sends a vendor specific event + * indicating that the bootup completed. + */ + btusb_intel_bootup(data, ptr, len); + break; + case 0x06: + /* When the firmware loading completes the + * device sends out a vendor specific event + * indicating the result of the firmware + * loading. + */ + btusb_intel_secure_send_result(data, ptr, len); + break; } } } @@ -2056,7 +2007,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) if (ver->fw_variant == 0x23) { kfree_skb(skb); clear_bit(BTUSB_BOOTLOADER, &data->flags); - btusb_check_bdaddr_intel(hdev); + btintel_check_bdaddr(hdev); return 0; } @@ -2340,23 +2291,6 @@ static void btusb_hw_error_intel(struct hci_dev *hdev, u8 code) kfree_skb(skb); } -static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr) -{ - struct sk_buff *skb; - long ret; - - skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - BT_ERR("%s: changing Intel device address failed (%ld)", - hdev->name, ret); - return ret; - } - kfree_skb(skb); - - return 0; -} - static int btusb_shutdown_intel(struct hci_dev *hdev) { struct sk_buff *skb; @@ -2401,266 +2335,6 @@ static int btusb_set_bdaddr_marvell(struct hci_dev *hdev, return 0; } -static const struct { - u16 subver; - const char *name; -} bcm_subver_table[] = { - { 0x210b, "BCM43142A0" }, /* 001.001.011 */ - { 0x2112, "BCM4314A0" }, /* 001.001.018 */ - { 0x2118, "BCM20702A0" }, /* 001.001.024 */ - { 0x2126, "BCM4335A0" }, /* 001.001.038 */ - { 0x220e, "BCM20702A1" }, /* 001.002.014 */ - { 0x230f, "BCM4354A2" }, /* 001.003.015 */ - { 0x4106, "BCM4335B0" }, /* 002.001.006 */ - { 0x410e, "BCM20702B0" }, /* 002.001.014 */ - { 0x6109, "BCM4335C0" }, /* 003.001.009 */ - { 0x610c, "BCM4354" }, /* 003.001.012 */ - { } -}; - -#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}}) - -static int btusb_setup_bcm_patchram(struct hci_dev *hdev) -{ - struct btusb_data *data = hci_get_drvdata(hdev); - struct usb_device *udev = data->udev; - char fw_name[64]; - const struct firmware *fw; - const u8 *fw_ptr; - size_t fw_size; - const struct hci_command_hdr *cmd; - const u8 *cmd_param; - u16 opcode, subver, rev; - const char *hw_name = NULL; - struct sk_buff *skb; - struct hci_rp_read_local_version *ver; - struct hci_rp_read_bd_addr *bda; - long ret; - int i; - - /* Reset */ - skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); - return ret; - } - kfree_skb(skb); - - /* Read Local Version Info */ - skb = btusb_read_local_version(hdev); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - ver = (struct hci_rp_read_local_version *)skb->data; - rev = le16_to_cpu(ver->hci_rev); - subver = le16_to_cpu(ver->lmp_subver); - kfree_skb(skb); - - /* Read Verbose Config Version Info */ - skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - BT_ERR("%s: BCM: Read Verbose Version failed (%ld)", - hdev->name, ret); - return ret; - } - - if (skb->len != 7) { - BT_ERR("%s: BCM: Read Verbose Version event length mismatch", - hdev->name); - kfree_skb(skb); - return -EIO; - } - - BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]); - kfree_skb(skb); - - for (i = 0; bcm_subver_table[i].name; i++) { - if (subver == bcm_subver_table[i].subver) { - hw_name = bcm_subver_table[i].name; - break; - } - } - - BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, - hw_name ? : "BCM", (subver & 0x7000) >> 13, - (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); - - snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd", - hw_name ? : "BCM", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); - - ret = request_firmware(&fw, fw_name, &hdev->dev); - if (ret < 0) { - BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name); - return 0; - } - - /* Start Download */ - skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - BT_ERR("%s: BCM: Download Minidrv command failed (%ld)", - hdev->name, ret); - goto reset_fw; - } - kfree_skb(skb); - - /* 50 msec delay after Download Minidrv completes */ - msleep(50); - - fw_ptr = fw->data; - fw_size = fw->size; - - while (fw_size >= sizeof(*cmd)) { - cmd = (struct hci_command_hdr *)fw_ptr; - fw_ptr += sizeof(*cmd); - fw_size -= sizeof(*cmd); - - if (fw_size < cmd->plen) { - BT_ERR("%s: BCM: patch %s is corrupted", - hdev->name, fw_name); - ret = -EINVAL; - goto reset_fw; - } - - cmd_param = fw_ptr; - fw_ptr += cmd->plen; - fw_size -= cmd->plen; - - opcode = le16_to_cpu(cmd->opcode); - - skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, - HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - BT_ERR("%s: BCM: patch command %04x failed (%ld)", - hdev->name, opcode, ret); - goto reset_fw; - } - kfree_skb(skb); - } - - /* 250 msec delay after Launch Ram completes */ - msleep(250); - -reset_fw: - /* Reset */ - skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); - goto done; - } - kfree_skb(skb); - - /* Read Local Version Info */ - skb = btusb_read_local_version(hdev); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - goto done; - } - - ver = (struct hci_rp_read_local_version *)skb->data; - rev = le16_to_cpu(ver->hci_rev); - subver = le16_to_cpu(ver->lmp_subver); - kfree_skb(skb); - - BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, - hw_name ? : "BCM", (subver & 0x7000) >> 13, - (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); - - /* Read BD Address */ - skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, - HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)", - hdev->name, ret); - goto done; - } - - if (skb->len != sizeof(*bda)) { - BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch", - hdev->name); - kfree_skb(skb); - ret = -EIO; - goto done; - } - - bda = (struct hci_rp_read_bd_addr *)skb->data; - if (bda->status) { - BT_ERR("%s: HCI_OP_READ_BD_ADDR error status (%02x)", - hdev->name, bda->status); - kfree_skb(skb); - ret = -bt_to_errno(bda->status); - goto done; - } - - /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller - * with no configured address. - */ - if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) { - BT_INFO("%s: BCM: using default device address (%pMR)", - hdev->name, &bda->bdaddr); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); - } - - kfree_skb(skb); - -done: - release_firmware(fw); - - return ret; -} - -static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr) -{ - struct sk_buff *skb; - long ret; - - skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - BT_ERR("%s: BCM: Change address command failed (%ld)", - hdev->name, ret); - return ret; - } - kfree_skb(skb); - - return 0; -} - -static int btusb_setup_bcm_apple(struct hci_dev *hdev) -{ - struct sk_buff *skb; - int err; - - /* Read Verbose Config Version Info */ - skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - BT_ERR("%s: BCM: Read Verbose Version failed (%d)", - hdev->name, err); - return err; - } - - if (skb->len != 7) { - BT_ERR("%s: BCM: Read Verbose Version event length mismatch", - hdev->name); - kfree_skb(skb); - return -EIO; - } - - BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1], - get_unaligned_le16(skb->data + 5)); - kfree_skb(skb); - - return 0; -} - static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev, const bdaddr_t *bdaddr) { @@ -3054,21 +2728,20 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_BCM92035) hdev->setup = btusb_setup_bcm92035; +#ifdef CONFIG_BT_HCIBTUSB_BCM if (id->driver_info & BTUSB_BCM_PATCHRAM) { - hdev->setup = btusb_setup_bcm_patchram; - hdev->set_bdaddr = btusb_set_bdaddr_bcm; - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + hdev->setup = btbcm_setup_patchram; + hdev->set_bdaddr = btbcm_set_bdaddr; } - if (id->driver_info & BTUSB_BCM_APPLE) { - hdev->setup = btusb_setup_bcm_apple; - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); - } + if (id->driver_info & BTUSB_BCM_APPLE) + hdev->setup = btbcm_setup_apple; +#endif if (id->driver_info & BTUSB_INTEL) { hdev->setup = btusb_setup_intel; hdev->shutdown = btusb_shutdown_intel; - hdev->set_bdaddr = btusb_set_bdaddr_intel; + hdev->set_bdaddr = btintel_set_bdaddr; set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); } @@ -3077,7 +2750,7 @@ static int btusb_probe(struct usb_interface *intf, hdev->send = btusb_send_frame_intel; hdev->setup = btusb_setup_intel_new; hdev->hw_error = btusb_hw_error_intel; - hdev->set_bdaddr = btusb_set_bdaddr_intel; + hdev->set_bdaddr = btintel_set_bdaddr; set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); } diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index 9c4dcf4c62ea..1b3f8647ea2f 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -45,6 +45,7 @@ struct ath_struct { struct hci_uart *hu; unsigned int cur_sleep; + struct sk_buff *rx_skb; struct sk_buff_head txq; struct work_struct ctxtsw; }; @@ -136,6 +137,8 @@ static int ath_close(struct hci_uart *hu) skb_queue_purge(&ath->txq); + kfree_skb(ath->rx_skb); + cancel_work_sync(&ath->ctxtsw); hu->priv = NULL; @@ -187,40 +190,42 @@ static struct sk_buff *ath_dequeue(struct hci_uart *hu) return skb_dequeue(&ath->txq); } +static const struct h4_recv_pkt ath_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, +}; + /* Recv data */ -static int ath_recv(struct hci_uart *hu, void *data, int count) +static int ath_recv(struct hci_uart *hu, const void *data, int count) { - int ret; + struct ath_struct *ath = hu->priv; - ret = hci_recv_stream_fragment(hu->hdev, data, count); - if (ret < 0) { - BT_ERR("Frame Reassembly Failed"); - return ret; + ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count, + ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts)); + if (IS_ERR(ath->rx_skb)) { + int err = PTR_ERR(ath->rx_skb); + BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + return err; } return count; } -static struct hci_uart_proto athp = { - .id = HCI_UART_ATH3K, - .open = ath_open, - .close = ath_close, - .recv = ath_recv, - .enqueue = ath_enqueue, - .dequeue = ath_dequeue, - .flush = ath_flush, +static const struct hci_uart_proto athp = { + .id = HCI_UART_ATH3K, + .name = "ATH3K", + .open = ath_open, + .close = ath_close, + .recv = ath_recv, + .enqueue = ath_enqueue, + .dequeue = ath_dequeue, + .flush = ath_flush, }; int __init ath_init(void) { - int err = hci_uart_register_proto(&athp); - - if (!err) - BT_INFO("HCIATH3K protocol initialized"); - else - BT_ERR("HCIATH3K protocol registration failed"); - - return err; + return hci_uart_register_proto(&athp); } int __exit ath_deinit(void) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c new file mode 100644 index 000000000000..1ec0b4a5ffa6 --- /dev/null +++ b/drivers/bluetooth/hci_bcm.c @@ -0,0 +1,153 @@ +/* + * + * Bluetooth HCI UART driver for Broadcom devices + * + * Copyright (C) 2015 Intel Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/skbuff.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "btbcm.h" +#include "hci_uart.h" + +struct bcm_data { + struct sk_buff *rx_skb; + struct sk_buff_head txq; +}; + +static int bcm_open(struct hci_uart *hu) +{ + struct bcm_data *bcm; + + BT_DBG("hu %p", hu); + + bcm = kzalloc(sizeof(*bcm), GFP_KERNEL); + if (!bcm) + return -ENOMEM; + + skb_queue_head_init(&bcm->txq); + + hu->priv = bcm; + return 0; +} + +static int bcm_close(struct hci_uart *hu) +{ + struct bcm_data *bcm = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&bcm->txq); + kfree_skb(bcm->rx_skb); + kfree(bcm); + + hu->priv = NULL; + return 0; +} + +static int bcm_flush(struct hci_uart *hu) +{ + struct bcm_data *bcm = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&bcm->txq); + + return 0; +} + +static int bcm_setup(struct hci_uart *hu) +{ + BT_DBG("hu %p", hu); + + hu->hdev->set_bdaddr = btbcm_set_bdaddr; + + return btbcm_setup_patchram(hu->hdev); +} + +static const struct h4_recv_pkt bcm_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, +}; + +static int bcm_recv(struct hci_uart *hu, const void *data, int count) +{ + struct bcm_data *bcm = hu->priv; + + if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) + return -EUNATCH; + + bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count, + bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts)); + if (IS_ERR(bcm->rx_skb)) { + int err = PTR_ERR(bcm->rx_skb); + BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + return err; + } + + return count; +} + +static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + struct bcm_data *bcm = hu->priv; + + BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + skb_queue_tail(&bcm->txq, skb); + + return 0; +} + +static struct sk_buff *bcm_dequeue(struct hci_uart *hu) +{ + struct bcm_data *bcm = hu->priv; + + return skb_dequeue(&bcm->txq); +} + +static const struct hci_uart_proto bcm_proto = { + .id = HCI_UART_BCM, + .name = "BCM", + .open = bcm_open, + .close = bcm_close, + .flush = bcm_flush, + .setup = bcm_setup, + .recv = bcm_recv, + .enqueue = bcm_enqueue, + .dequeue = bcm_dequeue, +}; + +int __init bcm_init(void) +{ + return hci_uart_register_proto(&bcm_proto); +} + +int __exit bcm_deinit(void) +{ + return hci_uart_unregister_proto(&bcm_proto); +} diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index 21cc45b34f13..dc8e3d4356a0 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -47,8 +47,6 @@ #include "hci_uart.h" -#define VERSION "0.3" - static bool txcrc = 1; static bool hciextn = 1; @@ -554,10 +552,10 @@ static u16 bscp_get_crc(struct bcsp_struct *bcsp) } /* Recv data */ -static int bcsp_recv(struct hci_uart *hu, void *data, int count) +static int bcsp_recv(struct hci_uart *hu, const void *data, int count) { struct bcsp_struct *bcsp = hu->priv; - unsigned char *ptr; + const unsigned char *ptr; BT_DBG("hu %p count %d rx_state %d rx_count %ld", hu, count, bcsp->rx_state, bcsp->rx_count); @@ -735,8 +733,9 @@ static int bcsp_close(struct hci_uart *hu) return 0; } -static struct hci_uart_proto bcsp = { +static const struct hci_uart_proto bcsp = { .id = HCI_UART_BCSP, + .name = "BCSP", .open = bcsp_open, .close = bcsp_close, .enqueue = bcsp_enqueue, @@ -747,14 +746,7 @@ static struct hci_uart_proto bcsp = { int __init bcsp_init(void) { - int err = hci_uart_register_proto(&bcsp); - - if (!err) - BT_INFO("HCI BCSP protocol initialized"); - else - BT_ERR("HCI BCSP protocol registration failed"); - - return err; + return hci_uart_register_proto(&bcsp); } int __exit bcsp_deinit(void) diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 66db9a803373..f7190f01e135 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -40,17 +40,14 @@ #include <linux/signal.h> #include <linux/ioctl.h> #include <linux/skbuff.h> +#include <asm/unaligned.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include "hci_uart.h" -#define VERSION "1.2" - struct h4_struct { - unsigned long rx_state; - unsigned long rx_count; struct sk_buff *rx_skb; struct sk_buff_head txq; }; @@ -117,18 +114,26 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) return 0; } +static const struct h4_recv_pkt h4_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, +}; + /* Recv data */ -static int h4_recv(struct hci_uart *hu, void *data, int count) +static int h4_recv(struct hci_uart *hu, const void *data, int count) { - int ret; + struct h4_struct *h4 = hu->priv; if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) return -EUNATCH; - ret = hci_recv_stream_fragment(hu->hdev, data, count); - if (ret < 0) { - BT_ERR("Frame Reassembly Failed"); - return ret; + h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count, + h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts)); + if (IS_ERR(h4->rx_skb)) { + int err = PTR_ERR(h4->rx_skb); + BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + return err; } return count; @@ -140,8 +145,9 @@ static struct sk_buff *h4_dequeue(struct hci_uart *hu) return skb_dequeue(&h4->txq); } -static struct hci_uart_proto h4p = { +static const struct hci_uart_proto h4p = { .id = HCI_UART_H4, + .name = "H4", .open = h4_open, .close = h4_close, .recv = h4_recv, @@ -152,17 +158,105 @@ static struct hci_uart_proto h4p = { int __init h4_init(void) { - int err = hci_uart_register_proto(&h4p); - - if (!err) - BT_INFO("HCI H4 protocol initialized"); - else - BT_ERR("HCI H4 protocol registration failed"); - - return err; + return hci_uart_register_proto(&h4p); } int __exit h4_deinit(void) { return hci_uart_unregister_proto(&h4p); } + +struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, + const unsigned char *buffer, int count, + const struct h4_recv_pkt *pkts, int pkts_count) +{ + while (count) { + int i, len; + + if (!skb) { + for (i = 0; i < pkts_count; i++) { + if (buffer[0] != (&pkts[i])->type) + continue; + + skb = bt_skb_alloc((&pkts[i])->maxlen, + GFP_ATOMIC); + if (!skb) + return ERR_PTR(-ENOMEM); + + bt_cb(skb)->pkt_type = (&pkts[i])->type; + bt_cb(skb)->expect = (&pkts[i])->hlen; + break; + } + + /* Check for invalid packet type */ + if (!skb) + return ERR_PTR(-EILSEQ); + + count -= 1; + buffer += 1; + } + + len = min_t(uint, bt_cb(skb)->expect - skb->len, count); + memcpy(skb_put(skb, len), buffer, len); + + count -= len; + buffer += len; + + /* Check for partial packet */ + if (skb->len < bt_cb(skb)->expect) + continue; + + for (i = 0; i < pkts_count; i++) { + if (bt_cb(skb)->pkt_type == (&pkts[i])->type) + break; + } + + if (i >= pkts_count) { + kfree_skb(skb); + return ERR_PTR(-EILSEQ); + } + + if (skb->len == (&pkts[i])->hlen) { + u16 dlen; + + switch ((&pkts[i])->lsize) { + case 0: + /* No variable data length */ + (&pkts[i])->recv(hdev, skb); + skb = NULL; + break; + case 1: + /* Single octet variable length */ + dlen = skb->data[(&pkts[i])->loff]; + bt_cb(skb)->expect += dlen; + + if (skb_tailroom(skb) < dlen) { + kfree_skb(skb); + return ERR_PTR(-EMSGSIZE); + } + break; + case 2: + /* Double octet variable length */ + dlen = get_unaligned_le16(skb->data + + (&pkts[i])->loff); + bt_cb(skb)->expect += dlen; + + if (skb_tailroom(skb) < dlen) { + kfree_skb(skb); + return ERR_PTR(-EMSGSIZE); + } + break; + default: + /* Unsupported variable length */ + kfree_skb(skb); + return ERR_PTR(-EILSEQ); + } + } else { + /* Complete frame */ + (&pkts[i])->recv(hdev, skb); + skb = NULL; + } + } + + return skb; +} diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index ec0fa7732c0d..3455cecc9ecf 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -511,10 +511,10 @@ static void h5_reset_rx(struct h5 *h5) clear_bit(H5_RX_ESC, &h5->flags); } -static int h5_recv(struct hci_uart *hu, void *data, int count) +static int h5_recv(struct hci_uart *hu, const void *data, int count) { struct h5 *h5 = hu->priv; - unsigned char *ptr = data; + const unsigned char *ptr = data; BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending, count); @@ -743,8 +743,9 @@ static int h5_flush(struct hci_uart *hu) return 0; } -static struct hci_uart_proto h5p = { +static const struct hci_uart_proto h5p = { .id = HCI_UART_3WIRE, + .name = "Three-wire (H5)", .open = h5_open, .close = h5_close, .recv = h5_recv, @@ -755,14 +756,7 @@ static struct hci_uart_proto h5p = { int __init h5_init(void) { - int err = hci_uart_register_proto(&h5p); - - if (!err) - BT_INFO("HCI Three-wire UART (H5) protocol initialized"); - else - BT_ERR("HCI Three-wire UART (H5) protocol init failed"); - - return err; + return hci_uart_register_proto(&h5p); } int __exit h5_deinit(void) diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c new file mode 100644 index 000000000000..5dd07bf05236 --- /dev/null +++ b/drivers/bluetooth/hci_intel.c @@ -0,0 +1,31 @@ +/* + * + * Bluetooth HCI UART driver for Intel devices + * + * Copyright (C) 2015 Intel Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/skbuff.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "hci_uart.h" diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 1363dc616ace..5c9a73f02664 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -44,13 +44,15 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> +#include "btintel.h" +#include "btbcm.h" #include "hci_uart.h" -#define VERSION "2.2" +#define VERSION "2.3" -static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO]; +static const struct hci_uart_proto *hup[HCI_UART_MAX_PROTO]; -int hci_uart_register_proto(struct hci_uart_proto *p) +int hci_uart_register_proto(const struct hci_uart_proto *p) { if (p->id >= HCI_UART_MAX_PROTO) return -EINVAL; @@ -60,10 +62,12 @@ int hci_uart_register_proto(struct hci_uart_proto *p) hup[p->id] = p; + BT_INFO("HCI UART protocol %s registered", p->name); + return 0; } -int hci_uart_unregister_proto(struct hci_uart_proto *p) +int hci_uart_unregister_proto(const struct hci_uart_proto *p) { if (p->id >= HCI_UART_MAX_PROTO) return -EINVAL; @@ -76,7 +80,7 @@ int hci_uart_unregister_proto(struct hci_uart_proto *p) return 0; } -static struct hci_uart_proto *hci_uart_get_proto(unsigned int id) +static const struct hci_uart_proto *hci_uart_get_proto(unsigned int id) { if (id >= HCI_UART_MAX_PROTO) return NULL; @@ -264,10 +268,48 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) static int hci_uart_setup(struct hci_dev *hdev) { struct hci_uart *hu = hci_get_drvdata(hdev); + struct hci_rp_read_local_version *ver; + struct sk_buff *skb; if (hu->proto->setup) return hu->proto->setup(hu); + if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags)) + return 0; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: Reading local version information failed (%ld)", + hdev->name, PTR_ERR(skb)); + return 0; + } + + if (skb->len != sizeof(*ver)) { + BT_ERR("%s: Event length mismatch for version information", + hdev->name); + goto done; + } + + ver = (struct hci_rp_read_local_version *)skb->data; + + switch (le16_to_cpu(ver->manufacturer)) { +#ifdef CONFIG_BT_HCIUART_INTEL + case 2: + hdev->set_bdaddr = btintel_set_bdaddr; + btintel_check_bdaddr(hdev); + break; +#endif +#ifdef CONFIG_BT_HCIUART_BCM + case 15: + hdev->set_bdaddr = btbcm_set_bdaddr; + btbcm_check_bdaddr(hdev); + break; +#endif + } + +done: + kfree_skb(skb); return 0; } @@ -326,7 +368,7 @@ static int hci_uart_tty_open(struct tty_struct *tty) */ static void hci_uart_tty_close(struct tty_struct *tty) { - struct hci_uart *hu = (void *)tty->disc_data; + struct hci_uart *hu = tty->disc_data; struct hci_dev *hdev; BT_DBG("tty %p", tty); @@ -365,7 +407,7 @@ static void hci_uart_tty_close(struct tty_struct *tty) */ static void hci_uart_tty_wakeup(struct tty_struct *tty) { - struct hci_uart *hu = (void *)tty->disc_data; + struct hci_uart *hu = tty->disc_data; BT_DBG(""); @@ -393,9 +435,10 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) * * Return Value: None */ -static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) +static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, + char *flags, int count) { - struct hci_uart *hu = (void *)tty->disc_data; + struct hci_uart *hu = tty->disc_data; if (!hu || tty != hu->tty) return; @@ -404,7 +447,7 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *f return; spin_lock(&hu->rx_lock); - hu->proto->recv(hu, (void *) data, count); + hu->proto->recv(hu, data, count); if (hu->hdev) hu->hdev->stat.byte_rx += count; @@ -469,7 +512,7 @@ static int hci_uart_register_dev(struct hci_uart *hu) static int hci_uart_set_proto(struct hci_uart *hu, int id) { - struct hci_uart_proto *p; + const struct hci_uart_proto *p; int err; p = hci_uart_get_proto(id); @@ -497,7 +540,8 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags) BIT(HCI_UART_RESET_ON_INIT) | BIT(HCI_UART_CREATE_AMP) | BIT(HCI_UART_INIT_PENDING) | - BIT(HCI_UART_EXT_CONFIG); + BIT(HCI_UART_EXT_CONFIG) | + BIT(HCI_UART_VND_DETECT); if (flags & ~valid_flags) return -EINVAL; @@ -520,10 +564,10 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags) * * Return Value: Command dependent */ -static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) +static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { - struct hci_uart *hu = (void *)tty->disc_data; + struct hci_uart *hu = tty->disc_data; int err = 0; BT_DBG(""); @@ -577,19 +621,19 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, * We don't provide read/write/poll interface for user space. */ static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) + unsigned char __user *buf, size_t nr) { return 0; } static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, - const unsigned char *data, size_t count) + const unsigned char *data, size_t count) { return 0; } static unsigned int hci_uart_tty_poll(struct tty_struct *tty, - struct file *filp, poll_table *wait) + struct file *filp, poll_table *wait) { return 0; } @@ -637,6 +681,9 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_3WIRE h5_init(); #endif +#ifdef CONFIG_BT_HCIUART_BCM + bcm_init(); +#endif return 0; } @@ -660,6 +707,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_3WIRE h5_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_BCM + bcm_deinit(); +#endif /* Release tty registration of line discipline */ err = tty_unregister_ldisc(N_HCI); diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 69a90b1b5ff5..9ee24b075f79 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -370,10 +370,10 @@ static inline int ll_check_data_len(struct hci_dev *hdev, struct ll_struct *ll, } /* Recv data */ -static int ll_recv(struct hci_uart *hu, void *data, int count) +static int ll_recv(struct hci_uart *hu, const void *data, int count) { struct ll_struct *ll = hu->priv; - char *ptr; + const char *ptr; struct hci_event_hdr *eh; struct hci_acl_hdr *ah; struct hci_sco_hdr *sh; @@ -505,8 +505,9 @@ static struct sk_buff *ll_dequeue(struct hci_uart *hu) return skb_dequeue(&ll->txq); } -static struct hci_uart_proto llp = { +static const struct hci_uart_proto llp = { .id = HCI_UART_LL, + .name = "LL", .open = ll_open, .close = ll_close, .recv = ll_recv, @@ -517,14 +518,7 @@ static struct hci_uart_proto llp = { int __init ll_init(void) { - int err = hci_uart_register_proto(&llp); - - if (!err) - BT_INFO("HCILL protocol initialized"); - else - BT_ERR("HCILL protocol registration failed"); - - return err; + return hci_uart_register_proto(&llp); } int __exit ll_deinit(void) diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 074ed29092b4..72120a5ba13c 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -35,7 +35,7 @@ #define HCIUARTGETFLAGS _IOR('U', 204, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 6 +#define HCI_UART_MAX_PROTO 8 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 @@ -43,23 +43,27 @@ #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 #define HCI_UART_ATH3K 5 +#define HCI_UART_INTEL 6 +#define HCI_UART_BCM 7 #define HCI_UART_RAW_DEVICE 0 #define HCI_UART_RESET_ON_INIT 1 #define HCI_UART_CREATE_AMP 2 #define HCI_UART_INIT_PENDING 3 #define HCI_UART_EXT_CONFIG 4 +#define HCI_UART_VND_DETECT 5 struct hci_uart; struct hci_uart_proto { unsigned int id; + const char *name; int (*open)(struct hci_uart *hu); int (*close)(struct hci_uart *hu); int (*flush)(struct hci_uart *hu); - int (*recv)(struct hci_uart *hu, void *data, int len); - int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb); int (*setup)(struct hci_uart *hu); + int (*recv)(struct hci_uart *hu, const void *data, int len); + int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb); struct sk_buff *(*dequeue)(struct hci_uart *hu); }; @@ -72,7 +76,7 @@ struct hci_uart { struct work_struct init_ready; struct work_struct write_work; - struct hci_uart_proto *proto; + const struct hci_uart_proto *proto; void *priv; struct sk_buff *tx_skb; @@ -88,14 +92,48 @@ struct hci_uart { #define HCI_UART_SENDING 1 #define HCI_UART_TX_WAKEUP 2 -int hci_uart_register_proto(struct hci_uart_proto *p); -int hci_uart_unregister_proto(struct hci_uart_proto *p); +int hci_uart_register_proto(const struct hci_uart_proto *p); +int hci_uart_unregister_proto(const struct hci_uart_proto *p); int hci_uart_tx_wakeup(struct hci_uart *hu); int hci_uart_init_ready(struct hci_uart *hu); #ifdef CONFIG_BT_HCIUART_H4 int h4_init(void); int h4_deinit(void); + +struct h4_recv_pkt { + u8 type; /* Packet type */ + u8 hlen; /* Header length */ + u8 loff; /* Data length offset in header */ + u8 lsize; /* Data length field size */ + u16 maxlen; /* Max overall packet length */ + int (*recv)(struct hci_dev *hdev, struct sk_buff *skb); +}; + +#define H4_RECV_ACL \ + .type = HCI_ACLDATA_PKT, \ + .hlen = HCI_ACL_HDR_SIZE, \ + .loff = 2, \ + .lsize = 2, \ + .maxlen = HCI_MAX_FRAME_SIZE \ + +#define H4_RECV_SCO \ + .type = HCI_SCODATA_PKT, \ + .hlen = HCI_SCO_HDR_SIZE, \ + .loff = 2, \ + .lsize = 1, \ + .maxlen = HCI_MAX_SCO_SIZE + +#define H4_RECV_EVENT \ + .type = HCI_EVENT_PKT, \ + .hlen = HCI_EVENT_HDR_SIZE, \ + .loff = 1, \ + .lsize = 1, \ + .maxlen = HCI_MAX_EVENT_SIZE + +struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, + const unsigned char *buffer, int count, + const struct h4_recv_pkt *pkts, int pkts_count); #endif #ifdef CONFIG_BT_HCIUART_BCSP @@ -117,3 +155,8 @@ int ath_deinit(void); int h5_init(void); int h5_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_BCM +int bcm_init(void); +int bcm_deinit(void); +#endif diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 5ad46f7f514f..38026650c038 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1173,7 +1173,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw, } static int -at86rf230_set_txpower(struct ieee802154_hw *hw, int db) +at86rf230_set_txpower(struct ieee802154_hw *hw, s8 db) { struct at86rf230_local *lp = hw->priv; diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3acecf35420b..d95da83cb1b0 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -374,6 +374,7 @@ enum { /* LE features */ #define HCI_LE_ENCRYPTION 0x01 #define HCI_LE_CONN_PARAM_REQ_PROC 0x02 +#define HCI_LE_SLAVE_FEATURES 0x08 #define HCI_LE_PING 0x10 #define HCI_LE_DATA_LEN_EXT 0x20 #define HCI_LE_EXT_SCAN_POLICY 0x80 @@ -463,12 +464,14 @@ enum { #define EIR_NAME_COMPLETE 0x09 /* complete local name */ #define EIR_TX_POWER 0x0A /* transmit power level */ #define EIR_CLASS_OF_DEV 0x0D /* Class of Device */ -#define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ -#define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ +#define EIR_SSP_HASH_C192 0x0E /* Simple Pairing Hash C-192 */ +#define EIR_SSP_RAND_R192 0x0F /* Simple Pairing Randomizer R-192 */ #define EIR_DEVICE_ID 0x10 /* device ID */ #define EIR_APPEARANCE 0x19 /* Device appearance */ #define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */ #define EIR_LE_ROLE 0x1C /* LE role */ +#define EIR_SSP_HASH_C256 0x1D /* Simple Pairing Hash C-256 */ +#define EIR_SSP_RAND_R256 0x1E /* Simple Pairing Rand R-256 */ #define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */ #define EIR_LE_SC_RANDOM 0x23 /* LE SC Random Value */ @@ -1374,6 +1377,11 @@ struct hci_cp_le_conn_update { __le16 max_ce_len; } __packed; +#define HCI_OP_LE_READ_REMOTE_FEATURES 0x2016 +struct hci_cp_le_read_remote_features { + __le16 handle; +} __packed; + #define HCI_OP_LE_START_ENC 0x2019 struct hci_cp_le_start_enc { __le16 handle; @@ -1866,6 +1874,13 @@ struct hci_ev_le_conn_update_complete { __le16 supervision_timeout; } __packed; +#define HCI_EV_LE_REMOTE_FEAT_COMPLETE 0x04 +struct hci_ev_le_remote_feat_complete { + __u8 status; + __le16 handle; + __u8 features[8]; +} __packed; + #define HCI_EV_LE_LTK_REQ 0x05 struct hci_ev_le_ltk_req { __le16 handle; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 93fd3e756b8a..a056c2bfeb81 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -185,7 +185,6 @@ struct amp_assoc { #define HCI_MAX_PAGES 3 -#define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; struct mutex lock; @@ -327,7 +326,6 @@ struct hci_dev { struct sk_buff_head cmd_q; struct sk_buff *sent_cmd; - struct sk_buff *reassembly[NUM_REASSEMBLY]; struct mutex req_lock; wait_queue_head_t req_wait_q; @@ -1012,7 +1010,6 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); -int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); void hci_init_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); diff --git a/include/net/mac802154.h b/include/net/mac802154.h index fb4e8a3d6229..e18e7fd43f47 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -213,7 +213,7 @@ struct ieee802154_ops { int (*set_hw_addr_filt)(struct ieee802154_hw *hw, struct ieee802154_hw_addr_filt *filt, unsigned long changed); - int (*set_txpower)(struct ieee802154_hw *hw, int db); + int (*set_txpower)(struct ieee802154_hw *hw, s8 dbm); int (*set_lbt)(struct ieee802154_hw *hw, bool on); int (*set_cca_mode)(struct ieee802154_hw *hw, const struct wpan_phy_cca *cca); @@ -247,7 +247,7 @@ static inline void ieee802154_le64_to_be64(void *be64_dst, const void *le64_src) __put_unaligned_memmove64(swab64p(le64_src), be64_dst); } -/* Basic interface to register ieee802154 hwice */ +/* Basic interface to register ieee802154 device */ struct ieee802154_hw * ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops); void ieee802154_free_hw(struct ieee802154_hw *hw); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 46b114c0140b..476709bd068a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3200,7 +3200,7 @@ EXPORT_SYMBOL(hci_register_dev); /* Unregister HCI device */ void hci_unregister_dev(struct hci_dev *hdev) { - int i, id; + int id; BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); @@ -3214,9 +3214,6 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_dev_do_close(hdev); - for (i = 0; i < NUM_REASSEMBLY; i++) - kfree_skb(hdev->reassembly[i]); - cancel_work_sync(&hdev->power_on); if (!test_bit(HCI_INIT, &hdev->flags) && @@ -3320,149 +3317,6 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) } EXPORT_SYMBOL(hci_recv_frame); -static int hci_reassembly(struct hci_dev *hdev, int type, void *data, - int count, __u8 index) -{ - int len = 0; - int hlen = 0; - int remain = count; - struct sk_buff *skb; - struct bt_skb_cb *scb; - - if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) || - index >= NUM_REASSEMBLY) - return -EILSEQ; - - skb = hdev->reassembly[index]; - - if (!skb) { - switch (type) { - case HCI_ACLDATA_PKT: - len = HCI_MAX_FRAME_SIZE; - hlen = HCI_ACL_HDR_SIZE; - break; - case HCI_EVENT_PKT: - len = HCI_MAX_EVENT_SIZE; - hlen = HCI_EVENT_HDR_SIZE; - break; - case HCI_SCODATA_PKT: - len = HCI_MAX_SCO_SIZE; - hlen = HCI_SCO_HDR_SIZE; - break; - } - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - scb = (void *) skb->cb; - scb->expect = hlen; - scb->pkt_type = type; - - hdev->reassembly[index] = skb; - } - - while (count) { - scb = (void *) skb->cb; - len = min_t(uint, scb->expect, count); - - memcpy(skb_put(skb, len), data, len); - - count -= len; - data += len; - scb->expect -= len; - remain = count; - - switch (type) { - case HCI_EVENT_PKT: - if (skb->len == HCI_EVENT_HDR_SIZE) { - struct hci_event_hdr *h = hci_event_hdr(skb); - scb->expect = h->plen; - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - - case HCI_ACLDATA_PKT: - if (skb->len == HCI_ACL_HDR_SIZE) { - struct hci_acl_hdr *h = hci_acl_hdr(skb); - scb->expect = __le16_to_cpu(h->dlen); - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - - case HCI_SCODATA_PKT: - if (skb->len == HCI_SCO_HDR_SIZE) { - struct hci_sco_hdr *h = hci_sco_hdr(skb); - scb->expect = h->dlen; - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - } - - if (scb->expect == 0) { - /* Complete frame */ - - bt_cb(skb)->pkt_type = type; - hci_recv_frame(hdev, skb); - - hdev->reassembly[index] = NULL; - return remain; - } - } - - return remain; -} - -#define STREAM_REASSEMBLY 0 - -int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) -{ - int type; - int rem = 0; - - while (count) { - struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY]; - - if (!skb) { - struct { char type; } *pkt; - - /* Start of the frame */ - pkt = data; - type = pkt->type; - - data++; - count--; - } else - type = bt_cb(skb)->pkt_type; - - rem = hci_reassembly(hdev, type, data, count, - STREAM_REASSEMBLY); - if (rem < 0) - return rem; - - data += (count - rem); - count = rem; - } - - return rem; -} -EXPORT_SYMBOL(hci_recv_stream_fragment); - /* ---- Interface to upper protocols ---- */ int hci_register_cb(struct hci_cb *cb) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 01031038eb0e..7b61be73650f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2036,6 +2036,33 @@ unlock: hci_dev_unlock(hdev); } +static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_le_read_remote_features *cp; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (!status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_REMOTE_FEATURES); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); + if (conn) { + if (conn->state == BT_CONFIG) { + hci_connect_cfm(conn, status); + hci_conn_drop(conn); + } + } + + hci_dev_unlock(hdev); +} + static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) { struct hci_cp_le_start_enc *cp; @@ -3104,6 +3131,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb, hci_cs_le_create_conn(hdev, ev->status); break; + case HCI_OP_LE_READ_REMOTE_FEATURES: + hci_cs_le_read_remote_features(hdev, ev->status); + break; + case HCI_OP_LE_START_ENC: hci_cs_le_start_enc(hdev, ev->status); break; @@ -4515,7 +4546,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); - conn->state = BT_CONNECTED; + conn->state = BT_CONFIG; conn->le_conn_interval = le16_to_cpu(ev->interval); conn->le_conn_latency = le16_to_cpu(ev->latency); @@ -4524,7 +4555,33 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_debugfs_create_conn(conn); hci_conn_add_sysfs(conn); - hci_connect_cfm(conn, ev->status); + if (!ev->status) { + /* The remote features procedure is defined for master + * role only. So only in case of an initiated connection + * request the remote features. + * + * If the local controller supports slave-initiated features + * exchange, then requesting the remote features in slave + * role is possible. Otherwise just transition into the + * connected state without requesting the remote features. + */ + if (conn->out || + (hdev->le_features[0] & HCI_LE_SLAVE_FEATURES)) { + struct hci_cp_le_read_remote_features cp; + + cp.handle = __cpu_to_le16(conn->handle); + + hci_send_cmd(hdev, HCI_OP_LE_READ_REMOTE_FEATURES, + sizeof(cp), &cp); + + hci_conn_hold(conn); + } else { + conn->state = BT_CONNECTED; + hci_connect_cfm(conn, ev->status); + } + } else { + hci_connect_cfm(conn, ev->status); + } params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst, conn->dst_type); @@ -4826,6 +4883,48 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); } +static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_le_remote_feat_complete *ev = (void *)skb->data; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + if (!ev->status) + memcpy(conn->features[0], ev->features, 8); + + if (conn->state == BT_CONFIG) { + __u8 status; + + /* If the local controller supports slave-initiated + * features exchange, but the remote controller does + * not, then it is possible that the error code 0x1a + * for unsupported remote feature gets returned. + * + * In this specific case, allow the connection to + * transition into connected state and mark it as + * successful. + */ + if ((hdev->le_features[0] & HCI_LE_SLAVE_FEATURES) && + !conn->out && ev->status == 0x1a) + status = 0x00; + else + status = ev->status; + + conn->state = BT_CONNECTED; + hci_connect_cfm(conn, status); + hci_conn_drop(conn); + } + } + + hci_dev_unlock(hdev); +} + static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_ltk_req *ev = (void *) skb->data; @@ -4999,6 +5098,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_adv_report_evt(hdev, skb); break; + case HCI_EV_LE_REMOTE_FEAT_COMPLETE: + hci_le_remote_feat_complete_evt(hdev, skb); + break; + case HCI_EV_LE_LTK_REQ: hci_le_ltk_request_evt(hdev, skb); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 845dfcc43a20..7fd87e7135b5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6466,6 +6466,145 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, return eir_len; } +static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status, + u16 opcode, struct sk_buff *skb) +{ + const struct mgmt_cp_read_local_oob_ext_data *mgmt_cp; + struct mgmt_rp_read_local_oob_ext_data *mgmt_rp; + u8 *h192, *r192, *h256, *r256; + struct mgmt_pending_cmd *cmd; + u16 eir_len; + int err; + + BT_DBG("%s status %u", hdev->name, status); + + cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev); + if (!cmd) + return; + + mgmt_cp = cmd->param; + + if (status) { + status = mgmt_status(status); + eir_len = 0; + + h192 = NULL; + r192 = NULL; + h256 = NULL; + r256 = NULL; + } else if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) { + struct hci_rp_read_local_oob_data *rp; + + if (skb->len != sizeof(*rp)) { + status = MGMT_STATUS_FAILED; + eir_len = 0; + } else { + status = MGMT_STATUS_SUCCESS; + rp = (void *)skb->data; + + eir_len = 5 + 18 + 18; + h192 = rp->hash; + r192 = rp->rand; + h256 = NULL; + r256 = NULL; + } + } else { + struct hci_rp_read_local_oob_ext_data *rp; + + if (skb->len != sizeof(*rp)) { + status = MGMT_STATUS_FAILED; + eir_len = 0; + } else { + status = MGMT_STATUS_SUCCESS; + rp = (void *)skb->data; + + if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) { + eir_len = 5 + 18 + 18; + h192 = NULL; + r192 = NULL; + } else { + eir_len = 5 + 18 + 18 + 18 + 18; + h192 = rp->hash192; + r192 = rp->rand192; + } + + h256 = rp->hash256; + r256 = rp->rand256; + } + } + + mgmt_rp = kmalloc(sizeof(*mgmt_rp) + eir_len, GFP_KERNEL); + if (!mgmt_rp) + goto done; + + if (status) + goto send_rsp; + + eir_len = eir_append_data(mgmt_rp->eir, 0, EIR_CLASS_OF_DEV, + hdev->dev_class, 3); + + if (h192 && r192) { + eir_len = eir_append_data(mgmt_rp->eir, eir_len, + EIR_SSP_HASH_C192, h192, 16); + eir_len = eir_append_data(mgmt_rp->eir, eir_len, + EIR_SSP_RAND_R192, r192, 16); + } + + if (h256 && r256) { + eir_len = eir_append_data(mgmt_rp->eir, eir_len, + EIR_SSP_HASH_C256, h256, 16); + eir_len = eir_append_data(mgmt_rp->eir, eir_len, + EIR_SSP_RAND_R256, r256, 16); + } + +send_rsp: + mgmt_rp->type = mgmt_cp->type; + mgmt_rp->eir_len = cpu_to_le16(eir_len); + + err = mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, status, + mgmt_rp, sizeof(*mgmt_rp) + eir_len); + if (err < 0 || status) + goto done; + + hci_sock_set_flag(cmd->sk, HCI_MGMT_OOB_DATA_EVENTS); + + err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev, + mgmt_rp, sizeof(*mgmt_rp) + eir_len, + HCI_MGMT_OOB_DATA_EVENTS, cmd->sk); +done: + kfree(mgmt_rp); + mgmt_pending_remove(cmd); +} + +static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk, + struct mgmt_cp_read_local_oob_ext_data *cp) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + int err; + + cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev, + cp, sizeof(*cp)); + if (!cmd) + return -ENOMEM; + + hci_req_init(&req, hdev); + + if (bredr_sc_enabled(hdev)) + hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL); + else + hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); + + err = hci_req_run_skb(&req, read_local_oob_ext_data_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + return err; + } + + return 0; +} + static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -6517,8 +6656,19 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, eir_len = 0; switch (cp->type) { case BIT(BDADDR_BREDR): - eir_len = eir_append_data(rp->eir, eir_len, EIR_CLASS_OF_DEV, - hdev->dev_class, 3); + if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { + err = read_local_ssp_oob_req(hdev, sk, cp); + hci_dev_unlock(hdev); + if (!err) + goto done; + + status = MGMT_STATUS_FAILED; + goto complete; + } else { + eir_len = eir_append_data(rp->eir, eir_len, + EIR_CLASS_OF_DEV, + hdev->dev_class, 3); + } break; case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) && |