diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2008-11-30 12:17:14 +0100 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2008-11-30 12:17:14 +0100 |
commit | 6a88adf2adf5d6a3b759c2e114da4c5266ca3972 (patch) | |
tree | 594d2b8db9614d4348490e513edbb99603d70790 | |
parent | a780efa8124fe7ef23d8ef844d56afe960356615 (diff) | |
download | linux-6a88adf2adf5d6a3b759c2e114da4c5266ca3972.tar.gz linux-6a88adf2adf5d6a3b759c2e114da4c5266ca3972.tar.bz2 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>
-rw-r--r-- | drivers/bluetooth/btusb.c | 62 |
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, }; |