[Bluetooth] Add suspend/resume support to the HCI USB driver
authorMarcel Holtmann <marcel@holtmann.org>
Mon, 3 Jul 2006 08:02:24 +0000 (10:02 +0200)
committerDavid S. Miller <davem@sunset.davemloft.net>
Tue, 4 Jul 2006 02:53:52 +0000 (19:53 -0700)
This patch implements the suspend/resume methods for the HCI USB
driver by killing all outstanding URBs on suspend, and re-issuing
them on resume.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
drivers/bluetooth/hci_usb.c

index 95afce1cb465e173486265a567e499fbf5fe70c7..987a3dff8af97344f2a729ddd9fd221528125881 100644 (file)
@@ -1045,10 +1045,81 @@ static void hci_usb_disconnect(struct usb_interface *intf)
        hci_free_dev(hdev);
 }
 
+static int hci_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct hci_usb *husb = usb_get_intfdata(intf);
+       struct list_head killed;
+       unsigned long flags;
+       int i;
+
+       if (!husb || intf == husb->isoc_iface)
+               return 0;
+
+       hci_suspend_dev(husb->hdev);
+
+       INIT_LIST_HEAD(&killed);
+
+       for (i = 0; i < 4; i++) {
+               struct _urb_queue *q = &husb->pending_q[i];
+               struct _urb *_urb, *_tmp;
+
+               while ((_urb = _urb_dequeue(q))) {
+                       /* reset queue since _urb_dequeue sets it to NULL */
+                       _urb->queue = q;
+                       usb_kill_urb(&_urb->urb);
+                       list_add(&_urb->list, &killed);
+               }
+
+               spin_lock_irqsave(&q->lock, flags);
+
+               list_for_each_entry_safe(_urb, _tmp, &killed, list) {
+                       list_move_tail(&_urb->list, &q->head);
+               }
+
+               spin_unlock_irqrestore(&q->lock, flags);
+       }
+
+       return 0;
+}
+
+static int hci_usb_resume(struct usb_interface *intf)
+{
+       struct hci_usb *husb = usb_get_intfdata(intf);
+       unsigned long flags;
+       int i, err = 0;
+
+       if (!husb || intf == husb->isoc_iface)
+               return 0;
+       
+       for (i = 0; i < 4; i++) {
+               struct _urb_queue *q = &husb->pending_q[i];
+               struct _urb *_urb;
+
+               spin_lock_irqsave(&q->lock, flags);
+
+               list_for_each_entry(_urb, &q->head, list) {
+                       err = usb_submit_urb(&_urb->urb, GFP_ATOMIC);
+                       if (err)
+                               break;
+               }
+
+               spin_unlock_irqrestore(&q->lock, flags);
+
+               if (err)
+                       return -EIO;
+       }
+
+       hci_resume_dev(husb->hdev);
+
+       return 0;
+}
+
 static struct usb_driver hci_usb_driver = {
        .name           = "hci_usb",
        .probe          = hci_usb_probe,
        .disconnect     = hci_usb_disconnect,
+       .suspend        = hci_usb_suspend,
+       .resume         = hci_usb_resume,
        .id_table       = bluetooth_ids,
 };