summaryrefslogtreecommitdiff
path: root/drivers/bluetooth/btusb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth/btusb.c')
-rw-r--r--drivers/bluetooth/btusb.c306
1 files changed, 288 insertions, 18 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 247b1062cb9a..e33dacf5bd98 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -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);
}