]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/bluetooth/btusb.c
Merge remote-tracking branch 'sound-current/for-linus'
[karo-tx-linux.git] / drivers / bluetooth / btusb.c
index 247b1062cb9affc67d9e7f7a0234c167f90f4b79..e33dacf5bd98765178ddac60f7d50d742de555ac 100644 (file)
@@ -60,6 +60,8 @@ static struct usb_driver btusb_driver;
 #define BTUSB_QCA_ROME         0x8000
 #define BTUSB_BCM_APPLE                0x10000
 #define BTUSB_REALTEK          0x20000
+#define BTUSB_BCM2045          0x40000
+#define BTUSB_IFNUM_2          0x80000
 
 static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
@@ -73,7 +75,7 @@ static const struct usb_device_id btusb_table[] = {
 
        /* Apple-specific (Broadcom) devices */
        { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01),
-         .driver_info = BTUSB_BCM_APPLE },
+         .driver_info = BTUSB_BCM_APPLE | BTUSB_IFNUM_2 },
 
        /* MediaTek MT76x0E */
        { USB_DEVICE(0x0e8d, 0x763f) },
@@ -124,6 +126,9 @@ static const struct usb_device_id btusb_table[] = {
        /* Broadcom BCM20702B0 (Dynex/Insignia) */
        { USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
 
+       /* Broadcom BCM43142A0 (Foxconn/Lenovo) */
+       { USB_DEVICE(0x105b, 0xe065), .driver_info = BTUSB_BCM_PATCHRAM },
+
        /* Foxconn - Hon Hai */
        { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01),
          .driver_info = BTUSB_BCM_PATCHRAM },
@@ -164,6 +169,9 @@ static const struct usb_device_id blacklist_table[] = {
        /* Broadcom BCM2033 without firmware */
        { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
 
+       /* Broadcom BCM2045 devices */
+       { USB_DEVICE(0x0a5c, 0x2045), .driver_info = BTUSB_BCM2045 },
+
        /* Atheros 3011 with sflash firmware */
        { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
        { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
@@ -195,6 +203,7 @@ static const struct usb_device_id blacklist_table[] = {
        { 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, 0x021c), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
@@ -206,6 +215,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x0cf3, 0x311f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x817b), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
@@ -341,12 +351,14 @@ static const struct usb_device_id blacklist_table[] = {
 #define BTUSB_FIRMWARE_FAILED  8
 #define BTUSB_BOOTING          9
 #define BTUSB_RESET_RESUME     10
+#define BTUSB_DIAG_RUNNING     11
 
 struct btusb_data {
        struct hci_dev       *hdev;
        struct usb_device    *udev;
        struct usb_interface *intf;
        struct usb_interface *isoc;
+       struct usb_interface *diag;
 
        unsigned long flags;
 
@@ -361,6 +373,7 @@ struct btusb_data {
        struct usb_anchor intr_anchor;
        struct usb_anchor bulk_anchor;
        struct usb_anchor isoc_anchor;
+       struct usb_anchor diag_anchor;
        spinlock_t rxlock;
 
        struct sk_buff *evt_skb;
@@ -372,6 +385,8 @@ struct btusb_data {
        struct usb_endpoint_descriptor *bulk_rx_ep;
        struct usb_endpoint_descriptor *isoc_tx_ep;
        struct usb_endpoint_descriptor *isoc_rx_ep;
+       struct usb_endpoint_descriptor *diag_tx_ep;
+       struct usb_endpoint_descriptor *diag_rx_ep;
 
        __u8 cmdreq_type;
        __u8 cmdreq;
@@ -869,6 +884,92 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
        return err;
 }
 
+static void btusb_diag_complete(struct urb *urb)
+{
+       struct hci_dev *hdev = urb->context;
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       int err;
+
+       BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+              urb->actual_length);
+
+       if (urb->status == 0) {
+               struct sk_buff *skb;
+
+               skb = bt_skb_alloc(urb->actual_length, GFP_ATOMIC);
+               if (skb) {
+                       memcpy(skb_put(skb, urb->actual_length),
+                              urb->transfer_buffer, urb->actual_length);
+                       hci_recv_diag(hdev, skb);
+               }
+       } else if (urb->status == -ENOENT) {
+               /* Avoid suspend failed when usb_kill_urb */
+               return;
+       }
+
+       if (!test_bit(BTUSB_DIAG_RUNNING, &data->flags))
+               return;
+
+       usb_anchor_urb(urb, &data->diag_anchor);
+       usb_mark_last_busy(data->udev);
+
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err < 0) {
+               /* -EPERM: urb is being killed;
+                * -ENODEV: device got disconnected */
+               if (err != -EPERM && err != -ENODEV)
+                       BT_ERR("%s urb %p failed to resubmit (%d)",
+                              hdev->name, urb, -err);
+               usb_unanchor_urb(urb);
+       }
+}
+
+static int btusb_submit_diag_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct urb *urb;
+       unsigned char *buf;
+       unsigned int pipe;
+       int err, size = HCI_MAX_FRAME_SIZE;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!data->diag_rx_ep)
+               return -ENODEV;
+
+       urb = usb_alloc_urb(0, mem_flags);
+       if (!urb)
+               return -ENOMEM;
+
+       buf = kmalloc(size, mem_flags);
+       if (!buf) {
+               usb_free_urb(urb);
+               return -ENOMEM;
+       }
+
+       pipe = usb_rcvbulkpipe(data->udev, data->diag_rx_ep->bEndpointAddress);
+
+       usb_fill_bulk_urb(urb, data->udev, pipe, buf, size,
+                         btusb_diag_complete, hdev);
+
+       urb->transfer_flags |= URB_FREE_BUFFER;
+
+       usb_mark_last_busy(data->udev);
+       usb_anchor_urb(urb, &data->diag_anchor);
+
+       err = usb_submit_urb(urb, mem_flags);
+       if (err < 0) {
+               if (err != -EPERM && err != -ENODEV)
+                       BT_ERR("%s urb %p submission failed (%d)",
+                              hdev->name, urb, -err);
+               usb_unanchor_urb(urb);
+       }
+
+       usb_free_urb(urb);
+
+       return err;
+}
+
 static void btusb_tx_complete(struct urb *urb)
 {
        struct sk_buff *skb = urb->context;
@@ -956,6 +1057,11 @@ static int btusb_open(struct hci_dev *hdev)
        set_bit(BTUSB_BULK_RUNNING, &data->flags);
        btusb_submit_bulk_urb(hdev, GFP_KERNEL);
 
+       if (data->diag) {
+               if (!btusb_submit_diag_urb(hdev, GFP_KERNEL))
+                       set_bit(BTUSB_DIAG_RUNNING, &data->flags);
+       }
+
 done:
        usb_autopm_put_interface(data->intf);
        return 0;
@@ -971,6 +1077,7 @@ static void btusb_stop_traffic(struct btusb_data *data)
        usb_kill_anchored_urbs(&data->intr_anchor);
        usb_kill_anchored_urbs(&data->bulk_anchor);
        usb_kill_anchored_urbs(&data->isoc_anchor);
+       usb_kill_anchored_urbs(&data->diag_anchor);
 }
 
 static int btusb_close(struct hci_dev *hdev)
@@ -986,6 +1093,7 @@ static int btusb_close(struct hci_dev *hdev)
        clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
        clear_bit(BTUSB_BULK_RUNNING, &data->flags);
        clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+       clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
 
        btusb_stop_traffic(data);
        btusb_free_frags(data);
@@ -1593,8 +1701,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);
-               btintel_check_bdaddr(hdev);
-               return 0;
+               goto complete;
        }
 
        /* Opens the firmware patch file based on the firmware version read
@@ -1606,8 +1713,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        fw = btusb_setup_intel_get_fw(hdev, ver);
        if (!fw) {
                kfree_skb(skb);
-               btintel_check_bdaddr(hdev);
-               return 0;
+               goto complete;
        }
        fw_ptr = fw->data;
 
@@ -1680,8 +1786,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
                hdev->name);
 
-       btintel_check_bdaddr(hdev);
-       return 0;
+       goto complete;
 
 exit_mfg_disable:
        /* Disable the manufacturer mode without reset */
@@ -1696,8 +1801,7 @@ exit_mfg_disable:
 
        BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
 
-       btintel_check_bdaddr(hdev);
-       return 0;
+       goto complete;
 
 exit_mfg_deactivate:
        release_firmware(fw);
@@ -1717,6 +1821,12 @@ exit_mfg_deactivate:
        BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
                hdev->name);
 
+complete:
+       /* Set the event mask for Intel specific vendor events. This enables
+        * a few extra events that are useful during general operation.
+        */
+       btintel_set_event_mask_mfg(hdev, false);
+
        btintel_check_bdaddr(hdev);
        return 0;
 }
