diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2006-07-03 04:02:24 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-07-03 22:53:52 -0400 |
commit | dcdcf63ef12dc3fbaa17a6d04f16ada8e63bb4d0 (patch) | |
tree | ebcd16f8c4221305782fd1643eef018b546df36e /drivers/bluetooth | |
parent | 2b86ad21deec4c47a1f0089298f12e4038c2aa68 (diff) |
[Bluetooth] Add suspend/resume support to the HCI USB driver
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>
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r-- | drivers/bluetooth/hci_usb.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c index 95afce1cb465..987a3dff8af9 100644 --- a/drivers/bluetooth/hci_usb.c +++ b/drivers/bluetooth/hci_usb.c | |||
@@ -1045,10 +1045,81 @@ static void hci_usb_disconnect(struct usb_interface *intf) | |||
1045 | hci_free_dev(hdev); | 1045 | hci_free_dev(hdev); |
1046 | } | 1046 | } |
1047 | 1047 | ||
1048 | static int hci_usb_suspend(struct usb_interface *intf, pm_message_t message) | ||
1049 | { | ||
1050 | struct hci_usb *husb = usb_get_intfdata(intf); | ||
1051 | struct list_head killed; | ||
1052 | unsigned long flags; | ||
1053 | int i; | ||
1054 | |||
1055 | if (!husb || intf == husb->isoc_iface) | ||
1056 | return 0; | ||
1057 | |||
1058 | hci_suspend_dev(husb->hdev); | ||
1059 | |||
1060 | INIT_LIST_HEAD(&killed); | ||
1061 | |||
1062 | for (i = 0; i < 4; i++) { | ||
1063 | struct _urb_queue *q = &husb->pending_q[i]; | ||
1064 | struct _urb *_urb, *_tmp; | ||
1065 | |||
1066 | while ((_urb = _urb_dequeue(q))) { | ||
1067 | /* reset queue since _urb_dequeue sets it to NULL */ | ||
1068 | _urb->queue = q; | ||
1069 | usb_kill_urb(&_urb->urb); | ||
1070 | list_add(&_urb->list, &killed); | ||
1071 | } | ||
1072 | |||
1073 | spin_lock_irqsave(&q->lock, flags); | ||
1074 | |||
1075 | list_for_each_entry_safe(_urb, _tmp, &killed, list) { | ||
1076 | list_move_tail(&_urb->list, &q->head); | ||
1077 | } | ||
1078 | |||
1079 | spin_unlock_irqrestore(&q->lock, flags); | ||
1080 | } | ||
1081 | |||
1082 | return 0; | ||
1083 | } | ||
1084 | |||
1085 | static int hci_usb_resume(struct usb_interface *intf) | ||
1086 | { | ||
1087 | struct hci_usb *husb = usb_get_intfdata(intf); | ||
1088 | unsigned long flags; | ||
1089 | int i, err = 0; | ||
1090 | |||
1091 | if (!husb || intf == husb->isoc_iface) | ||
1092 | return 0; | ||
1093 | |||
1094 | for (i = 0; i < 4; i++) { | ||
1095 | struct _urb_queue *q = &husb->pending_q[i]; | ||
1096 | struct _urb *_urb; | ||
1097 | |||
1098 | spin_lock_irqsave(&q->lock, flags); | ||
1099 | |||
1100 | list_for_each_entry(_urb, &q->head, list) { | ||
1101 | err = usb_submit_urb(&_urb->urb, GFP_ATOMIC); | ||
1102 | if (err) | ||
1103 | break; | ||
1104 | } | ||
1105 | |||
1106 | spin_unlock_irqrestore(&q->lock, flags); | ||
1107 | |||
1108 | if (err) | ||
1109 | return -EIO; | ||
1110 | } | ||
1111 | |||
1112 | hci_resume_dev(husb->hdev); | ||
1113 | |||
1114 | return 0; | ||
1115 | } | ||
1116 | |||
1048 | static struct usb_driver hci_usb_driver = { | 1117 | static struct usb_driver hci_usb_driver = { |
1049 | .name = "hci_usb", | 1118 | .name = "hci_usb", |
1050 | .probe = hci_usb_probe, | 1119 | .probe = hci_usb_probe, |
1051 | .disconnect = hci_usb_disconnect, | 1120 | .disconnect = hci_usb_disconnect, |
1121 | .suspend = hci_usb_suspend, | ||
1122 | .resume = hci_usb_resume, | ||
1052 | .id_table = bluetooth_ids, | 1123 | .id_table = bluetooth_ids, |
1053 | }; | 1124 | }; |
1054 | 1125 | ||