Bluetooth: Add support for Broadcom device of Asus Z97-DELUXE motherboard
[pandora-kernel.git] / drivers / bluetooth / btusb.c
index a7dfbf9..292c38e 100644 (file)
@@ -30,9 +30,6 @@
 
 #define VERSION "0.6"
 
-static bool ignore_dga;
-static bool ignore_csr;
-static bool ignore_sniffer;
 static bool disable_scofix;
 static bool force_scofix;
 
@@ -49,6 +46,9 @@ static struct usb_driver btusb_driver;
 #define BTUSB_WRONG_SCO_MTU    0x40
 #define BTUSB_ATH3012          0x80
 #define BTUSB_INTEL            0x100
+#define BTUSB_INTEL_BOOT       0x200
+#define BTUSB_BCM_PATCHRAM     0x400
+#define BTUSB_MARVELL          0x800
 
 static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
@@ -111,7 +111,11 @@ static const struct usb_device_id btusb_table[] = {
        { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
 
        /* Broadcom devices with vendor specific id */
-       { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
+         .driver_info = BTUSB_BCM_PATCHRAM },
+
+       /* ASUSTek Computer - Broadcom based */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) },
 
        /* Belkin F8065bf - Broadcom based */
        { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
@@ -119,6 +123,10 @@ static const struct usb_device_id btusb_table[] = {
        /* IMC Networks - Broadcom based */
        { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
 
+       /* Intel Bluetooth USB Bootloader (RAM module) */
+       { USB_DEVICE(0x8087, 0x0a5a),
+         .driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC },
+
        { }     /* Terminating entry */
 };
 
@@ -160,7 +168,6 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0x3005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x311e), .driver_info = BTUSB_ATH3012 },
@@ -174,6 +181,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
@@ -227,15 +235,21 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
 
        /* CSR BlueCore Bluetooth Sniffer */
-       { USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER },
+       { USB_DEVICE(0x0a12, 0x0002),
+         .driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC },
 
        /* Frontline ComProbe Bluetooth Sniffer */
-       { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
+       { USB_DEVICE(0x16d3, 0x0002),
+         .driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC },
 
        /* Intel Bluetooth device */
        { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
        { USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
 
+       /* Marvell device */
+       { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL },
+       { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
+
        { }     /* Terminating entry */
 };
 
@@ -1181,6 +1195,51 @@ 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;
@@ -1253,6 +1312,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);
                return 0;
        }
 
@@ -1265,6 +1325,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        fw = btusb_setup_intel_get_fw(hdev, ver);
        if (!fw) {
                kfree_skb(skb);
+               btusb_check_bdaddr_intel(hdev);
                return 0;
        }
        fw_ptr = fw->data;
@@ -1344,6 +1405,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);
        return 0;
 
 exit_mfg_disable:
@@ -1358,6 +1420,8 @@ exit_mfg_disable:
        kfree_skb(skb);
 
        BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
+
+       btusb_check_bdaddr_intel(hdev);
        return 0;
 
 exit_mfg_deactivate:
@@ -1378,6 +1442,252 @@ exit_mfg_deactivate:
        BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
                hdev->name);
 
