summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2008-11-30 12:17:14 +0100
committerMarcel Holtmann <marcel@holtmann.org>2008-11-30 12:17:14 +0100
commit6a88adf2adf5d6a3b759c2e114da4c5266ca3972 (patch)
tree594d2b8db9614d4348490e513edbb99603d70790 /drivers
parentBluetooth: Handle bulk URBs in btusb driver from notify callback (diff)
downloadlinux-6a88adf2adf5d6a3b759c2e114da4c5266ca3972.tar.xz
linux-6a88adf2adf5d6a3b759c2e114da4c5266ca3972.zip
Bluetooth: Add suspend/resume support to btusb driver
During suspend it is important that all URBs are cancelled and then on resume re-submitted. This gives initial suspend/resume support. Based on initial work from Oliver Neukum <oneukum@suse.de> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bluetooth/btusb.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 9ca95208fc24..0cd4a55dd5c4 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -198,6 +198,7 @@ struct btusb_data {
struct usb_endpoint_descriptor *isoc_rx_ep;
int isoc_altsetting;
+ int suspend_count;
};
static void btusb_intr_complete(struct urb *urb)
@@ -948,10 +949,71 @@ static void btusb_disconnect(struct usb_interface *intf)
hci_free_dev(hdev);
}
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ if (data->suspend_count++)
+ return 0;
+
+ cancel_work_sync(&data->work);
+
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+ usb_kill_anchored_urbs(&data->bulk_anchor);
+ usb_kill_anchored_urbs(&data->intr_anchor);
+
+ return 0;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev = data->hdev;
+ int err;
+
+ BT_DBG("intf %p", intf);
+
+ if (--data->suspend_count)
+ return 0;
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
+ err = btusb_submit_intr_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+ return err;
+ }
+ }
+
+ if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+ if (btusb_submit_bulk_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ else
+ btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ }
+
+ if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_NOIO);
+ }
+
+ return 0;
+}
+
static struct usb_driver btusb_driver = {
.name = "btusb",
.probe = btusb_probe,
.disconnect = btusb_disconnect,
+ .suspend = btusb_suspend,
+ .resume = btusb_resume,
.id_table = btusb_table,
};