diff options
author | Marcel Holtmann | 2015-01-28 20:09:56 +0100 |
---|---|---|
committer | Marcel Holtmann | 2015-01-28 21:26:25 +0100 |
commit | 385a768c3be1b4235ed57d707581c74b0d0efd63 (patch) | |
tree | 701af9afa74c53b7bca38f8d1a740232a1812b42 /drivers/bluetooth/btusb.c | |
parent | Bluetooth: Perform a power cycle when receiving hardware error event (diff) | |
download | kernel-qcow2-linux-385a768c3be1b4235ed57d707581c74b0d0efd63.tar.gz kernel-qcow2-linux-385a768c3be1b4235ed57d707581c74b0d0efd63.tar.xz kernel-qcow2-linux-385a768c3be1b4235ed57d707581c74b0d0efd63.zip |
Bluetooth: btusb: Provide hardware error handler for Intel devices
The Intel Bluetooth controllers can provide an additional exception
info string when a hardware error event occurs. The core will now
call hdev->hw_error to let the driver read out this information.
This change will cause a reset of the hardware to bring it back
into functional state and then read the Intel exception info
string and print it along with the error information.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Diffstat (limited to 'drivers/bluetooth/btusb.c')
-rw-r--r-- | drivers/bluetooth/btusb.c | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 70513b8d4d4a..b2753d79edcf 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2266,6 +2266,46 @@ done: return 0; } +static void btusb_hw_error_intel(struct hci_dev *hdev, u8 code) +{ + struct sk_buff *skb; + u8 type = 0x00; + + BT_ERR("%s: Hardware error 0x%2.2x", hdev->name, code); + + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: Reset after hardware error failed (%ld)", + hdev->name, PTR_ERR(skb)); + return; + } + kfree_skb(skb); + + skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: Retrieving Intel exception info failed (%ld)", + hdev->name, PTR_ERR(skb)); + return; + } + + if (skb->len != 13) { + BT_ERR("%s: Exception info size mismatch", hdev->name); + kfree_skb(skb); + 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) { struct sk_buff *skb; @@ -2649,12 +2689,14 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_INTEL) { hdev->setup = btusb_setup_intel; + hdev->hw_error = btusb_hw_error_intel; hdev->set_bdaddr = btusb_set_bdaddr_intel; } 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; } |