summaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2015-07-20 10:08:17 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-07-20 10:08:17 -0700
commitc57d5621d2f2dc238f4b9c4d00b2a54187a75445 (patch)
treeece13738a44545fb110e5d73adbf2625bc7a1ea6 /drivers/bluetooth
parent6ccfe64c770139675a080ee5029ded7d89d9ea0d (diff)
parent52721d9d3334c1cb1f76219a161084094ec634dc (diff)
downloadblackbird-op-linux-c57d5621d2f2dc238f4b9c4d00b2a54187a75445.tar.gz
blackbird-op-linux-c57d5621d2f2dc238f4b9c4d00b2a54187a75445.zip
Merge tag 'v4.2-rc3' into next
Sync up with Linux 4.2-rc3 to bring in infrastructure (OF) pieces.
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r--drivers/bluetooth/Kconfig56
-rw-r--r--drivers/bluetooth/Makefile5
-rw-r--r--drivers/bluetooth/ath3k.c11
-rw-r--r--drivers/bluetooth/bt3c_cs.c6
-rw-r--r--drivers/bluetooth/btbcm.c491
-rw-r--r--drivers/bluetooth/btbcm.h117
-rw-r--r--drivers/bluetooth/btintel.c95
-rw-r--r--drivers/bluetooth/btintel.h89
-rw-r--r--drivers/bluetooth/btmrvl_drv.h1
-rw-r--r--drivers/bluetooth/btmrvl_main.c14
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c2
-rw-r--r--drivers/bluetooth/btrtl.c390
-rw-r--r--drivers/bluetooth/btrtl.h52
-rw-r--r--drivers/bluetooth/btusb.c869
-rw-r--r--drivers/bluetooth/btwilink.c2
-rw-r--r--drivers/bluetooth/hci_ath.c116
-rw-r--r--drivers/bluetooth/hci_bcm.c258
-rw-r--r--drivers/bluetooth/hci_bcsp.c38
-rw-r--r--drivers/bluetooth/hci_h4.c133
-rw-r--r--drivers/bluetooth/hci_h5.c16
-rw-r--r--drivers/bluetooth/hci_intel.c31
-rw-r--r--drivers/bluetooth/hci_ldisc.c222
-rw-r--r--drivers/bluetooth/hci_ll.c16
-rw-r--r--drivers/bluetooth/hci_uart.h65
-rw-r--r--drivers/bluetooth/hci_vhci.c2
25 files changed, 2573 insertions, 524 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 364f080768d0..2e777071e1dc 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -2,9 +2,21 @@
menu "Bluetooth device drivers"
depends on BT
+config BT_INTEL
+ tristate
+
+config BT_BCM
+ tristate
+ select FW_LOADER
+
+config BT_RTL
+ 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 +25,28 @@ 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_HCIBTUSB_RTL
+ bool "Realtek protocol support"
+ depends on BT_HCIBTUSB
+ select BT_RTL
+ default y
+ help
+ The Realtek protocol support enables firmware and configuration
+ download support for Realtek Bluetooth controllers.
+
+ Say Y here to compile support for Realtek protocol.
+
config BT_HCIBTSDIO
tristate "HCI SDIO driver"
depends on MMC
@@ -62,6 +96,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 +129,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..f40e194e7080 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -15,10 +15,13 @@ 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
+obj-$(CONFIG_BT_RTL) += btrtl.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
@@ -29,6 +32,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/ath3k.c b/drivers/bluetooth/ath3k.c
index de4c8499cbac..e527a3e13939 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -65,6 +65,7 @@ static const struct usb_device_id ath3k_table[] = {
/* Atheros AR3011 with sflash firmware*/
{ USB_DEVICE(0x0489, 0xE027) },
{ USB_DEVICE(0x0489, 0xE03D) },
+ { USB_DEVICE(0x04F2, 0xAFF1) },
{ USB_DEVICE(0x0930, 0x0215) },
{ USB_DEVICE(0x0CF3, 0x3002) },
{ USB_DEVICE(0x0CF3, 0xE019) },
@@ -79,6 +80,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x0489, 0xe057) },
{ USB_DEVICE(0x0489, 0xe056) },
{ USB_DEVICE(0x0489, 0xe05f) },
+ { USB_DEVICE(0x0489, 0xe076) },
{ USB_DEVICE(0x0489, 0xe078) },
{ USB_DEVICE(0x04c5, 0x1330) },
{ USB_DEVICE(0x04CA, 0x3004) },
@@ -87,6 +89,8 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x04CA, 0x3007) },
{ USB_DEVICE(0x04CA, 0x3008) },
{ USB_DEVICE(0x04CA, 0x300b) },
+ { USB_DEVICE(0x04CA, 0x300d) },
+ { USB_DEVICE(0x04CA, 0x300f) },
{ USB_DEVICE(0x04CA, 0x3010) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x0220) },
@@ -103,6 +107,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x0cf3, 0xe003) },
{ USB_DEVICE(0x0CF3, 0xE004) },
{ USB_DEVICE(0x0CF3, 0xE005) },
+ { USB_DEVICE(0x0CF3, 0xE006) },
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x13d3, 0x3375) },
{ USB_DEVICE(0x13d3, 0x3393) },
@@ -110,6 +115,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x13d3, 0x3408) },
{ USB_DEVICE(0x13d3, 0x3423) },
{ USB_DEVICE(0x13d3, 0x3432) },
+ { USB_DEVICE(0x13d3, 0x3474) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
@@ -134,6 +140,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
@@ -142,6 +149,8 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
@@ -157,6 +166,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
@@ -165,6 +175,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 4f7e8d400bc0..7aab65427d38 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -202,9 +202,8 @@ static void bt3c_write_wakeup(struct bt3c_info *info)
/* Send frame */
len = bt3c_write(iobase, 256, skb->data, skb->len);
- if (len != skb->len) {
+ if (len != skb->len)
BT_ERR("Very strange");
- }
kfree_skb(skb);
@@ -227,7 +226,6 @@ static void bt3c_receive(struct bt3c_info *info)
iobase = info->p_dev->resource[0]->start;
avail = bt3c_read(iobase, 0x7006);
- //printk("bt3c_cs: receiving %d bytes\n", avail);
bt3c_address(iobase, 0x7480);
while (size < avail) {
@@ -250,7 +248,6 @@ static void bt3c_receive(struct bt3c_info *info)
bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
inb(iobase + DATA_H);
- //printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
switch (bt_cb(info->rx_skb)->pkt_type) {
@@ -364,7 +361,6 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
if (stat & 0x0001)
bt3c_receive(info);
if (stat & 0x0002) {
- //BT_ERR("Ack (stat=0x%04x)", stat);
clear_bit(XMIT_SENDING, &(info->tx_state));
bt3c_write_wakeup(info);
}
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
new file mode 100644
index 000000000000..1e1a4323a71f
--- /dev/null
+++ b/drivers/bluetooth/btbcm.c
@@ -0,0 +1,491 @@
+/*
+ *
+ * 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}})
+#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
+
+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;
+
+ /* Check if the address indicates a controller with either an
+ * invalid or default address. In both cases the device needs
+ * to be marked as not having a valid address.
+ *
+ * The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
+ * with no configured address.
+ *
+ * The address 43:24:B3:00:00:00 indicates a BCM4324B3 controller
+ * with waiting for configuration state.
+ */
+ if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
+ !bacmp(&bda->bdaddr, BDADDR_BCM4324B3)) {
+ 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);
+
+int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
+{
+ const struct hci_command_hdr *cmd;
+ const u8 *fw_ptr;
+ size_t fw_size;
+ struct sk_buff *skb;
+ u16 opcode;
+ int err = 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 done;
+ }
+ 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 is corrupted", hdev->name);
+ err = -EINVAL;
+ goto done;
+ }
+
+ 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 done;
+ }
+ kfree_skb(skb);
+ }
+
+ /* 250 msec delay after Launch Ram completes */
+ msleep(250);
+
+done:
+ return err;
+}
+EXPORT_SYMBOL(btbcm_patchram);
+
+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 */
+ { 0x4406, "BCM4324B3" }, /* 002.004.006 */
+ { 0x610c, "BCM4354" }, /* 003.001.012 */
+ { }
+};
+
+int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
+{
+ u16 subver, rev;
+ 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:
+ case 1:
+ case 3:
+ 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, len, "brcm/%s.hcd", hw_name ? : "BCM");
+ 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);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_initialize);
+
+int btbcm_finalize(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ struct hci_rp_read_local_version *ver;
+ u16 subver, rev;
+ int 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);
+
+ BT_INFO("%s: BCM (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
+ (subver & 0x7000) >> 13, (subver & 0x1f00) >> 8,
+ (subver & 0x00ff), rev & 0x0fff);
+
+ btbcm_check_bdaddr(hdev);
+
+ set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_finalize);
+
+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)
+{
+ char fw_name[64];
+ const struct firmware *fw;
+ u16 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:
+ case 3:
+ 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;
+ }
+
+ btbcm_patchram(hdev, fw);
+
+ release_firmware(fw);
+
+ /* 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);
+
+ 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);
+
+ return 0;
+}
+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..d9e6b41658e5
--- /dev/null
+++ b/drivers/bluetooth/btbcm.h
@@ -0,0 +1,117 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#define BCM_UART_CLOCK_48MHZ 0x01
+#define BCM_UART_CLOCK_24MHZ 0x02
+
+struct bcm_update_uart_baud_rate {
+ __le16 zero;
+ __le32 baud_rate;
+} __packed;
+
+struct bcm_write_uart_clock_setting {
+ __u8 type;
+} __packed;
+
+struct bcm_set_sleep_mode {
+ __u8 sleep_mode;
+ __u8 idle_host;
+ __u8 idle_dev;
+ __u8 bt_wake_active;
+ __u8 host_wake_active;
+ __u8 allow_host_sleep;
+ __u8 combine_modes;
+ __u8 tristate_control;
+ __u8 usb_auto_sleep;
+ __u8 usb_resume_timeout;
+ __u8 pulsed_host_wake;
+ __u8 break_to_host;
+} __packed;
+
+struct bcm_set_pcm_int_params {
+ __u8 routing;
+ __u8 rate;
+ __u8 frame_sync;
+ __u8 sync_mode;
+ __u8 clock_mode;
+} __packed;
+
+struct bcm_set_pcm_format_params {
+ __u8 lsb_first;
+ __u8 fill_value;
+ __u8 fill_method;
+ __u8 fill_num;
+ __u8 right_justify;
+} __packed;
+
+#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_patchram(struct hci_dev *hdev, const struct firmware *fw);
+
+int btbcm_setup_patchram(struct hci_dev *hdev);
+int btbcm_setup_apple(struct hci_dev *hdev);
+
+int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len);
+int btbcm_finalize(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_patchram(struct hci_dev *hdev, const struct firmware *fw)
+{
+ 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;
+}
+
+static inline int btbcm_initialize(struct hci_dev *hdev, char *fw_name,
+ size_t len)
+{
+ return 0;
+}
+
+static inline int btbcm_finalize(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..828f2f8d1568
--- /dev/null
+++ b/drivers/bluetooth/btintel.c
@@ -0,0 +1,95 @@
+/*
+ *
+ * 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;
+
+ /* 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/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
index e75f8ee2512c..086f0ec89580 100644
--- a/drivers/bluetooth/btmrvl_drv.h
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -111,6 +111,7 @@ struct btmrvl_private {
/* Vendor specific Bluetooth commands */
#define BT_CMD_PSCAN_WIN_REPORT_ENABLE 0xFC03
+#define BT_CMD_ROUTE_SCO_TO_HOST 0xFC1D
#define BT_CMD_SET_BDADDR 0xFC22
#define BT_CMD_AUTO_SLEEP_MODE 0xFC23
#define BT_CMD_HOST_SLEEP_CONFIG 0xFC59
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 413597789c61..de05deb444ce 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -230,6 +230,18 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)
}
EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
+static int btmrvl_enable_sco_routing_to_host(struct btmrvl_private *priv)
+{
+ int ret;
+ u8 subcmd = 0;
+
+ ret = btmrvl_send_sync_cmd(priv, BT_CMD_ROUTE_SCO_TO_HOST, &subcmd, 1);
+ if (ret)
+ BT_ERR("BT_CMD_ROUTE_SCO_TO_HOST command failed: %#x", ret);
+
+ return ret;
+}
+
int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd)
{
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
@@ -558,6 +570,8 @@ static int btmrvl_setup(struct hci_dev *hdev)
btmrvl_check_device_tree(priv);
+ btmrvl_enable_sco_routing_to_host(priv);
+
btmrvl_pscan_window_reporting(priv, 0x01);
priv->btmrvl_dev.psmode = 1;
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 01d6da577eeb..b9a811900f6a 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -1217,7 +1217,7 @@ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
unsigned int reg, reg_start, reg_end;
enum rdwr_status stat;
u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr;
- u8 dump_num, idx, i, read_reg, doneflag = 0;
+ u8 dump_num = 0, idx, i, read_reg, doneflag = 0;
u32 memory_size, fw_dump_len = 0;
/* dump sdio register first */
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
new file mode 100644
index 000000000000..84288938f7f2
--- /dev/null
+++ b/drivers/bluetooth/btrtl.c
@@ -0,0 +1,390 @@
+/*
+ * Bluetooth support for Realtek devices
+ *
+ * Copyright (C) 2015 Endless Mobile, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btrtl.h"
+
+#define VERSION "0.1"
+
+#define RTL_EPATCH_SIGNATURE "Realtech"
+#define RTL_ROM_LMP_3499 0x3499
+#define RTL_ROM_LMP_8723A 0x1200
+#define RTL_ROM_LMP_8723B 0x8723
+#define RTL_ROM_LMP_8821A 0x8821
+#define RTL_ROM_LMP_8761A 0x8761
+
+static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
+{
+ struct rtl_rom_version_evt *rom_version;
+ struct sk_buff *skb;
+
+ /* Read RTL ROM version command */
+ skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: Read ROM version failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ if (skb->len != sizeof(*rom_version)) {
+ BT_ERR("%s: RTL version event length mismatch", hdev->name);
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ rom_version = (struct rtl_rom_version_evt *)skb->data;
+ BT_INFO("%s: rom_version status=%x version=%x",
+ hdev->name, rom_version->status, rom_version->version);
+
+ *version = rom_version->version;
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
+ const struct firmware *fw,
+ unsigned char **_buf)
+{
+ const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
+ struct rtl_epatch_header *epatch_info;
+ unsigned char *buf;
+ int i, ret, len;
+ size_t min_size;
+ u8 opcode, length, data, rom_version = 0;
+ int project_id = -1;
+ const unsigned char *fwptr, *chip_id_base;
+ const unsigned char *patch_length_base, *patch_offset_base;
+ u32 patch_offset = 0;
+ u16 patch_length, num_patches;
+ const u16 project_id_to_lmp_subver[] = {
+ RTL_ROM_LMP_8723A,
+ RTL_ROM_LMP_8723B,
+ RTL_ROM_LMP_8821A,
+ RTL_ROM_LMP_8761A
+ };
+
+ ret = rtl_read_rom_version(hdev, &rom_version);
+ if (ret)
+ return ret;
+
+ min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
+ if (fw->size < min_size)
+ return -EINVAL;
+
+ fwptr = fw->data + fw->size - sizeof(extension_sig);
+ if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
+ BT_ERR("%s: extension section signature mismatch", hdev->name);
+ return -EINVAL;
+ }
+
+ /* Loop from the end of the firmware parsing instructions, until
+ * we find an instruction that identifies the "project ID" for the
+ * hardware supported by this firwmare file.
+ * Once we have that, we double-check that that project_id is suitable
+ * for the hardware we are working with.
+ */
+ while (fwptr >= fw->data + (sizeof(struct rtl_epatch_header) + 3)) {
+ opcode = *--fwptr;
+ length = *--fwptr;
+ data = *--fwptr;
+
+ BT_DBG("check op=%x len=%x data=%x", opcode, length, data);
+
+ if (opcode == 0xff) /* EOF */
+ break;
+
+ if (length == 0) {
+ BT_ERR("%s: found instruction with length 0",
+ hdev->name);
+ return -EINVAL;
+ }
+
+ if (opcode == 0 && length == 1) {
+ project_id = data;
+ break;
+ }
+
+ fwptr -= length;
+ }
+
+ if (project_id < 0) {
+ BT_ERR("%s: failed to find version instruction", hdev->name);
+ return -EINVAL;
+ }
+
+ if (project_id >= ARRAY_SIZE(project_id_to_lmp_subver)) {
+ BT_ERR("%s: unknown project id %d", hdev->name, project_id);
+ return -EINVAL;
+ }
+
+ if (lmp_subver != project_id_to_lmp_subver[project_id]) {
+ BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
+ project_id_to_lmp_subver[project_id], lmp_subver);
+ return -EINVAL;
+ }
+
+ epatch_info = (struct rtl_epatch_header *)fw->data;
+ if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
+ BT_ERR("%s: bad EPATCH signature", hdev->name);
+ return -EINVAL;
+ }
+
+ num_patches = le16_to_cpu(epatch_info->num_patches);
+ BT_DBG("fw_version=%x, num_patches=%d",
+ le32_to_cpu(epatch_info->fw_version), num_patches);
+
+ /* After the rtl_epatch_header there is a funky patch metadata section.
+ * Assuming 2 patches, the layout is:
+ * ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2
+ *
+ * Find the right patch for this chip.
+ */
+ min_size += 8 * num_patches;
+ if (fw->size < min_size)
+ return -EINVAL;
+
+ chip_id_base = fw->data + sizeof(struct rtl_epatch_header);
+ patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
+ patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
+ for (i = 0; i < num_patches; i++) {
+ u16 chip_id = get_unaligned_le16(chip_id_base +
+ (i * sizeof(u16)));
+ if (chip_id == rom_version + 1) {
+ patch_length = get_unaligned_le16(patch_length_base +
+ (i * sizeof(u16)));
+ patch_offset = get_unaligned_le32(patch_offset_base +
+ (i * sizeof(u32)));
+ break;
+ }
+ }
+
+ if (!patch_offset) {
+ BT_ERR("%s: didn't find patch for chip id %d",
+ hdev->name, rom_version);
+ return -EINVAL;
+ }
+
+ BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
+ min_size = patch_offset + patch_length;
+ if (fw->size < min_size)
+ return -EINVAL;
+
+ /* Copy the firmware into a new buffer and write the version at
+ * the end.
+ */
+ len = patch_length;
+ buf = kmemdup(fw->data + patch_offset, patch_length, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
+
+ *_buf = buf;
+ return len;
+}
+
+static int rtl_download_firmware(struct hci_dev *hdev,
+ const unsigned char *data, int fw_len)
+{
+ struct rtl_download_cmd *dl_cmd;
+ int frag_num = fw_len / RTL_FRAG_LEN + 1;
+ int frag_len = RTL_FRAG_LEN;
+ int ret = 0;
+ int i;
+
+ dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL);
+ if (!dl_cmd)
+ return -ENOMEM;
+
+ for (i = 0; i < frag_num; i++) {
+ struct sk_buff *skb;
+
+ BT_DBG("download fw (%d/%d)", i, frag_num);
+
+ dl_cmd->index = i;
+ if (i == (frag_num - 1)) {
+ dl_cmd->index |= 0x80; /* data end */
+ frag_len = fw_len % RTL_FRAG_LEN;
+ }
+ memcpy(dl_cmd->data, data, frag_len);
+
+ /* Send download command */
+ skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: download fw command failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ ret = -PTR_ERR(skb);
+ goto out;
+ }
+
+ if (skb->len != sizeof(struct rtl_download_response)) {
+ BT_ERR("%s: download fw event length mismatch",
+ hdev->name);
+ kfree_skb(skb);
+ ret = -EIO;
+ goto out;
+ }
+
+ kfree_skb(skb);
+ data += RTL_FRAG_LEN;
+ }
+
+out:
+ kfree(dl_cmd);
+ return ret;
+}
+
+static int btrtl_setup_rtl8723a(struct hci_dev *hdev)
+{
+ const struct firmware *fw;
+ int ret;
+
+ BT_INFO("%s: rtl: loading rtl_bt/rtl8723a_fw.bin", hdev->name);
+ ret = request_firmware(&fw, "rtl_bt/rtl8723a_fw.bin", &hdev->dev);
+ if (ret < 0) {
+ BT_ERR("%s: Failed to load rtl_bt/rtl8723a_fw.bin", hdev->name);
+ return ret;
+ }
+
+ if (fw->size < 8) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Check that the firmware doesn't have the epatch signature
+ * (which is only for RTL8723B and newer).
+ */
+ if (!memcmp(fw->data, RTL_EPATCH_SIGNATURE, 8)) {
+ BT_ERR("%s: unexpected EPATCH signature!", hdev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = rtl_download_firmware(hdev, fw->data, fw->size);
+
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
+ const char *fw_name)
+{
+ unsigned char *fw_data = NULL;
+ const struct firmware *fw;
+ int ret;
+
+ BT_INFO("%s: rtl: loading %s", hdev->name, fw_name);
+ ret = request_firmware(&fw, fw_name, &hdev->dev);
+ if (ret < 0) {
+ BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
+ return ret;
+ }
+
+ ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
+ if (ret < 0)
+ goto out;
+
+ ret = rtl_download_firmware(hdev, fw_data, ret);
+ kfree(fw_data);
+ if (ret < 0)
+ goto out;
+
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static struct sk_buff *btrtl_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: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return skb;
+ }
+
+ if (skb->len != sizeof(struct hci_rp_read_local_version)) {
+ BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
+ hdev->name);
+ kfree_skb(skb);
+ return ERR_PTR(-EIO);
+ }
+
+ return skb;
+}
+
+int btrtl_setup_realtek(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ struct hci_rp_read_local_version *resp;
+ u16 lmp_subver;
+
+ skb = btrtl_read_local_version(hdev);
+ if (IS_ERR(skb))
+ return -PTR_ERR(skb);
+
+ resp = (struct hci_rp_read_local_version *)skb->data;
+ BT_INFO("%s: rtl: examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+ "lmp_subver=%04x", hdev->name, resp->hci_ver, resp->hci_rev,
+ resp->lmp_ver, resp->lmp_subver);
+
+ lmp_subver = le16_to_cpu(resp->lmp_subver);
+ kfree_skb(skb);
+
+ /* Match a set of subver values that correspond to stock firmware,
+ * which is not compatible with standard btusb.
+ * If matched, upload an alternative firmware that does conform to
+ * standard btusb. Once that firmware is uploaded, the subver changes
+ * to a different value.
+ */
+ switch (lmp_subver) {
+ case RTL_ROM_LMP_8723A:
+ case RTL_ROM_LMP_3499:
+ return btrtl_setup_rtl8723a(hdev);
+ case RTL_ROM_LMP_8723B:
+ return btrtl_setup_rtl8723b(hdev, lmp_subver,
+ "rtl_bt/rtl8723b_fw.bin");
+ case RTL_ROM_LMP_8821A:
+ return btrtl_setup_rtl8723b(hdev, lmp_subver,
+ "rtl_bt/rtl8821a_fw.bin");
+ case RTL_ROM_LMP_8761A:
+ return btrtl_setup_rtl8723b(hdev, lmp_subver,
+ "rtl_bt/rtl8761a_fw.bin");
+ default:
+ BT_INFO("rtl: assuming no firmware upload needed.");
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
+
+MODULE_AUTHOR("Daniel Drake <drake@endlessm.com>");
+MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
new file mode 100644
index 000000000000..38ffe4890cd1
--- /dev/null
+++ b/drivers/bluetooth/btrtl.h
@@ -0,0 +1,52 @@
+/*
+ * Bluetooth support for Realtek devices
+ *
+ * Copyright (C) 2015 Endless Mobile, Inc.
+ *
+ * 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.
+ *
+ */
+
+#define RTL_FRAG_LEN 252
+
+struct rtl_download_cmd {
+ __u8 index;
+ __u8 data[RTL_FRAG_LEN];
+} __packed;
+
+struct rtl_download_response {
+ __u8 status;
+ __u8 index;
+} __packed;
+
+struct rtl_rom_version_evt {
+ __u8 status;
+ __u8 version;
+} __packed;
+
+struct rtl_epatch_header {
+ __u8 signature[8];
+ __le32 fw_version;
+ __le16 num_patches;
+} __packed;
+
+#if IS_ENABLED(CONFIG_BT_RTL)
+
+int btrtl_setup_realtek(struct hci_dev *hdev);
+
+#else
+
+static inline int btrtl_setup_realtek(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 8bfc4c2bba87..b4cf8d9c9dac 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -24,16 +24,21 @@
#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"
+#include "btrtl.h"
+
+#define VERSION "0.8"
static bool disable_scofix;
static bool force_scofix;
-static bool reset = 1;
+static bool reset = true;
static struct usb_driver btusb_driver;
@@ -52,6 +57,9 @@ static struct usb_driver btusb_driver;
#define BTUSB_SWAVE 0x1000
#define BTUSB_INTEL_NEW 0x2000
#define BTUSB_AMP 0x4000
+#define BTUSB_QCA_ROME 0x8000
+#define BTUSB_BCM_APPLE 0x10000
+#define BTUSB_REALTEK 0x20000
static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@@ -61,7 +69,8 @@ static const struct usb_device_id btusb_table[] = {
{ USB_DEVICE_INFO(0xe0, 0x01, 0x04), .driver_info = BTUSB_AMP },
/* Apple-specific (Broadcom) devices */
- { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_APPLE },
/* MediaTek MT76x0E */
{ USB_DEVICE(0x0e8d, 0x763f) },
@@ -107,13 +116,7 @@ static const struct usb_device_id btusb_table[] = {
{ USB_DEVICE(0x0c10, 0x0000) },
/* Broadcom BCM20702A0 */
- { USB_DEVICE(0x0489, 0xe042) },
- { USB_DEVICE(0x04ca, 0x2003) },
- { USB_DEVICE(0x0b05, 0x17b5) },
- { USB_DEVICE(0x0b05, 0x17cb) },
{ USB_DEVICE(0x413c, 0x8197) },
- { USB_DEVICE(0x13d3, 0x3404),
- .driver_info = BTUSB_BCM_PATCHRAM },
/* Broadcom BCM20702B0 (Dynex/Insignia) */
{ USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
@@ -135,10 +138,12 @@ static const struct usb_device_id btusb_table[] = {
.driver_info = BTUSB_BCM_PATCHRAM },
/* Belkin F8065bf - Broadcom based */
- { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
/* IMC Networks - Broadcom based */
- { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
/* Intel Bluetooth USB Bootloader (RAM module) */
{ USB_DEVICE(0x8087, 0x0a5a),
@@ -159,6 +164,7 @@ static const struct usb_device_id blacklist_table[] = {
/* Atheros 3011 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x04f2, 0xaff1), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE },
@@ -173,6 +179,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
@@ -181,6 +188,8 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
@@ -197,6 +206,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
@@ -204,6 +214,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
@@ -212,6 +223,11 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
+ /* QCA ROME chipset */
+ { USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME },
+ { USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
+ { USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
+
/* Broadcom BCM2035 */
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
@@ -253,7 +269,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
/* Roper Class 1 Bluetooth Dongle (Silicon Wave based) */
- { USB_DEVICE(0x1300, 0x0001), .driver_info = BTUSB_SWAVE },
+ { USB_DEVICE(0x1310, 0x0001), .driver_info = BTUSB_SWAVE },
/* Digianswer devices */
{ USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
@@ -281,6 +297,28 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x8087, 0xe0, 0x01, 0x01),
.driver_info = BTUSB_IGNORE },
+ /* Realtek Bluetooth devices */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
+ .driver_info = BTUSB_REALTEK },
+
+ /* Additional Realtek 8723AE Bluetooth devices */
+ { USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
+
+ /* Additional Realtek 8723BE Bluetooth devices */
+ { USB_DEVICE(0x0489, 0xe085), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x0489, 0xe08b), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3410), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3416), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
+
+ /* Additional Realtek 8821AE Bluetooth devices */
+ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3458), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK },
+
{ } /* Terminating entry */
};
@@ -296,6 +334,7 @@ static const struct usb_device_id blacklist_table[] = {
#define BTUSB_FIRMWARE_LOADED 7
#define BTUSB_FIRMWARE_FAILED 8
#define BTUSB_BOOTING 9
+#define BTUSB_RESET_RESUME 10
struct btusb_data {
struct hci_dev *hdev;
@@ -337,17 +376,9 @@ struct btusb_data {
int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
-};
-static int btusb_wait_on_bit_timeout(void *word, int bit, unsigned long timeout,
- unsigned mode)
-{
- might_sleep();
- if (!test_bit(bit, word))
- return 0;
- return out_of_line_wait_on_bit_timeout(word, bit, bit_wait_timeout,
- mode, timeout);
-}
+ int (*setup_on_usb)(struct hci_dev *hdev);
+};
static inline void btusb_free_frags(struct btusb_data *data)
{
@@ -888,6 +919,15 @@ static int btusb_open(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
+ /* Patching USB firmware files prior to starting any URBs of HCI path
+ * It is more safe to use USB bulk channel for downloading USB patch
+ */
+ if (data->setup_on_usb) {
+ err = data->setup_on_usb(hdev);
+ if (err < 0)
+ return err;
+ }
+
err = usb_autopm_get_interface(data->intf);
if (err < 0)
return err;
@@ -1283,74 +1323,42 @@ static int btusb_setup_csr(struct hci_dev *hdev)
{
struct hci_rp_read_local_version *rp;
struct sk_buff *skb;
- int ret;
BT_DBG("%s", hdev->name);
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
- BT_ERR("Reading local version failed (%ld)", -PTR_ERR(skb));
- return -PTR_ERR(skb);
+ int err = PTR_ERR(skb);
+ BT_ERR("%s: CSR: Local version failed (%d)", hdev->name, err);
+ return err;
+ }
+
+ if (skb->len != sizeof(struct hci_rp_read_local_version)) {
+ BT_ERR("%s: CSR: Local version length mismatch", hdev->name);
+ kfree_skb(skb);
+ return -EIO;
}
rp = (struct hci_rp_read_local_version *)skb->data;
- if (!rp->status) {
- if (le16_to_cpu(rp->manufacturer) != 10) {
- /* Clear the reset quirk since this is not an actual
- * early Bluetooth 1.1 device from CSR.
- */
- clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+ if (le16_to_cpu(rp->manufacturer) != 10) {
+ /* Clear the reset quirk since this is not an actual
+ * early Bluetooth 1.1 device from CSR.
+ */
+ clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
- /* These fake CSR controllers have all a broken
- * stored link key handling and so just disable it.
- */
- set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY,
- &hdev->quirks);
- }
+ /* These fake CSR controllers have all a broken
+ * stored link key handling and so just disable it.
+ */
+ set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
}
- ret = -bt_to_errno(rp->status);
-
kfree_skb(skb);
- return ret;
+ return 0;
}
-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)
{
@@ -1507,51 +1515,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;
@@ -1603,12 +1566,6 @@ static int btusb_setup_intel(struct hci_dev *hdev)
}
ver = (struct intel_version *)skb->data;
- if (ver->status) {
- BT_ERR("%s Intel fw version event failed (%02x)", hdev->name,
- ver->status);
- kfree_skb(skb);
- return -bt_to_errno(ver->status);
- }
BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
hdev->name, ver->hw_platform, ver->hw_variant,
@@ -1624,7 +1581,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;
}
@@ -1637,11 +1594,13 @@ 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;
+ kfree_skb(skb);
+
/* This Intel specific command enables the manufacturer mode of the
* controller.
*
@@ -1656,15 +1615,6 @@ static int btusb_setup_intel(struct hci_dev *hdev)
return PTR_ERR(skb);
}
- if (skb->data[0]) {
- u8 evt_status = skb->data[0];
-
- BT_ERR("%s enable Intel manufacturer mode event failed (%02x)",
- hdev->name, evt_status);
- kfree_skb(skb);
- release_firmware(fw);
- return -bt_to_errno(evt_status);
- }
kfree_skb(skb);
disable_patch = 1;
@@ -1718,7 +1668,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:
@@ -1734,7 +1684,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:
@@ -1755,7 +1705,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;
}
@@ -1797,6 +1747,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);
@@ -1804,32 +1786,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;
}
}
}
@@ -1956,6 +1933,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
struct intel_boot_params *params;
const struct firmware *fw;
const u8 *fw_ptr;
+ u32 frag_len;
char fwname[64];
ktime_t calltime, delta, rettime;
unsigned long long duration;
@@ -1983,13 +1961,6 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
}
ver = (struct intel_version *)skb->data;
- if (ver->status) {
- BT_ERR("%s: Intel version command failure (%02x)",
- hdev->name, ver->status);
- err = -bt_to_errno(ver->status);
- kfree_skb(skb);
- return err;
- }
/* The hardware platform number has a fixed value of 0x37 and
* for now only accept this single value.
@@ -2031,7 +2002,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;
}
@@ -2064,13 +2035,6 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
}
params = (struct intel_boot_params *)skb->data;
- if (params->status) {
- BT_ERR("%s: Intel boot parameters command failure (%02x)",
- hdev->name, params->status);
- err = -bt_to_errno(params->status);
- kfree_skb(skb);
- return err;
- }
BT_INFO("%s: Device revision is %u", hdev->name,
le16_to_cpu(params->dev_revid));
@@ -2120,6 +2084,12 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
BT_INFO("%s: Found device firmware: %s", hdev->name, fwname);
+ /* Save the DDC file name for later use to apply once the firmware
+ * downloading is done.
+ */
+ snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.ddc",
+ le16_to_cpu(params->dev_revid));
+
kfree_skb(skb);
if (fw->size < 644) {
@@ -2162,24 +2132,33 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
}
fw_ptr = fw->data + 644;
+ frag_len = 0;
while (fw_ptr - fw->data < fw->size) {
- struct hci_command_hdr *cmd = (void *)fw_ptr;
- u8 cmd_len;
+ struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);
- cmd_len = sizeof(*cmd) + cmd->plen;
+ frag_len += sizeof(*cmd) + cmd->plen;
- /* Send each command from the firmware data buffer as
- * a single Data fragment.
+ /* The paramter length of the secure send command requires
+ * a 4 byte alignment. It happens so that the firmware file
+ * contains proper Intel_NOP commands to align the fragments
+ * as needed.
+ *
+ * Send set of commands with 4 byte alignment from the
+ * firmware data buffer as a single Data fragement.
*/
- err = btusb_intel_secure_send(hdev, 0x01, cmd_len, fw_ptr);
- if (err < 0) {
- BT_ERR("%s: Failed to send firmware data (%d)",
- hdev->name, err);
- goto done;
- }
+ if (!(frag_len % 4)) {
+ err = btusb_intel_secure_send(hdev, 0x01, frag_len,
+ fw_ptr);
+ if (err < 0) {
+ BT_ERR("%s: Failed to send firmware data (%d)",
+ hdev->name, err);
+ goto done;
+ }
- fw_ptr += cmd_len;
+ fw_ptr += frag_len;
+ frag_len = 0;
+ }
}
set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
@@ -2197,9 +2176,9 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* and thus just timeout if that happens and fail the setup
* of this device.
*/
- err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
- msecs_to_jiffies(5000),
- TASK_INTERRUPTIBLE);
+ err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
+ TASK_INTERRUPTIBLE,
+ msecs_to_jiffies(5000));
if (err == 1) {
BT_ERR("%s: Firmware loading interrupted", hdev->name);
err = -EINTR;
@@ -2250,9 +2229,9 @@ done:
*/
BT_INFO("%s: Waiting for device to boot", hdev->name);
- err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
- msecs_to_jiffies(1000),
- TASK_INTERRUPTIBLE);
+ err = wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
+ TASK_INTERRUPTIBLE,
+ msecs_to_jiffies(1000));
if (err == 1) {
BT_ERR("%s: Device boot interrupted", hdev->name);
@@ -2272,6 +2251,43 @@ done:
clear_bit(BTUSB_BOOTLOADER, &data->flags);
+ /* Once the device is running in operational mode, it needs to apply
+ * the device configuration (DDC) parameters.
+ *
+ * The device can work without DDC parameters, so even if it fails
+ * to load the file, no need to fail the setup.
+ */
+ err = request_firmware_direct(&fw, fwname, &hdev->dev);
+ if (err < 0)
+ return 0;
+
+ BT_INFO("%s: Found Intel DDC parameters: %s", hdev->name, fwname);
+
+ fw_ptr = fw->data;
+
+ /* DDC file contains one or more DDC structure which has
+ * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
+ */
+ while (fw->size > fw_ptr - fw->data) {
+ u8 cmd_plen = fw_ptr[0] + sizeof(u8);
+
+ skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: Failed to send Intel_Write_DDC (%ld)",
+ hdev->name, PTR_ERR(skb));
+ release_firmware(fw);
+ return PTR_ERR(skb);
+ }
+
+ fw_ptr += cmd_plen;
+ kfree_skb(skb);
+ }
+
+ release_firmware(fw);
+
+ BT_INFO("%s: Applying Intel DDC parameters completed", hdev->name);
+
return 0;
}
@@ -2303,27 +2319,24 @@ static void btusb_hw_error_intel(struct hci_dev *hdev, u8 code)
return;
}
- if (skb->data[0] != 0x00) {
- BT_ERR("%s: Exception info command failure (%02x)",
- hdev->name, skb->data[0]);
- kfree_skb(skb);
- return;
- }
-
BT_ERR("%s: Exception info %s", hdev->name, (char *)(skb->data + 1));
kfree_skb(skb);
}
-static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+static int btusb_shutdown_intel(struct hci_dev *hdev)
{
struct sk_buff *skb;
long ret;
- skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
+ /* Some platforms have an issue with BT LED when the interface is
+ * down or BT radio is turned off, which takes 5 seconds to BT LED
+ * goes off. This command turns off the BT LED immediately.
+ */
+ skb = __hci_cmd_sync(hdev, 0xfc3f, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
- BT_ERR("%s: changing Intel device address failed (%ld)",
+ BT_ERR("%s: turning off Intel device LED failed (%ld)",
hdev->name, ret);
return ret;
}
@@ -2355,232 +2368,280 @@ static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
return 0;
}
-#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
-
-static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
+static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
+ const bdaddr_t *bdaddr)
{
- 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;
struct sk_buff *skb;
- struct hci_rp_read_local_version *ver;
- struct hci_rp_read_bd_addr *bda;
+ u8 buf[10];
long ret;
- snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd",
- udev->product ? udev->product : "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;
- }
+ buf[0] = 0x01;
+ buf[1] = 0x01;
+ buf[2] = 0x00;
+ buf[3] = sizeof(bdaddr_t);
+ memcpy(buf + 4, bdaddr, sizeof(bdaddr_t));
- /* Reset */
- skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
- BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
- goto done;
+ BT_ERR("%s: Change address command failed (%ld)",
+ hdev->name, ret);
+ return ret;
}
kfree_skb(skb);
- /* Read Local Version Info */
- skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
- hdev->name, ret);
- goto done;
- }
+ return 0;
+}
- if (skb->len != sizeof(*ver)) {
- BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
- hdev->name);
- kfree_skb(skb);
- ret = -EIO;
- goto done;
- }
+#define QCA_DFU_PACKET_LEN 4096
- ver = (struct hci_rp_read_local_version *)skb->data;
- BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
- "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
- ver->lmp_ver, ver->lmp_subver);
- kfree_skb(skb);
+#define QCA_GET_TARGET_VERSION 0x09
+#define QCA_CHECK_STATUS 0x05
+#define QCA_DFU_DOWNLOAD 0x01
- /* 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);
+#define QCA_SYSCFG_UPDATED 0x40
+#define QCA_PATCH_UPDATED 0x80
+#define QCA_DFU_TIMEOUT 3000
- /* 50 msec delay after Download Minidrv completes */
- msleep(50);
+struct qca_version {
+ __le32 rom_version;
+ __le32 patch_version;
+ __le32 ram_version;
+ __le32 ref_clock;
+ __u8 reserved[4];
+} __packed;
- 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;
- }
+struct qca_rampatch_version {
+ __le16 rom_version;
+ __le16 patch_version;
+} __packed;
- cmd_param = fw_ptr;
- fw_ptr += cmd->plen;
- fw_size -= cmd->plen;
+struct qca_device_info {
+ u32 rom_version;
+ u8 rampatch_hdr; /* length of header in rampatch */
+ u8 nvm_hdr; /* length of header in NVM */
+ u8 ver_offset; /* offset of version structure in rampatch */
+};
- opcode = le16_to_cpu(cmd->opcode);
+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 */
+};
- 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);
- }
+static int btusb_qca_send_vendor_req(struct hci_dev *hdev, u8 request,
+ void *data, u16 size)
+{
+ struct btusb_data *btdata = hci_get_drvdata(hdev);
+ struct usb_device *udev = btdata->udev;
+ int pipe, err;
+ u8 *buf;
- /* 250 msec delay after Launch Ram completes */
- msleep(250);
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
-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);
+ /* Found some of USB hosts have IOT issues with ours so that we should
+ * not wait until HCI layer is ready.
+ */
+ pipe = usb_rcvctrlpipe(udev, 0);
+ err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN,
+ 0, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("%s: Failed to access otp area (%d)", hdev->name, err);
goto done;
}
- kfree_skb(skb);
- /* Read Local Version Info */
- skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
- hdev->name, ret);
- goto done;
- }
+ memcpy(data, buf, size);
- if (skb->len != sizeof(*ver)) {
- BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
- hdev->name);
- kfree_skb(skb);
- ret = -EIO;
- goto done;
- }
+done:
+ kfree(buf);
- ver = (struct hci_rp_read_local_version *)skb->data;
- BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
- "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
- ver->lmp_ver, ver->lmp_subver);
- kfree_skb(skb);
+ return err;
+}
- /* 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);
+static int btusb_setup_qca_download_fw(struct hci_dev *hdev,
+ const struct firmware *firmware,
+ size_t hdr_size)
+{
+ struct btusb_data *btdata = hci_get_drvdata(hdev);
+ struct usb_device *udev = btdata->udev;
+ size_t count, size, sent = 0;
+ int pipe, len, err;
+ u8 *buf;
+
+ buf = kmalloc(QCA_DFU_PACKET_LEN, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ count = firmware->size;
+
+ size = min_t(size_t, count, hdr_size);
+ memcpy(buf, firmware->data, size);
+
+ /* USB patches should go down to controller through USB path
+ * because binary format fits to go down through USB channel.
+ * USB control path is for patching headers and USB bulk is for
+ * patch body.
+ */
+ pipe = usb_sndctrlpipe(udev, 0);
+ err = usb_control_msg(udev, pipe, QCA_DFU_DOWNLOAD, USB_TYPE_VENDOR,
+ 0, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("%s: Failed to send headers (%d)", hdev->name, err);
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;
+ sent += size;
+ count -= size;
+
+ while (count) {
+ size = min_t(size_t, count, QCA_DFU_PACKET_LEN);
+
+ memcpy(buf, firmware->data + sent, size);
+
+ pipe = usb_sndbulkpipe(udev, 0x02);
+ err = usb_bulk_msg(udev, pipe, buf, size, &len,
+ QCA_DFU_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("%s: Failed to send body at %zd of %zd (%d)",
+ hdev->name, sent, firmware->size, err);
+ break;
+ }
+
+ if (size != len) {
+ BT_ERR("%s: Failed to get bulk buffer", hdev->name);
+ err = -EILSEQ;
+ break;
+ }
+
+ sent += size;
+ count -= size;
}
- 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;
+done:
+ kfree(buf);
+ return err;
+}
+
+static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
+ struct qca_version *ver,
+ const struct qca_device_info *info)
+{
+ struct qca_rampatch_version *rver;
+ const struct firmware *fw;
+ u32 ver_rom, ver_patch;
+ u16 rver_rom, rver_patch;
+ char fwname[64];
+ int err;
+
+ ver_rom = le32_to_cpu(ver->rom_version);
+ ver_patch = le32_to_cpu(ver->patch_version);
+
+ snprintf(fwname, sizeof(fwname), "qca/rampatch_usb_%08x.bin", ver_rom);
+
+ err = request_firmware(&fw, fwname, &hdev->dev);
+ if (err) {
+ BT_ERR("%s: failed to request rampatch file: %s (%d)",
+ hdev->name, fwname, err);
+ return err;
}
- /* 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);
+ BT_INFO("%s: using rampatch file: %s", hdev->name, fwname);
+
+ rver = (struct qca_rampatch_version *)(fw->data + info->ver_offset);
+ rver_rom = le16_to_cpu(rver->rom_version);
+ rver_patch = le16_to_cpu(rver->patch_version);
+
+ BT_INFO("%s: QCA: patch rome 0x%x build 0x%x, firmware rome 0x%x "
+ "build 0x%x", hdev->name, rver_rom, rver_patch, ver_rom,
+ ver_patch);
+
+ if (rver_rom != ver_rom || rver_patch <= ver_patch) {
+ BT_ERR("%s: rampatch file version did not match with firmware",
+ hdev->name);
+ err = -EINVAL;
+ goto done;
}
- kfree_skb(skb);
+ err = btusb_setup_qca_download_fw(hdev, fw, info->rampatch_hdr);
done:
release_firmware(fw);
- return ret;
+ return err;
}
-static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+static int btusb_setup_qca_load_nvm(struct hci_dev *hdev,
+ struct qca_version *ver,
+ const struct qca_device_info *info)
{
- struct sk_buff *skb;
- long ret;
+ const struct firmware *fw;
+ char fwname[64];
+ int err;
- 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;
+ snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x.bin",
+ le32_to_cpu(ver->rom_version));
+
+ err = request_firmware(&fw, fwname, &hdev->dev);
+ if (err) {
+ BT_ERR("%s: failed to request NVM file: %s (%d)",
+ hdev->name, fwname, err);
+ return err;
}
- kfree_skb(skb);
- return 0;
+ BT_INFO("%s: using NVM file: %s", hdev->name, fwname);
+
+ err = btusb_setup_qca_download_fw(hdev, fw, info->nvm_hdr);
+
+ release_firmware(fw);
+
+ return err;
}
-static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
- const bdaddr_t *bdaddr)
+static int btusb_setup_qca(struct hci_dev *hdev)
{
- struct sk_buff *skb;
- u8 buf[10];
- long ret;
+ const struct qca_device_info *info = NULL;
+ struct qca_version ver;
+ u32 ver_rom;
+ u8 status;
+ int i, err;
- buf[0] = 0x01;
- buf[1] = 0x01;
- buf[2] = 0x00;
- buf[3] = sizeof(bdaddr_t);
- memcpy(buf + 4, bdaddr, sizeof(bdaddr_t));
+ err = btusb_qca_send_vendor_req(hdev, QCA_GET_TARGET_VERSION, &ver,
+ sizeof(ver));
+ if (err < 0)
+ return err;
- skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: Change address command failed (%ld)",
- hdev->name, ret);
- return ret;
+ ver_rom = le32_to_cpu(ver.rom_version);
+ for (i = 0; i < ARRAY_SIZE(qca_devices_table); i++) {
+ if (ver_rom == qca_devices_table[i].rom_version)
+ info = &qca_devices_table[i];
+ }
+ if (!info) {
+ BT_ERR("%s: don't support firmware rome 0x%x", hdev->name,
+ ver_rom);
+ return -ENODEV;
+ }
+
+ err = btusb_qca_send_vendor_req(hdev, QCA_CHECK_STATUS, &status,
+ sizeof(status));
+ if (err < 0)
+ return err;
+
+ if (!(status & QCA_PATCH_UPDATED)) {
+ err = btusb_setup_qca_load_rampatch(hdev, &ver, info);
+ if (err < 0)
+ return err;
+ }
+
+ if (!(status & QCA_SYSCFG_UPDATED)) {
+ err = btusb_setup_qca_load_nvm(hdev, &ver, info);
+ if (err < 0)
+ return err;
}
- kfree_skb(skb);
return 0;
}
@@ -2701,23 +2762,29 @@ 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 = btbcm_setup_apple;
+#endif
+
if (id->driver_info & BTUSB_INTEL) {
hdev->setup = btusb_setup_intel;
- hdev->set_bdaddr = btusb_set_bdaddr_intel;
+ hdev->shutdown = btusb_shutdown_intel;
+ hdev->set_bdaddr = btintel_set_bdaddr;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+ set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
}
if (id->driver_info & BTUSB_INTEL_NEW) {
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);
}
@@ -2734,9 +2801,27 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_ATH3012) {
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
+ set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
}
+ if (id->driver_info & BTUSB_QCA_ROME) {
+ data->setup_on_usb = btusb_setup_qca;
+ hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
+ }
+
+#ifdef CONFIG_BT_HCIBTUSB_RTL
+ if (id->driver_info & BTUSB_REALTEK) {
+ hdev->setup = btrtl_setup_realtek;
+
+ /* Realtek devices lose their updated firmware over suspend,
+ * but the USB hub doesn't notice any status change.
+ * Explicitly request a device reset on resume.
+ */
+ set_bit(BTUSB_RESET_RESUME, &data->flags);
+ }
+#endif
+
if (id->driver_info & BTUSB_AMP) {
/* AMP controllers do not support SCO packets */
data->isoc = NULL;
@@ -2772,6 +2857,8 @@ static int btusb_probe(struct usb_interface *intf,
/* Fake CSR devices with broken commands */
if (bcdDevice <= 0x100)
hdev->setup = btusb_setup_csr;
+
+ set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
}
if (id->driver_info & BTUSB_SNIFFER) {
@@ -2865,6 +2952,14 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
+ /* Optionally request a device reset on resume, but only when
+ * wakeups are disabled. If wakeups are enabled we assume the
+ * device will stay powered up throughout suspend.
+ */
+ if (test_bit(BTUSB_RESET_RESUME, &data->flags) &&
+ !device_may_wakeup(&data->udev->dev))
+ data->udev->reset_resume = 1;
+
return 0;
}
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
index 55c135b7757a..7a722df97343 100644
--- a/drivers/bluetooth/btwilink.c
+++ b/drivers/bluetooth/btwilink.c
@@ -22,7 +22,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#define DEBUG
+
#include <linux/platform_device.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 9c4dcf4c62ea..6da5e4ca13ea 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;
};
@@ -94,7 +95,6 @@ static void ath_hci_uart_work(struct work_struct *work)
hci_uart_tx_wakeup(hu);
}
-/* Initialize protocol */
static int ath_open(struct hci_uart *hu)
{
struct ath_struct *ath;
@@ -115,8 +115,7 @@ static int ath_open(struct hci_uart *hu)
return 0;
}
-/* Flush protocol data */
-static int ath_flush(struct hci_uart *hu)
+static int ath_close(struct hci_uart *hu)
{
struct ath_struct *ath = hu->priv;
@@ -124,11 +123,17 @@ static int ath_flush(struct hci_uart *hu)
skb_queue_purge(&ath->txq);
+ kfree_skb(ath->rx_skb);
+
+ cancel_work_sync(&ath->ctxtsw);
+
+ hu->priv = NULL;
+ kfree(ath);
+
return 0;
}
-/* Close protocol */
-static int ath_close(struct hci_uart *hu)
+static int ath_flush(struct hci_uart *hu)
{
struct ath_struct *ath = hu->priv;
@@ -136,17 +141,66 @@ static int ath_close(struct hci_uart *hu)
skb_queue_purge(&ath->txq);
- cancel_work_sync(&ath->ctxtsw);
+ return 0;
+}
- hu->priv = NULL;
- kfree(ath);
+static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ struct sk_buff *skb;
+ u8 buf[10];
+ int err;
+
+ buf[0] = 0x01;
+ buf[1] = 0x01;
+ buf[2] = 0x00;
+ buf[3] = sizeof(bdaddr_t);
+ memcpy(buf + 4, bdaddr, sizeof(bdaddr_t));
+
+ skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: Change address command failed (%d)",
+ hdev->name, err);
+ return err;
+ }
+ kfree_skb(skb);
return 0;
}
+static int ath_setup(struct hci_uart *hu)
+{
+ BT_DBG("hu %p", hu);
+
+ hu->hdev->set_bdaddr = ath_set_bdaddr;
+
+ return 0;
+}
+
+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 },
+};
+
+static int ath_recv(struct hci_uart *hu, const void *data, int count)
+{
+ struct ath_struct *ath = hu->priv;
+
+ 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);
+ ath->rx_skb = NULL;
+ return err;
+ }
+
+ return count;
+}
+
#define HCI_OP_ATH_SLEEP 0xFC04
-/* Enqueue frame for transmittion */
static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct ath_struct *ath = hu->priv;
@@ -156,8 +210,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}
- /*
- * Update power management enable flag with parameters of
+ /* Update power management enable flag with parameters of
* HCI sleep enable vendor specific HCI command.
*/
if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
@@ -187,40 +240,21 @@ static struct sk_buff *ath_dequeue(struct hci_uart *hu)
return skb_dequeue(&ath->txq);
}
-/* Recv data */
-static int ath_recv(struct hci_uart *hu, void *data, int count)
-{
- int ret;
-
- ret = hci_recv_stream_fragment(hu->hdev, data, count);
- if (ret < 0) {
- BT_ERR("Frame Reassembly Failed");
- return ret;
- }
-
- 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,
+ .flush = ath_flush,
+ .setup = ath_setup,
+ .recv = ath_recv,
+ .enqueue = ath_enqueue,
+ .dequeue = ath_dequeue,
};
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..23523e140a9a
--- /dev/null
+++ b/drivers/bluetooth/hci_bcm.c
@@ -0,0 +1,258 @@
+/*
+ *
+ * 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 <linux/firmware.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_set_baudrate(struct hci_uart *hu, unsigned int speed)
+{
+ struct hci_dev *hdev = hu->hdev;
+ struct sk_buff *skb;
+ struct bcm_update_uart_baud_rate param;
+
+ if (speed > 3000000) {
+ struct bcm_write_uart_clock_setting clock;
+
+ clock.type = BCM_UART_CLOCK_48MHZ;
+
+ BT_DBG("%s: Set Controller clock (%d)", hdev->name, clock.type);
+
+ /* This Broadcom specific command changes the UART's controller
+ * clock for baud rate > 3000000.
+ */
+ skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ int err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: failed to write clock command (%d)",
+ hdev->name, err);
+ return err;
+ }
+
+ kfree_skb(skb);
+ }
+
+ BT_DBG("%s: Set Controller UART speed to %d bit/s", hdev->name, speed);
+
+ param.zero = cpu_to_le16(0);
+ param.baud_rate = cpu_to_le32(speed);
+
+ /* This Broadcom specific command changes the UART's controller baud
+ * rate.
+ */
+ skb = __hci_cmd_sync(hdev, 0xfc18, sizeof(param), &param,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ int err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: failed to write update baudrate command (%d)",
+ hdev->name, err);
+ return err;
+ }
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+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)
+{
+ char fw_name[64];
+ const struct firmware *fw;
+ unsigned int speed;
+ int err;
+
+ BT_DBG("hu %p", hu);
+
+ hu->hdev->set_bdaddr = btbcm_set_bdaddr;
+
+ err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
+ if (err)
+ return err;
+
+ err = request_firmware(&fw, fw_name, &hu->hdev->dev);
+ if (err < 0) {
+ BT_INFO("%s: BCM: Patch %s not found", hu->hdev->name, fw_name);
+ return 0;
+ }
+
+ err = btbcm_patchram(hu->hdev, fw);
+ if (err) {
+ BT_INFO("%s: BCM: Patch failed (%d)", hu->hdev->name, err);
+ goto finalize;
+ }
+
+ /* Init speed if any */
+ if (hu->init_speed)
+ speed = hu->init_speed;
+ else if (hu->proto->init_speed)
+ speed = hu->proto->init_speed;
+ else
+ speed = 0;
+
+ if (speed)
+ hci_uart_set_baudrate(hu, speed);
+
+ /* Operational speed if any */
+ if (hu->oper_speed)
+ speed = hu->oper_speed;
+ else if (hu->proto->oper_speed)
+ speed = hu->proto->oper_speed;
+ else
+ speed = 0;
+
+ if (speed) {
+ err = bcm_set_baudrate(hu, speed);
+ if (!err)
+ hci_uart_set_baudrate(hu, speed);
+ }
+
+finalize:
+ release_firmware(fw);
+
+ err = btbcm_finalize(hu->hdev);
+
+ return err;
+}
+
+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);
+ bcm->rx_skb = NULL;
+ 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",
+ .init_speed = 115200,
+ .oper_speed = 4000000,
+ .open = bcm_open,
+ .close = bcm_close,
+ .flush = bcm_flush,
+ .setup = bcm_setup,
+ .set_baudrate = bcm_set_baudrate,
+ .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..d0b615a932d1 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -47,10 +47,8 @@
#include "hci_uart.h"
-#define VERSION "0.3"
-
-static bool txcrc = 1;
-static bool hciextn = 1;
+static bool txcrc = true;
+static bool hciextn = true;
#define BCSP_TXWINSIZE 4
@@ -438,7 +436,7 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char
break;
default:
memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
- if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
bcsp_crc_update(&bcsp->message_crc, byte);
bcsp->rx_count--;
@@ -449,24 +447,24 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char
switch (byte) {
case 0xdc:
memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
- if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
- bcsp_crc_update(&bcsp-> message_crc, 0xc0);
+ bcsp_crc_update(&bcsp->message_crc, 0xc0);
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
bcsp->rx_count--;
break;
case 0xdd:
memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
- if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
- bcsp_crc_update(&bcsp-> message_crc, 0xdb);
+ bcsp_crc_update(&bcsp->message_crc, 0xdb);
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
bcsp->rx_count--;
break;
default:
- BT_ERR ("Invalid byte %02x after esc byte", byte);
+ BT_ERR("Invalid byte %02x after esc byte", byte);
kfree_skb(bcsp->rx_skb);
bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
@@ -529,7 +527,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
hci_recv_frame(hu->hdev, bcsp->rx_skb);
} else {
- BT_ERR ("Packet for unknown channel (%u %s)",
+ BT_ERR("Packet for unknown channel (%u %s)",
bcsp->rx_skb->data[1] & 0x0f,
bcsp->rx_skb->data[0] & 0x80 ?
"reliable" : "unreliable");
@@ -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);
@@ -589,7 +587,7 @@ static int bcsp_recv(struct hci_uart *hu, void *data, int count)
}
if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */
&& (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
- BT_ERR ("Out-of-order packet arrived, got %u expected %u",
+ BT_ERR("Out-of-order packet arrived, got %u expected %u",
bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
kfree_skb(bcsp->rx_skb);
@@ -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..57faddc53645 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,27 @@ 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);
+ h4->rx_skb = NULL;
+ return err;
}
return count;
@@ -140,8 +146,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 +159,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 dc487b5d1156..177dd69fdd95 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -40,17 +40,20 @@
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
+#include <linux/firmware.h>
#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 +63,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 +81,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;
@@ -261,6 +266,176 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}
+/* Flow control or un-flow control the device */
+void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
+{
+ struct tty_struct *tty = hu->tty;
+ struct ktermios ktermios;
+ int status;
+ unsigned int set = 0;
+ unsigned int clear = 0;
+
+ if (enable) {
+ /* Disable hardware flow control */
+ ktermios = tty->termios;
+ ktermios.c_cflag &= ~CRTSCTS;
+ status = tty_set_termios(tty, &ktermios);
+ BT_DBG("Disabling hardware flow control: %s",
+ status ? "failed" : "success");
+
+ /* Clear RTS to prevent the device from sending */
+ /* Most UARTs need OUT2 to enable interrupts */
+ status = tty->driver->ops->tiocmget(tty);
+ BT_DBG("Current tiocm 0x%x", status);
+
+ set &= ~(TIOCM_OUT2 | TIOCM_RTS);
+ clear = ~set;
+ set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ status = tty->driver->ops->tiocmset(tty, set, clear);
+ BT_DBG("Clearing RTS: %s", status ? "failed" : "success");
+ } else {
+ /* Set RTS to allow the device to send again */
+ status = tty->driver->ops->tiocmget(tty);
+ BT_DBG("Current tiocm 0x%x", status);
+
+ set |= (TIOCM_OUT2 | TIOCM_RTS);
+ clear = ~set;
+ set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+ TIOCM_OUT2 | TIOCM_LOOP;
+ status = tty->driver->ops->tiocmset(tty, set, clear);
+ BT_DBG("Setting RTS: %s", status ? "failed" : "success");
+
+ /* Re-enable hardware flow control */
+ ktermios = tty->termios;
+ ktermios.c_cflag |= CRTSCTS;
+ status = tty_set_termios(tty, &ktermios);
+ BT_DBG("Enabling hardware flow control: %s",
+ status ? "failed" : "success");
+ }
+}
+
+void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
+ unsigned int oper_speed)
+{
+ hu->init_speed = init_speed;
+ hu->oper_speed = oper_speed;
+}
+
+void hci_uart_init_tty(struct hci_uart *hu)
+{
+ struct tty_struct *tty = hu->tty;
+ struct ktermios ktermios;
+
+ /* Bring the UART into a known 8 bits no parity hw fc state */
+ ktermios = tty->termios;
+ ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
+ INLCR | IGNCR | ICRNL | IXON);
+ ktermios.c_oflag &= ~OPOST;
+ ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ ktermios.c_cflag &= ~(CSIZE | PARENB);
+ ktermios.c_cflag |= CS8;
+ ktermios.c_cflag |= CRTSCTS;
+
+ /* tty_set_termios() return not checked as it is always 0 */
+ tty_set_termios(tty, &ktermios);
+}
+
+void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
+{
+ struct tty_struct *tty = hu->tty;
+ struct ktermios ktermios;
+
+ ktermios = tty->termios;
+ ktermios.c_cflag &= ~CBAUD;
+ tty_termios_encode_baud_rate(&ktermios, speed, speed);
+
+ /* tty_set_termios() return not checked as it is always 0 */
+ tty_set_termios(tty, &ktermios);
+
+ BT_DBG("%s: New tty speeds: %d/%d", hu->hdev->name,
+ tty->termios.c_ispeed, tty->termios.c_ospeed);
+}
+
+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;
+ unsigned int speed;
+ int err;
+
+ /* Init speed if any */
+ if (hu->init_speed)
+ speed = hu->init_speed;
+ else if (hu->proto->init_speed)
+ speed = hu->proto->init_speed;
+ else
+ speed = 0;
+
+ if (speed)
+ hci_uart_set_baudrate(hu, speed);
+
+ /* Operational speed if any */
+ if (hu->oper_speed)
+ speed = hu->oper_speed;
+ else if (hu->proto->oper_speed)
+ speed = hu->proto->oper_speed;
+ else
+ speed = 0;
+
+ if (hu->proto->set_baudrate && speed) {
+ err = hu->proto->set_baudrate(hu, speed);
+ if (!err)
+ hci_uart_set_baudrate(hu, speed);
+ }
+
+ 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;
+}
+
/* ------ LDISC part ------ */
/* hci_uart_tty_open
*
@@ -316,7 +491,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);
@@ -355,7 +530,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("");
@@ -383,9 +558,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;
@@ -394,7 +570,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;
@@ -426,6 +602,7 @@ static int hci_uart_register_dev(struct hci_uart *hu)
hdev->close = hci_uart_close;
hdev->flush = hci_uart_flush;
hdev->send = hci_uart_send_frame;
+ hdev->setup = hci_uart_setup;
SET_HCIDEV_DEV(hdev, hu->tty->dev);
if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
@@ -458,7 +635,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);
@@ -486,9 +663,10 @@ 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))
+ if (flags & ~valid_flags)
return -EINVAL;
hu->hdev_flags = flags;
@@ -509,10 +687,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("");
@@ -566,19 +744,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;
}
@@ -626,6 +804,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;
}
@@ -649,6 +830,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 247488edcbf9..ce9c670956f5 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,21 +43,29 @@
#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;
+ unsigned int init_speed;
+ unsigned int oper_speed;
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 (*setup)(struct hci_uart *hu);
+ int (*set_baudrate)(struct hci_uart *hu, unsigned int speed);
+ 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);
};
@@ -71,12 +79,15 @@ 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;
unsigned long tx_state;
spinlock_t rx_lock;
+
+ unsigned int init_speed;
+ unsigned int oper_speed;
};
/* HCI_UART proto flag bits */
@@ -87,14 +98,53 @@ 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);
+void hci_uart_init_tty(struct hci_uart *hu);
+void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
+void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
+void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
+ unsigned int oper_speed);
#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
@@ -116,3 +166,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/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 6653473f2757..78653db2ef2b 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -366,7 +366,7 @@ static const struct file_operations vhci_fops = {
.llseek = no_llseek,
};
-static struct miscdevice vhci_miscdev= {
+static struct miscdevice vhci_miscdev = {
.name = "vhci",
.fops = &vhci_fops,
.minor = VHCI_MINOR,
OpenPOWER on IntegriCloud