@@ -2006,6 +2116,15 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
        BT_INFO("%s: Secure boot is %s", hdev->name,
                params->secure_boot ? "enabled" : "disabled");
 
+       BT_INFO("%s: OTP lock is %s", hdev->name,
+               params->otp_lock ? "enabled" : "disabled");
+
+       BT_INFO("%s: API lock is %s", hdev->name,
+               params->api_lock ? "enabled" : "disabled");
+
+       BT_INFO("%s: Debug lock is %s", hdev->name,
+               params->debug_lock ? "enabled" : "disabled");
+
        BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
                params->min_fw_build_nn, params->min_fw_build_cw,
                2000 + params->min_fw_build_yy);
@@ -2222,6 +2341,15 @@ done:
         */
        btintel_load_ddc_config(hdev, fwname);
 
+       /* Set the event mask for Intel specific vendor events. This enables
+        * a few extra events that are useful during general operation. It
+        * does not enable any debugging related events.
+        *
+        * The device will function correctly without these events enabled
+        * and thus no need to fail the setup.
+        */
+       btintel_set_event_mask(hdev, false);
+
        return 0;
 }
 
@@ -2547,19 +2675,115 @@ static int btusb_setup_qca(struct hci_dev *hdev)
        return 0;
 }
 
+#ifdef CONFIG_BT_HCIBTUSB_BCM
+static inline int __set_diag_interface(struct hci_dev *hdev)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct usb_interface *intf = data->diag;
+       int i;
+
+       if (!data->diag)
+               return -ENODEV;
+
+       data->diag_tx_ep = NULL;
+       data->diag_rx_ep = NULL;
+
+       for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+               struct usb_endpoint_descriptor *ep_desc;
+
+               ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+               if (!data->diag_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
+                       data->diag_tx_ep = ep_desc;
+                       continue;
+               }
+
+               if (!data->diag_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
+                       data->diag_rx_ep = ep_desc;
+                       continue;
+               }
+       }
+
+       if (!data->diag_tx_ep || !data->diag_rx_ep) {
+               BT_ERR("%s invalid diagnostic descriptors", hdev->name);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static struct urb *alloc_diag_urb(struct hci_dev *hdev, bool enable)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct sk_buff *skb;
+       struct urb *urb;
+       unsigned int pipe;
+
+       if (!data->diag_tx_ep)
+               return ERR_PTR(-ENODEV);
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!urb)
+               return ERR_PTR(-ENOMEM);
+
+       skb = bt_skb_alloc(2, GFP_KERNEL);
+       if (!skb) {
+               usb_free_urb(urb);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       *skb_put(skb, 1) = 0xf0;
+       *skb_put(skb, 1) = enable;
+
+       pipe = usb_sndbulkpipe(data->udev, data->diag_tx_ep->bEndpointAddress);
+
+       usb_fill_bulk_urb(urb, data->udev, pipe,
+                         skb->data, skb->len, btusb_tx_complete, skb);
+
+       skb->dev = (void *)hdev;
+
+       return urb;
+}
+
+static int btusb_bcm_set_diag(struct hci_dev *hdev, bool enable)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct urb *urb;
+
+       if (!data->diag)
+               return -ENODEV;
+
+       if (!test_bit(HCI_RUNNING, &hdev->flags))
+               return -ENETDOWN;
+
+       urb = alloc_diag_urb(hdev, enable);
+       if (IS_ERR(urb))
+               return PTR_ERR(urb);
+
+       return submit_or_queue_tx_urb(hdev, urb);
+}
+#endif
+
 static int btusb_probe(struct usb_interface *intf,
                       const struct usb_device_id *id)
 {
        struct usb_endpoint_descriptor *ep_desc;
        struct btusb_data *data;
        struct hci_dev *hdev;
+       unsigned ifnum_base;
        int i, err;
 
        BT_DBG("intf %p id %p", intf, id);
 
        /* interface numbers are hardcoded in the spec */
-       if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
-               return -ENODEV;
+       if (intf->cur_altsetting->desc.bInterfaceNumber != 0) {
+               if (!(id->driver_info & BTUSB_IFNUM_2))
+                       return -ENODEV;
+               if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
+                       return -ENODEV;
+       }
+
+       ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
 
        if (!id->driver_info) {
                const struct usb_device_id *match;
@@ -2627,6 +2851,7 @@ static int btusb_probe(struct usb_interface *intf,
        init_usb_anchor(&data->intr_anchor);
        init_usb_anchor(&data->bulk_anchor);
        init_usb_anchor(&data->isoc_anchor);
+       init_usb_anchor(&data->diag_anchor);
        spin_lock_init(&data->rxlock);
 
        if (id->driver_info & BTUSB_INTEL_NEW) {
@@ -2660,33 +2885,53 @@ static int btusb_probe(struct usb_interface *intf,
        hdev->send   = btusb_send_frame;
        hdev->notify = btusb_notify;
 
+       if (id->driver_info & BTUSB_BCM2045)
+               set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
+
        if (id->driver_info & BTUSB_BCM92035)
                hdev->setup = btusb_setup_bcm92035;
 
 #ifdef CONFIG_BT_HCIBTUSB_BCM
        if (id->driver_info & BTUSB_BCM_PATCHRAM) {
+               hdev->manufacturer = 15;
                hdev->setup = btbcm_setup_patchram;
+               hdev->set_diag = btusb_bcm_set_diag;
                hdev->set_bdaddr = btbcm_set_bdaddr;
+
+               /* Broadcom LM_DIAG Interface numbers are hardcoded */
+               data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
        }
 
-       if (id->driver_info & BTUSB_BCM_APPLE)
+       if (id->driver_info & BTUSB_BCM_APPLE) {
+               hdev->manufacturer = 15;
                hdev->setup = btbcm_setup_apple;
+               hdev->set_diag = btusb_bcm_set_diag;
+
+               /* Broadcom LM_DIAG Interface numbers are hardcoded */
+               data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
+       }
 #endif
 
        if (id->driver_info & BTUSB_INTEL) {
+               hdev->manufacturer = 2;
                hdev->setup = btusb_setup_intel;
                hdev->shutdown = btusb_shutdown_intel;
+               hdev->set_diag = btintel_set_diag_mfg;
                hdev->set_bdaddr = btintel_set_bdaddr;
                set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
                set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+               set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
        }
 
        if (id->driver_info & BTUSB_INTEL_NEW) {
+               hdev->manufacturer = 2;
                hdev->send = btusb_send_frame_intel;
                hdev->setup = btusb_setup_intel_new;
                hdev->hw_error = btintel_hw_error;
+               hdev->set_diag = btintel_set_diag;
                hdev->set_bdaddr = btintel_set_bdaddr;
                set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+               set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
        }
 
        if (id->driver_info & BTUSB_MARVELL)
@@ -2697,8 +2942,10 @@ static int btusb_probe(struct usb_interface *intf,
                set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
        }
 
-       if (id->driver_info & BTUSB_INTEL_BOOT)
+       if (id->driver_info & BTUSB_INTEL_BOOT) {
+               hdev->manufacturer = 2;
                set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+       }
 
        if (id->driver_info & BTUSB_ATH3012) {
                hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
@@ -2727,8 +2974,8 @@ static int btusb_probe(struct usb_interface *intf,
                /* AMP controllers do not support SCO packets */
                data->isoc = NULL;
        } else {
-               /* Interface numbers are hardcoded in the specification */
-               data->isoc = usb_ifnum_to_if(data->udev, 1);
+               /* Interface orders are hardcoded in the specification */
+               data->isoc = usb_ifnum_to_if(data->udev, ifnum_base + 1);
        }
 
        if (!reset)
@@ -2791,6 +3038,16 @@ static int btusb_probe(struct usb_interface *intf,
                }
        }
 
+#ifdef CONFIG_BT_HCIBTUSB_BCM
+       if (data->diag) {
+               if (!usb_driver_claim_interface(&btusb_driver,
+                                               data->diag, data))
+                       __set_diag_interface(hdev);
+               else
+                       data->diag = NULL;
+       }
+#endif
+
        err = hci_register_dev(hdev);
        if (err < 0) {
                hci_free_dev(hdev);
@@ -2818,12 +3075,25 @@ static void btusb_disconnect(struct usb_interface *intf)
        if (data->isoc)
                usb_set_intfdata(data->isoc, NULL);
 
+       if (data->diag)
+               usb_set_intfdata(data->diag, NULL);
+
        hci_unregister_dev(hdev);
 
-       if (intf == data->isoc)
+       if (intf == data->intf) {
+               if (data->isoc)
+                       usb_driver_release_interface(&btusb_driver, data->isoc);
+               if (data->diag)
+                       usb_driver_release_interface(&btusb_driver, data->diag);
+       } else if (intf == data->isoc) {
+               if (data->diag)
+                       usb_driver_release_interface(&btusb_driver, data->diag);
                usb_driver_release_interface(&btusb_driver, data->intf);
-       else if (data->isoc)
-               usb_driver_release_interface(&btusb_driver, data->isoc);
+       } else if (intf == data->diag) {
+               usb_driver_release_interface(&btusb_driver, data->intf);
+               if (data->isoc)
+                       usb_driver_release_interface(&btusb_driver, data->isoc);
+       }
 
        hci_free_dev(hdev);
 }