+       btusb_check_bdaddr_intel(hdev);
+       return 0;
+}
+
+static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       long ret;
+
+       skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: changing Intel device address failed (%ld)",
+                       hdev->name, ret);
+               return ret;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+
+static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
+                                   const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       u8 buf[8];
+       long ret;
+
+       buf[0] = 0xfe;
+       buf[1] = sizeof(bdaddr_t);
+       memcpy(buf + 2, bdaddr, sizeof(bdaddr_t));
+
+       skb = __hci_cmd_sync(hdev, 0xfc22, sizeof(buf), buf, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: changing Marvell device address failed (%ld)",
+                      hdev->name, ret);
+               return ret;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+
+#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
+
+static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct usb_device *udev = data->udev;
+       char fw_name[64];
+       const struct firmware *fw;
+       const u8 *fw_ptr;
+       size_t fw_size;
+       const struct hci_command_hdr *cmd;
+       const u8 *cmd_param;
+       u16 opcode;
+       struct sk_buff *skb;
+       struct hci_rp_read_local_version *ver;
+       struct hci_rp_read_bd_addr *bda;
+       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;
+       }
+
+       /* Reset */
+       skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
+               goto done;
+       }
+       kfree_skb(skb);
+
+       /* Read Local Version Info */
+       skb = __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;
+       }
+
+       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;
+       }
+
+       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);
+
+       /* Start Download */
+       skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Download Minidrv command failed (%ld)",
+                       hdev->name, ret);
+               goto reset_fw;
+       }
+       kfree_skb(skb);
+
+       /* 50 msec delay after Download Minidrv completes */
+       msleep(50);
+
+       fw_ptr = fw->data;
+       fw_size = fw->size;
+
+       while (fw_size >= sizeof(*cmd)) {
+               cmd = (struct hci_command_hdr *) fw_ptr;
+               fw_ptr += sizeof(*cmd);
+               fw_size -= sizeof(*cmd);
+
+               if (fw_size < cmd->plen) {
+                       BT_ERR("%s: BCM: patch %s is corrupted",
+                               hdev->name, fw_name);
+                       ret = -EINVAL;
+                       goto reset_fw;
+               }
+
+               cmd_param = fw_ptr;
+               fw_ptr += cmd->plen;
+               fw_size -= cmd->plen;
+
+               opcode = le16_to_cpu(cmd->opcode);
+
+               skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
+                                    HCI_INIT_TIMEOUT);
+               if (IS_ERR(skb)) {
+                       ret = PTR_ERR(skb);
+                       BT_ERR("%s: BCM: patch command %04x failed (%ld)",
+                               hdev->name, opcode, ret);
+                       goto reset_fw;
+               }
+               kfree_skb(skb);
+       }
+
+       /* 250 msec delay after Launch Ram completes */
+       msleep(250);
+
+reset_fw:
+       /* Reset */
+       skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
+               goto done;
+       }
+       kfree_skb(skb);
+
+       /* Read Local Version Info */
+       skb = __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;
+       }
+
+       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;
+       }
+
+       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);
+
+       /* Read BD Address */
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)",
+                       hdev->name, ret);
+               goto done;
+       }
+
+       if (skb->len != sizeof(*bda)) {
+               BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch",
+                       hdev->name);
+               kfree_skb(skb);
+               ret = -EIO;
+               goto done;
+       }
+
+       bda = (struct hci_rp_read_bd_addr *) skb->data;
+       if (bda->status) {
+               BT_ERR("%s: HCI_OP_READ_BD_ADDR error status (%02x)",
+                      hdev->name, bda->status);
+               kfree_skb(skb);
+               ret = -bt_to_errno(bda->status);
+               goto done;
+       }
+
+       /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
+        * with no configured address.
+        */
+       if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
+               BT_INFO("%s: BCM: using default device address (%pMR)",
+                       hdev->name, &bda->bdaddr);
+               set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+       }
+
+       kfree_skb(skb);
+
+done:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       long ret;
+
+       skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Change address command failed (%ld)",
+                       hdev->name, ret);
+               return ret;
+       }
+       kfree_skb(skb);
+
        return 0;
 }
 
@@ -1405,15 +1715,6 @@ static int btusb_probe(struct usb_interface *intf,
        if (id->driver_info == BTUSB_IGNORE)
                return -ENODEV;
 
-       if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER)
-               return -ENODEV;
-
-       if (ignore_csr && id->driver_info & BTUSB_CSR)
-               return -ENODEV;
-
-       if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
-               return -ENODEV;
-
        if (id->driver_info & BTUSB_ATH3012) {
                struct usb_device *udev = interface_to_usbdev(intf);
 
@@ -1486,8 +1787,21 @@ static int btusb_probe(struct usb_interface *intf,
        if (id->driver_info & BTUSB_BCM92035)
                hdev->setup = btusb_setup_bcm92035;
 
-       if (id->driver_info & BTUSB_INTEL)
+       if (id->driver_info & BTUSB_BCM_PATCHRAM) {
+               hdev->setup = btusb_setup_bcm_patchram;
+               hdev->set_bdaddr = btusb_set_bdaddr_bcm;
+       }
+
+       if (id->driver_info & BTUSB_INTEL) {
                hdev->setup = btusb_setup_intel;
+               hdev->set_bdaddr = btusb_set_bdaddr_intel;
+       }
+
+       if (id->driver_info & BTUSB_MARVELL)
+               hdev->set_bdaddr = btusb_set_bdaddr_marvell;
+
+       if (id->driver_info & BTUSB_INTEL_BOOT)
+               set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
 
        /* Interface numbers are hardcoded in the specification */
        data->isoc = usb_ifnum_to_if(data->udev, 1);
@@ -1527,8 +1841,18 @@ static int btusb_probe(struct usb_interface *intf,
                /* New sniffer firmware has crippled HCI interface */
                if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
                        set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+       }
 
-               data->isoc = NULL;
+       if (id->driver_info & BTUSB_INTEL_BOOT) {
+               /* A bug in the bootloader causes that interrupt interface is
+                * only enabled after receiving SetInterface(0, AltSetting=0).
+                */
+               err = usb_set_interface(data->udev, 0, 0);
+               if (err < 0) {
+                       BT_ERR("failed to set interface 0, alt 0 %d", err);
+                       hci_free_dev(hdev);
+                       return err;
+               }
        }
 
        if (data->isoc) {
@@ -1693,15 +2017,6 @@ static struct usb_driver btusb_driver = {
 
 module_usb_driver(btusb_driver);
 
-module_param(ignore_dga, bool, 0644);
-MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
-
-module_param(ignore_csr, bool, 0644);
-MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
-
-module_param(ignore_sniffer, bool, 0644);
-MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
-
 module_param(disable_scofix, bool, 0644);
 MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");