All of lore.kernel.org
 help / color / mirror / Atom feed
* [rft]autosuspend for btusb
@ 2008-08-22 13:20 Oliver Neukum
  2008-08-22 13:33 ` Marcel Holtmann
  2008-08-22 13:33 ` Marcel Holtmann
  0 siblings, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-22 13:20 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Pavel Machek, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

Hi,

this patch against vanilla 2.6.27-rc4 implements full autosuspend for
btusb. It should allow the HCI to be suspended during periods of inactivity
while retaining full service if the device supports USB remote wakeup.

Please test and/or comment on the code.
It works for me with a few glitches but still needs to be a bit polished.

	Regards
		Oliver

---

--- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000 +0200
+++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-20 17:21:24.000000000 +0200
@@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 {
 	struct urb *victim;
+	unsigned long flags;
 
-	spin_lock_irq(&anchor->lock);
+	spin_lock_irqsave(&anchor->lock, flags);
 	while (!list_empty(&anchor->urb_list)) {
 		victim = list_entry(anchor->urb_list.prev, struct urb,
 				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
 		/* this will unanchor the URB */
 		usb_unlink_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
 	}
-	spin_unlock_irq(&anchor->lock);
+	spin_unlock_irqrestore(&anchor->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 
@@ -628,3 +633,47 @@ int usb_wait_anchor_empty_timeout(struct
 				  msecs_to_jiffies(timeout));
 }
 EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
+struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	if (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.next, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		usb_unanchor_urb(victim);
+	} else {
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		victim = NULL;
+	}
+
+	return victim;
+}
+
+EXPORT_SYMBOL_GPL(usb_get_from_anchor);
+
+void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		/* this will unanchor the URB */
+		usb_unanchor_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
+	}
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);
+
--- linux-2.6.27-rc4/include/linux/usb.h	2008-08-21 10:04:29.000000000 +0200
+++ linux-2.6.27-rc3/include/linux/usb.h	2008-08-20 17:09:57.000000000 +0200
@@ -1462,6 +1460,8 @@ extern void usb_anchor_urb(struct urb *u
 extern void usb_unanchor_urb(struct urb *urb);
 extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
 					 unsigned int timeout);
+extern struct urb *usb_get_from_anchor(struct usb_anchor *anchor);
+extern void usb_scuttle_anchored_urbs(struct usb_anchor *anchor);
 
 /**
  * usb_urb_dir_in - check if an URB describes an IN transfer
--- linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-21 10:04:11.000000000 +0200
+++ linux-2.6.27-rc3/drivers/bluetooth/btusb.c	2008-08-22 14:39:05.000000000 +0200
@@ -35,13 +35,13 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-//#define CONFIG_BT_HCIBTUSB_DEBUG
+#define CONFIG_BT_HCIBTUSB_DEBUG
 #ifndef CONFIG_BT_HCIBTUSB_DEBUG
 #undef  BT_DBG
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "0.3"
+#define VERSION "0.4"
 
 static int ignore_dga;
 static int ignore_csr;
@@ -165,10 +165,12 @@ static struct usb_device_id blacklist_ta
 #define BTUSB_INTR_RUNNING	0
 #define BTUSB_BULK_RUNNING	1
 #define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
 
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,11 +178,15 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
 	struct usb_anchor bulk_anchor;
 	struct usb_anchor isoc_anchor;
+	struct usb_anchor deferred;
+	int tx_in_flight;
+	spinlock_t txlock;
 
 	struct usb_endpoint_descriptor *intr_ep;
 	struct usb_endpoint_descriptor *bulk_tx_ep;
@@ -189,8 +195,26 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int did_iso_resume:1;
+	int susp_count;
 };
 
+static int inc_tx(struct btusb_data *data)
+{
+	unsigned long flags;
+	int rv;
+
+	BT_DBG("entered");
+	spin_lock_irqsave(&data->txlock, flags);
+	rv = test_bit(BTUSB_SUSPENDING, &data->flags);
+	if (!rv)
+		data->tx_in_flight++;
+	spin_unlock_irqrestore(&data->txlock, flags);
+	BT_DBG("returning %d", rv);
+
+	return rv;
+}
+
 static void btusb_intr_complete(struct urb *urb)
 {
 	struct hci_dev *hdev = urb->context;
@@ -217,6 +241,7 @@ static void btusb_intr_complete(struct u
 	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->intr_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -227,7 +252,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +265,13 @@ static int btusb_submit_intr_urb(struct
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +287,7 @@ static int btusb_submit_intr_urb(struct
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -301,6 +326,7 @@ static void btusb_bulk_complete(struct u
 	if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -311,7 +337,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +350,19 @@ static int btusb_submit_bulk_urb(struct
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +372,7 @@ static int btusb_submit_bulk_urb(struct
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -483,6 +510,33 @@ static void btusb_tx_complete(struct urb
 {
 	struct sk_buff *skb = urb->context;
 	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct btusb_data *data = hdev->driver_data;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		goto done;
+
+	if (!urb->status)
+		hdev->stat.byte_tx += urb->transfer_buffer_length;
+	else
+		hdev->stat.err_tx++;
+
+done:
+	spin_lock(&data->txlock);
+	data->tx_in_flight--;
+	spin_unlock(&data->txlock);
+
+	kfree(urb->setup_packet);
+
+	kfree_skb(skb);
+}
+
+static void btusb_isoc_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = urb->context;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
 
 	BT_DBG("%s urb %p status %d count %d", hdev->name,
 					urb, urb->status, urb->actual_length);
@@ -508,13 +562,19 @@ static int btusb_open(struct hci_dev *hd
 
 	BT_DBG("%s", hdev->name);
 
+	err = usb_autopm_get_interface(data->acl);
+	if (err < 0)
+		return err;
+	data->acl->needs_remote_wakeup = 1;
+	usb_autopm_put_interface(data->acl);
+
 	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_ATOMIC);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,24 +583,34 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
+	int err;
 
 	BT_DBG("%s", hdev->name);
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
-
+	btusb_stop_traffic(data);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err) {
+		data->acl->needs_remote_wakeup = 0;
+		usb_autopm_put_interface(data->acl);
+	}
 	return 0;
 }
 
@@ -571,6 +641,7 @@ static int btusb_send_frame(struct sk_bu
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
+		BT_DBG("HCI_COMMAND_PKT");
 		urb = usb_alloc_urb(0, GFP_ATOMIC);
 		if (!urb)
 			return -ENOMEM;
@@ -596,6 +667,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_ACLDATA_PKT:
+		BT_DBG("HCI_ACLDATA_PKT");
 		if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
 			return -ENODEV;
 
@@ -613,6 +685,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_SCODATA_PKT:
+		BT_DBG("HCI_SCODATA_PKT");
 		if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
 			return -ENODEV;
 
@@ -626,7 +699,7 @@ static int btusb_send_frame(struct sk_bu
 		urb->dev      = data->udev;
 		urb->pipe     = pipe;
 		urb->context  = skb;
-		urb->complete = btusb_tx_complete;
+		urb->complete = btusb_isoc_tx_complete;
 		urb->interval = data->isoc_tx_ep->bInterval;
 
 		urb->transfer_flags  = URB_ISO_ASAP;
@@ -637,12 +710,21 @@ static int btusb_send_frame(struct sk_bu
 				le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
 
 		hdev->stat.sco_tx++;
-		break;
+		goto skip_waking;
 
 	default:
 		return -EILSEQ;
 	}
 
+	err = inc_tx(data);
+	if (err) {
+		printk(KERN_ERR"Autosuspension detected\n");
+		usb_anchor_urb(urb, &data->deferred);
+		schedule_work(&data->waker);
+		err = 0;
+		goto out;
+	}
+skip_waking:
 	usb_anchor_urb(urb, &data->tx_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -654,6 +736,7 @@ static int btusb_send_frame(struct sk_bu
 
 	usb_free_urb(urb);
 
+out:
 	return err;
 }
 
@@ -672,8 +755,19 @@ static void btusb_notify(struct hci_dev
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -723,20 +817,19 @@ static void btusb_work(struct work_struc
 {
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
-
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
+	int err;
 
 	if (hdev->conn_hash.sco_num > 0) {
+		if (!data->did_iso_resume) {
+			err = usb_autopm_get_interface(data->isoc);
+			if (!err) {
+				data->did_iso_resume = 1;
+			} else {
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->isoc_anchor);
+				return;
+			}
+		}
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			usb_kill_anchored_urbs(&data->isoc_anchor);
@@ -756,9 +849,26 @@ static void btusb_work(struct work_struc
 		usb_kill_anchored_urbs(&data->isoc_anchor);
 
 		__set_isoc_interface(hdev, 0);
+		if (data->did_iso_resume) {
+			data->did_iso_resume = 0;
+			usb_autopm_put_interface(data->isoc);
+		}
 	}
 }
 
+static void btusb_waker(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, waker);
+	int err;
+
+	BUG_ON(data == NULL);
+	BT_DBG("about to resume");
+	BUG_ON(data->acl == NULL);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err)
+		usb_autopm_put_interface(data->acl);
+}
+
 static int btusb_probe(struct usb_interface *intf,
 				const struct usb_device_id *id)
 {
@@ -821,15 +931,19 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
 	INIT_WORK(&data->work, btusb_work);
+	INIT_WORK(&data->waker, btusb_waker);
+	spin_lock_init(&data->txlock);
 
 	init_usb_anchor(&data->tx_anchor);
 	init_usb_anchor(&data->intr_anchor);
 	init_usb_anchor(&data->bulk_anchor);
 	init_usb_anchor(&data->isoc_anchor);
+	init_usb_anchor(&data->deferred);
 
 	hdev = hci_alloc_dev();
 	if (!hdev) {
@@ -889,7 +1003,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,21 +1035,131 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	hci_free_dev(hdev);
 }
 
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+
+	printk(KERN_ERR"%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+	spin_lock_irq(&data->txlock);
+	if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) {
+		set_bit(BTUSB_SUSPENDING, &data->flags);
+		printk(KERN_ERR"%s accepting\n", __func__);
+	} else {
+		spin_unlock_irq(&data->txlock);
+		printk(KERN_ERR"%s rejecting, count %d\n", __func__, data->tx_in_flight);
+		return -EBUSY;
+	}
+	spin_unlock_irq(&data->txlock);
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_anchor);
+	BT_DBG("flags: %ld", data->flags);
+	return 0;
+}
+
+static int play_deferred(struct btusb_data *data)
+{
+	struct urb *urb;
+	int err = 0;
+
+	while ((urb = usb_get_from_anchor(&data->deferred))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0)
+			break;
+		else
+			data->tx_in_flight++;
+	}
+	usb_scuttle_anchored_urbs(&data->deferred);
+	return err;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+	struct hci_dev *hdev = data->hdev;
+	int ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+
+		spin_lock_irq(&data->txlock);
+		ret = play_deferred(data);
+		clear_bit(BTUSB_SUSPENDING, &data->flags);
+		spin_unlock_irq(&data->txlock);
+
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
+	.supports_autosuspend = 1,
 };
 
 static int __init btusb_init(void)

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 13:20 [rft]autosuspend for btusb Oliver Neukum
@ 2008-08-22 13:33 ` Marcel Holtmann
  2008-08-22 13:51   ` Oliver Neukum
  2008-08-22 13:51   ` [rft]autosuspend for btusb Oliver Neukum
  2008-08-22 13:33 ` Marcel Holtmann
  1 sibling, 2 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-22 13:33 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Pavel Machek, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

Hi Oliver,

> this patch against vanilla 2.6.27-rc4 implements full autosuspend for
> btusb. It should allow the HCI to be suspended during periods of inactivity
> while retaining full service if the device supports USB remote wakeup.

actually we do have two cases. An inactive device can be woken up by an
HCI Connection Request coming via the interrupt endpoint or if we have a
send_frame callback via the Bluetooth stack itself.

What do we do if a device does not support remote wakeup?

> Please test and/or comment on the code.
> It works for me with a few glitches but still needs to be a bit polished.

Please explain the tx_in_flight stuff to me. It looks unneeded since we
anchor all TX URBs anyway.

So can we just leave the ISOC interface stuff out of it and try to get
the case right where we have control and interrupt URBs only and maybe
bulk URBs in case we an ACL link up.

This reminds me that we should extend the notify() callback to inform
about sniff and active state changes. Since in theory when a connection
goes into sniff mode, we could suspend it.

Regards

Marcel



^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 13:20 [rft]autosuspend for btusb Oliver Neukum
  2008-08-22 13:33 ` Marcel Holtmann
@ 2008-08-22 13:33 ` Marcel Holtmann
  1 sibling, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-22 13:33 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-bluetooth, Pavel Machek, linux-usb, Stefan Seyfried, linux-pm

Hi Oliver,

> this patch against vanilla 2.6.27-rc4 implements full autosuspend for
> btusb. It should allow the HCI to be suspended during periods of inactivity
> while retaining full service if the device supports USB remote wakeup.

actually we do have two cases. An inactive device can be woken up by an
HCI Connection Request coming via the interrupt endpoint or if we have a
send_frame callback via the Bluetooth stack itself.

What do we do if a device does not support remote wakeup?

> Please test and/or comment on the code.
> It works for me with a few glitches but still needs to be a bit polished.

Please explain the tx_in_flight stuff to me. It looks unneeded since we
anchor all TX URBs anyway.

So can we just leave the ISOC interface stuff out of it and try to get
the case right where we have control and interrupt URBs only and maybe
bulk URBs in case we an ACL link up.

This reminds me that we should extend the notify() callback to inform
about sniff and active state changes. Since in theory when a connection
goes into sniff mode, we could suspend it.

Regards

Marcel

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 13:33 ` Marcel Holtmann
@ 2008-08-22 13:51   ` Oliver Neukum
  2008-08-22 14:31     ` Marcel Holtmann
  2008-08-22 14:31     ` Marcel Holtmann
  2008-08-22 13:51   ` [rft]autosuspend for btusb Oliver Neukum
  1 sibling, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-22 13:51 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Pavel Machek, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

Am Freitag 22 August 2008 15:33:53 schrieb Marcel Holtmann:
> Hi Oliver,
> 
> > this patch against vanilla 2.6.27-rc4 implements full autosuspend for
> > btusb. It should allow the HCI to be suspended during periods of inactivity
> > while retaining full service if the device supports USB remote wakeup.
> 
> actually we do have two cases. An inactive device can be woken up by an
> HCI Connection Request coming via the interrupt endpoint or if we have a
> send_frame callback via the Bluetooth stack itself.

Yes, if send_frame triggers it, we wake using a work queue. A connection
request will wake us via remote wakeup.

> What do we do if a device does not support remote wakeup?

Autosuspend on close, resume on open. I don't think we can do more.

> > Please test and/or comment on the code.
> > It works for me with a few glitches but still needs to be a bit polished.
> 
> Please explain the tx_in_flight stuff to me. It looks unneeded since we
> anchor all TX URBs anyway.

The completion of an URB may happen after the autosuspend timeout passed.
But we cannot use the pm counters as they are not accessible in interrupt.
Hence we must maintain a counter ourselves.

> So can we just leave the ISOC interface stuff out of it and try to get

The isoc stuff should be handled already, but I haven't tested that.

> the case right where we have control and interrupt URBs only and maybe
> bulk URBs in case we an ACL link up.
> 
> This reminds me that we should extend the notify() callback to inform
> about sniff and active state changes. Since in theory when a connection
> goes into sniff mode, we could suspend it.

We should definitely do that.

	Regards
		Oliver


	

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 13:33 ` Marcel Holtmann
  2008-08-22 13:51   ` Oliver Neukum
@ 2008-08-22 13:51   ` Oliver Neukum
  1 sibling, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-22 13:51 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-bluetooth, Pavel Machek, linux-usb, Stefan Seyfried, linux-pm

Am Freitag 22 August 2008 15:33:53 schrieb Marcel Holtmann:
> Hi Oliver,
> 
> > this patch against vanilla 2.6.27-rc4 implements full autosuspend for
> > btusb. It should allow the HCI to be suspended during periods of inactivity
> > while retaining full service if the device supports USB remote wakeup.
> 
> actually we do have two cases. An inactive device can be woken up by an
> HCI Connection Request coming via the interrupt endpoint or if we have a
> send_frame callback via the Bluetooth stack itself.

Yes, if send_frame triggers it, we wake using a work queue. A connection
request will wake us via remote wakeup.

> What do we do if a device does not support remote wakeup?

Autosuspend on close, resume on open. I don't think we can do more.

> > Please test and/or comment on the code.
> > It works for me with a few glitches but still needs to be a bit polished.
> 
> Please explain the tx_in_flight stuff to me. It looks unneeded since we
> anchor all TX URBs anyway.

The completion of an URB may happen after the autosuspend timeout passed.
But we cannot use the pm counters as they are not accessible in interrupt.
Hence we must maintain a counter ourselves.

> So can we just leave the ISOC interface stuff out of it and try to get

The isoc stuff should be handled already, but I haven't tested that.

> the case right where we have control and interrupt URBs only and maybe
> bulk URBs in case we an ACL link up.
> 
> This reminds me that we should extend the notify() callback to inform
> about sniff and active state changes. Since in theory when a connection
> goes into sniff mode, we could suspend it.

We should definitely do that.

	Regards
		Oliver


	

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 13:51   ` Oliver Neukum
  2008-08-22 14:31     ` Marcel Holtmann
@ 2008-08-22 14:31     ` Marcel Holtmann
  2008-08-22 14:38       ` Oliver Neukum
                         ` (5 more replies)
  1 sibling, 6 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-22 14:31 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Pavel Machek, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

[-- Attachment #1: Type: text/plain, Size: 2309 bytes --]

Hi Oliver,

> > > this patch against vanilla 2.6.27-rc4 implements full autosuspend for
> > > btusb. It should allow the HCI to be suspended during periods of inactivity
> > > while retaining full service if the device supports USB remote wakeup.
> > 
> > actually we do have two cases. An inactive device can be woken up by an
> > HCI Connection Request coming via the interrupt endpoint or if we have a
> > send_frame callback via the Bluetooth stack itself.
> 
> Yes, if send_frame triggers it, we wake using a work queue. A connection
> request will wake us via remote wakeup.
> 
> > What do we do if a device does not support remote wakeup?
> 
> Autosuspend on close, resume on open. I don't think we can do more.

sounds good to me. I wanna split the whole work into small patches so we
can have an easier review. I attached the first two of this series and
they should apply cleanly against 2.6.27-rc4, but keep in mind that you
need the usb_unlink_anchored_urbs() patch too.

> > > Please test and/or comment on the code.
> > > It works for me with a few glitches but still needs to be a bit polished.
> > 
> > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > anchor all TX URBs anyway.
> 
> The completion of an URB may happen after the autosuspend timeout passed.
> But we cannot use the pm counters as they are not accessible in interrupt.
> Hence we must maintain a counter ourselves.

Can we not just check the number of URBs in the anchor? I am against
just duplicating a counter, but then lets call it it what it is to make
it gets not misused. It is a purely a PM counter.

> > So can we just leave the ISOC interface stuff out of it and try to get
> 
> The isoc stuff should be handled already, but I haven't tested that.

I know, but I wanna get the basic logic clean and tested. The SCO part
comes after that. Even if I think it should be good already.

> > the case right where we have control and interrupt URBs only and maybe
> > bulk URBs in case we an ACL link up.
> > 
> > This reminds me that we should extend the notify() callback to inform
> > about sniff and active state changes. Since in theory when a connection
> > goes into sniff mode, we could suspend it.
> 
> We should definitely do that.

I will work on a patch for that one.

Regards

Marcel


[-- Attachment #2: patch-btusb-001 --]
[-- Type: text/plain, Size: 5255 bytes --]

[Bluetooth] Add fine grained mem_flags used to btusb driver

The URB submission routines need more fine grained control for the
mem_flags used by kmalloc(), usb_alloc_urb() and usb_submit_urb() to
better support different caller situations. Add a mem_flags parameter
and have the caller full control.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

---
commit 283407ed763e144f2343c980542a460dee98e98c
tree ea86d454c7e8b6243c8a717ac009bbde2a11beb0
parent 6a55617ed5d1aa62b850de2cf66f5ede2eef4825
author Marcel Holtmann <marcel@holtmann.org> Fri, 22 Aug 2008 16:23:47 +0200
committer Marcel Holtmann <marcel@holtmann.org> Fri, 22 Aug 2008 16:23:47 +0200

 drivers/bluetooth/btusb.c |   34 +++++++++++++++++-----------------
 1 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 6a01068..9296df0 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -227,7 +227,7 @@ static void btusb_intr_complete(struct urb *urb)
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +240,13 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev)
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, mem_flags);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, mem_flags);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +262,7 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev)
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -311,7 +311,7 @@ static void btusb_bulk_complete(struct urb *urb)
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,13 +324,13 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev)
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, mem_flags);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, mem_flags);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -345,7 +345,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev)
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -423,7 +423,7 @@ static void inline __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
 	urb->number_of_packets = i;
 }
 
-static int btusb_submit_isoc_urb(struct hci_dev *hdev)
+static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -436,14 +436,14 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev)
 	if (!data->isoc_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
+	urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
 						BTUSB_MAX_ISOC_FRAMES;
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, mem_flags);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -466,7 +466,7 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev)
 
 	usb_anchor_urb(urb, &data->isoc_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -514,7 +514,7 @@ static int btusb_open(struct hci_dev *hdev)
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -726,10 +726,10 @@ static void btusb_work(struct work_struct *work)
 
 	if (hdev->conn_hash.acl_num > 0) {
 		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
+			if (btusb_submit_bulk_urb(hdev, GFP_KERNEL) < 0)
 				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
 			else
-				btusb_submit_bulk_urb(hdev);
+				btusb_submit_bulk_urb(hdev, GFP_KERNEL);
 		}
 	} else {
 		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
@@ -746,10 +746,10 @@ static void btusb_work(struct work_struct *work)
 		}
 
 		if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
-			if (btusb_submit_isoc_urb(hdev) < 0)
+			if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
 				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			else
-				btusb_submit_isoc_urb(hdev);
+				btusb_submit_isoc_urb(hdev, GFP_KERNEL);
 		}
 	} else {
 		clear_bit(BTUSB_ISOC_RUNNING, &data->flags);

[-- Attachment #3: patch-btusb-002 --]
[-- Type: text/plain, Size: 2619 bytes --]

[Bluetooth] Handle bulk URBs in btusb driver from notify callback

With the addition of usb_unlink_anchored_urbs() it is possible to fully
control the bulk URBs from the notify callback. There is no need to
schedule work and so only do this for the isoc URBs.

Also cancel any scheduled work when closing down the interface.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

---
commit 3c7a240281e8269ba297d3805f90c65c726828f5
tree 4a345086275bc7625aef6848a9acf2f4db58af65
parent 283407ed763e144f2343c980542a460dee98e98c
author Marcel Holtmann <marcel@holtmann.org> Fri, 22 Aug 2008 16:24:45 +0200
committer Marcel Holtmann <marcel@holtmann.org> Fri, 22 Aug 2008 16:24:45 +0200

 drivers/bluetooth/btusb.c |   29 +++++++++++++++--------------
 1 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 9296df0..0532569 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -532,6 +532,8 @@ static int btusb_close(struct hci_dev *hdev)
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
+	cancel_work_sync(&data->work);
+
 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	usb_kill_anchored_urbs(&data->intr_anchor);
 
@@ -672,8 +674,19 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -724,18 +737,6 @@ static void btusb_work(struct work_struct *work)
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
 
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev, GFP_KERNEL) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev, GFP_KERNEL);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
-
 	if (hdev->conn_hash.sco_num > 0) {
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);

^ permalink raw reply related	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 13:51   ` Oliver Neukum
@ 2008-08-22 14:31     ` Marcel Holtmann
  2008-08-22 14:31     ` Marcel Holtmann
  1 sibling, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-22 14:31 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-bluetooth, Pavel Machek, linux-usb, Stefan Seyfried, linux-pm

[-- Attachment #1: Type: text/plain, Size: 2309 bytes --]

Hi Oliver,

> > > this patch against vanilla 2.6.27-rc4 implements full autosuspend for
> > > btusb. It should allow the HCI to be suspended during periods of inactivity
> > > while retaining full service if the device supports USB remote wakeup.
> > 
> > actually we do have two cases. An inactive device can be woken up by an
> > HCI Connection Request coming via the interrupt endpoint or if we have a
> > send_frame callback via the Bluetooth stack itself.
> 
> Yes, if send_frame triggers it, we wake using a work queue. A connection
> request will wake us via remote wakeup.
> 
> > What do we do if a device does not support remote wakeup?
> 
> Autosuspend on close, resume on open. I don't think we can do more.

sounds good to me. I wanna split the whole work into small patches so we
can have an easier review. I attached the first two of this series and
they should apply cleanly against 2.6.27-rc4, but keep in mind that you
need the usb_unlink_anchored_urbs() patch too.

> > > Please test and/or comment on the code.
> > > It works for me with a few glitches but still needs to be a bit polished.
> > 
> > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > anchor all TX URBs anyway.
> 
> The completion of an URB may happen after the autosuspend timeout passed.
> But we cannot use the pm counters as they are not accessible in interrupt.
> Hence we must maintain a counter ourselves.

Can we not just check the number of URBs in the anchor? I am against
just duplicating a counter, but then lets call it it what it is to make
it gets not misused. It is a purely a PM counter.

> > So can we just leave the ISOC interface stuff out of it and try to get
> 
> The isoc stuff should be handled already, but I haven't tested that.

I know, but I wanna get the basic logic clean and tested. The SCO part
comes after that. Even if I think it should be good already.

> > the case right where we have control and interrupt URBs only and maybe
> > bulk URBs in case we an ACL link up.
> > 
> > This reminds me that we should extend the notify() callback to inform
> > about sniff and active state changes. Since in theory when a connection
> > goes into sniff mode, we could suspend it.
> 
> We should definitely do that.

I will work on a patch for that one.

Regards

Marcel


[-- Attachment #2: patch-btusb-001 --]
[-- Type: text/plain, Size: 5255 bytes --]

[Bluetooth] Add fine grained mem_flags used to btusb driver

The URB submission routines need more fine grained control for the
mem_flags used by kmalloc(), usb_alloc_urb() and usb_submit_urb() to
better support different caller situations. Add a mem_flags parameter
and have the caller full control.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

---
commit 283407ed763e144f2343c980542a460dee98e98c
tree ea86d454c7e8b6243c8a717ac009bbde2a11beb0
parent 6a55617ed5d1aa62b850de2cf66f5ede2eef4825
author Marcel Holtmann <marcel@holtmann.org> Fri, 22 Aug 2008 16:23:47 +0200
committer Marcel Holtmann <marcel@holtmann.org> Fri, 22 Aug 2008 16:23:47 +0200

 drivers/bluetooth/btusb.c |   34 +++++++++++++++++-----------------
 1 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 6a01068..9296df0 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -227,7 +227,7 @@ static void btusb_intr_complete(struct urb *urb)
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +240,13 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev)
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, mem_flags);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, mem_flags);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +262,7 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev)
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -311,7 +311,7 @@ static void btusb_bulk_complete(struct urb *urb)
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,13 +324,13 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev)
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, mem_flags);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, mem_flags);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -345,7 +345,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev)
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -423,7 +423,7 @@ static void inline __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
 	urb->number_of_packets = i;
 }
 
-static int btusb_submit_isoc_urb(struct hci_dev *hdev)
+static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -436,14 +436,14 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev)
 	if (!data->isoc_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
+	urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
 						BTUSB_MAX_ISOC_FRAMES;
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, mem_flags);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -466,7 +466,7 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev)
 
 	usb_anchor_urb(urb, &data->isoc_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, mem_flags);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -514,7 +514,7 @@ static int btusb_open(struct hci_dev *hdev)
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -726,10 +726,10 @@ static void btusb_work(struct work_struct *work)
 
 	if (hdev->conn_hash.acl_num > 0) {
 		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
+			if (btusb_submit_bulk_urb(hdev, GFP_KERNEL) < 0)
 				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
 			else
-				btusb_submit_bulk_urb(hdev);
+				btusb_submit_bulk_urb(hdev, GFP_KERNEL);
 		}
 	} else {
 		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
@@ -746,10 +746,10 @@ static void btusb_work(struct work_struct *work)
 		}
 
 		if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
-			if (btusb_submit_isoc_urb(hdev) < 0)
+			if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
 				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			else
-				btusb_submit_isoc_urb(hdev);
+				btusb_submit_isoc_urb(hdev, GFP_KERNEL);
 		}
 	} else {
 		clear_bit(BTUSB_ISOC_RUNNING, &data->flags);

[-- Attachment #3: patch-btusb-002 --]
[-- Type: text/plain, Size: 2619 bytes --]

[Bluetooth] Handle bulk URBs in btusb driver from notify callback

With the addition of usb_unlink_anchored_urbs() it is possible to fully
control the bulk URBs from the notify callback. There is no need to
schedule work and so only do this for the isoc URBs.

Also cancel any scheduled work when closing down the interface.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

---
commit 3c7a240281e8269ba297d3805f90c65c726828f5
tree 4a345086275bc7625aef6848a9acf2f4db58af65
parent 283407ed763e144f2343c980542a460dee98e98c
author Marcel Holtmann <marcel@holtmann.org> Fri, 22 Aug 2008 16:24:45 +0200
committer Marcel Holtmann <marcel@holtmann.org> Fri, 22 Aug 2008 16:24:45 +0200

 drivers/bluetooth/btusb.c |   29 +++++++++++++++--------------
 1 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 9296df0..0532569 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -532,6 +532,8 @@ static int btusb_close(struct hci_dev *hdev)
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
+	cancel_work_sync(&data->work);
+
 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	usb_kill_anchored_urbs(&data->intr_anchor);
 
@@ -672,8 +674,19 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -724,18 +737,6 @@ static void btusb_work(struct work_struct *work)
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
 
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev, GFP_KERNEL) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev, GFP_KERNEL);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
-
 	if (hdev->conn_hash.sco_num > 0) {
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);

[-- Attachment #4: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply related	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 14:31     ` Marcel Holtmann
@ 2008-08-22 14:38       ` Oliver Neukum
  2008-08-22 14:38       ` Oliver Neukum
                         ` (4 subsequent siblings)
  5 siblings, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-22 14:38 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Pavel Machek, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

Am Freitag 22 August 2008 16:31:10 schrieb Marcel Holtmann:

Hi,

> > > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > > anchor all TX URBs anyway.
> > 
> > The completion of an URB may happen after the autosuspend timeout passed.
> > But we cannot use the pm counters as they are not accessible in interrupt.
> > Hence we must maintain a counter ourselves.
> 
> Can we not just check the number of URBs in the anchor? I am against
> just duplicating a counter, but then lets call it it what it is to make
> it gets not misused. It is a purely a PM counter.

Yes, we can do that. In fact a bool empty/!empty will do. I'll do that.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 14:31     ` Marcel Holtmann
  2008-08-22 14:38       ` Oliver Neukum
@ 2008-08-22 14:38       ` Oliver Neukum
  2008-08-25 10:43       ` Oliver Neukum
                         ` (3 subsequent siblings)
  5 siblings, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-22 14:38 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-bluetooth, Pavel Machek, linux-usb, Stefan Seyfried, linux-pm

Am Freitag 22 August 2008 16:31:10 schrieb Marcel Holtmann:

Hi,

> > > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > > anchor all TX URBs anyway.
> > 
> > The completion of an URB may happen after the autosuspend timeout passed.
> > But we cannot use the pm counters as they are not accessible in interrupt.
> > Hence we must maintain a counter ourselves.
> 
> Can we not just check the number of URBs in the anchor? I am against
> just duplicating a counter, but then lets call it it what it is to make
> it gets not misused. It is a purely a PM counter.

Yes, we can do that. In fact a bool empty/!empty will do. I'll do that.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 14:31     ` Marcel Holtmann
  2008-08-22 14:38       ` Oliver Neukum
  2008-08-22 14:38       ` Oliver Neukum
@ 2008-08-25 10:43       ` Oliver Neukum
  2008-08-25 11:51         ` Marcel Holtmann
                           ` (3 more replies)
  2008-08-25 10:43       ` Oliver Neukum
                         ` (2 subsequent siblings)
  5 siblings, 4 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-25 10:43 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Pavel Machek, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

Am Freitag 22 August 2008 16:31:10 schrieb Marcel Holtmann:
> > > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > > anchor all TX URBs anyway.
> > 
> > The completion of an URB may happen after the autosuspend timeout passed.
> > But we cannot use the pm counters as they are not accessible in interrupt.
> > Hence we must maintain a counter ourselves.
> 
> Can we not just check the number of URBs in the anchor? I am against
> just duplicating a counter, but then lets call it it what it is to make
> it gets not misused. It is a purely a PM counter.
> 

An excellent suggestion allowing major simplifications. This version works
for me. It required an extension of the anchor API, so it obsoletes
the split-up you've done. Therefore the whole thing comes as a big patch
against rc4, easy to test.

	Regards
		Oliver

----

--- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000 +0200
+++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-22 17:25:49.000000000 +0200
@@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 {
 	struct urb *victim;
+	unsigned long flags;
 
-	spin_lock_irq(&anchor->lock);
+	spin_lock_irqsave(&anchor->lock, flags);
 	while (!list_empty(&anchor->urb_list)) {
 		victim = list_entry(anchor->urb_list.prev, struct urb,
 				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
 		/* this will unanchor the URB */
 		usb_unlink_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
 	}
-	spin_unlock_irq(&anchor->lock);
+	spin_unlock_irqrestore(&anchor->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 
@@ -628,3 +633,61 @@ int usb_wait_anchor_empty_timeout(struct
 				  msecs_to_jiffies(timeout));
 }
 EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
+struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	if (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.next, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		usb_unanchor_urb(victim);
+	} else {
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		victim = NULL;
+	}
+
+	return victim;
+}
+
+EXPORT_SYMBOL_GPL(usb_get_from_anchor);
+
+void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		/* this will unanchor the URB */
+		usb_unanchor_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
+	}
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);
+
+int usb_anchor_empty(struct usb_anchor *anchor)
+{
+	unsigned long flags;
+	int rv;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	rv = list_empty(&anchor->urb_list);
+	spin_unlock_irqrestore(&anchor->lock, flags);
+
+	return rv;
+}
+
+EXPORT_SYMBOL_GPL(usb_anchor_empty);
+
--- linux-2.6.27-rc4/include/linux/usb.h	2008-08-21 10:04:29.000000000 +0200
+++ linux-2.6.27-rc3/include/linux/usb.h	2008-08-22 17:25:47.000000000 +0200
@@ -110,8 +110,6 @@ enum usb_interface_condition {
  * @sysfs_files_created: sysfs attributes exist
  * @needs_remote_wakeup: flag set when the driver requires remote-wakeup
  *	capability during autosuspend.
- * @needs_binding: flag set when the driver should be re-probed or unbound
- *	following a reset or suspend operation it doesn't support.
  * @dev: driver model's view of this device
  * @usb_dev: if an interface is bound to the USB major, this will point
  *	to the sysfs representation for that device.
@@ -1462,6 +1460,9 @@ extern void usb_anchor_urb(struct urb *u
 extern void usb_unanchor_urb(struct urb *urb);
 extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
 					 unsigned int timeout);
+extern struct urb *usb_get_from_anchor(struct usb_anchor *anchor);
+extern void usb_scuttle_anchored_urbs(struct usb_anchor *anchor);
+extern int usb_anchor_empty(struct usb_anchor *anchor);
 
 /**
  * usb_urb_dir_in - check if an URB describes an IN transfer
--- linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-21 10:04:11.000000000 +0200
+++ linux-2.6.27-rc3/drivers/bluetooth/btusb.c	2008-08-25 12:01:15.000000000 +0200
@@ -35,13 +35,13 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-//#define CONFIG_BT_HCIBTUSB_DEBUG
+#define CONFIG_BT_HCIBTUSB_DEBUG
 #ifndef CONFIG_BT_HCIBTUSB_DEBUG
 #undef  BT_DBG
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "0.3"
+#define VERSION "0.4"
 
 static int ignore_dga;
 static int ignore_csr;
@@ -165,10 +165,12 @@ static struct usb_device_id blacklist_ta
 #define BTUSB_INTR_RUNNING	0
 #define BTUSB_BULK_RUNNING	1
 #define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
 
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,11 +178,13 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
 	struct usb_anchor bulk_anchor;
 	struct usb_anchor isoc_anchor;
+	struct usb_anchor deferred;
 
 	struct usb_endpoint_descriptor *intr_ep;
 	struct usb_endpoint_descriptor *bulk_tx_ep;
@@ -189,6 +193,8 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int did_iso_resume:1;
+	int susp_count;
 };
 
 static void btusb_intr_complete(struct urb *urb)
@@ -217,6 +223,7 @@ static void btusb_intr_complete(struct u
 	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->intr_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -227,7 +234,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +247,13 @@ static int btusb_submit_intr_urb(struct
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +269,7 @@ static int btusb_submit_intr_urb(struct
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -301,6 +308,7 @@ static void btusb_bulk_complete(struct u
 	if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -311,7 +319,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +332,19 @@ static int btusb_submit_bulk_urb(struct
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +354,7 @@ static int btusb_submit_bulk_urb(struct
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -508,13 +517,19 @@ static int btusb_open(struct hci_dev *hd
 
 	BT_DBG("%s", hdev->name);
 
+	err = usb_autopm_get_interface(data->acl);
+	if (err < 0)
+		return err;
+	data->acl->needs_remote_wakeup = 1;
+	usb_autopm_put_interface(data->acl);
+
 	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_ATOMIC);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,24 +538,34 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
+	int err;
 
 	BT_DBG("%s", hdev->name);
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
-
+	btusb_stop_traffic(data);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err) {
+		data->acl->needs_remote_wakeup = 0;
+		usb_autopm_put_interface(data->acl);
+	}
 	return 0;
 }
 
@@ -562,7 +587,7 @@ static int btusb_send_frame(struct sk_bu
 	struct usb_ctrlrequest *dr;
 	struct urb *urb;
 	unsigned int pipe;
-	int err;
+	int err, susp;
 
 	BT_DBG("%s", hdev->name);
 
@@ -571,6 +596,7 @@ static int btusb_send_frame(struct sk_bu
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
+		BT_DBG("HCI_COMMAND_PKT");
 		urb = usb_alloc_urb(0, GFP_ATOMIC);
 		if (!urb)
 			return -ENOMEM;
@@ -596,6 +622,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_ACLDATA_PKT:
+		BT_DBG("HCI_ACLDATA_PKT");
 		if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
 			return -ENODEV;
 
@@ -613,6 +640,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_SCODATA_PKT:
+		BT_DBG("HCI_SCODATA_PKT");
 		if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
 			return -ENODEV;
 
@@ -643,17 +671,22 @@ static int btusb_send_frame(struct sk_bu
 		return -EILSEQ;
 	}
 
+	spin_lock(&data->lock);
+	susp = test_bit(BTUSB_SUSPENDING, &data->flags);
 	usb_anchor_urb(urb, &data->tx_anchor);
-
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err < 0) {
-		BT_ERR("%s urb %p submission failed", hdev->name, urb);
-		kfree(urb->setup_packet);
-		usb_unanchor_urb(urb);
+	if (susp) {
+		schedule_work(&data->waker);
+		err = 0;
+	} else {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0) {
+			BT_ERR("%s urb %p submission failed", hdev->name, urb);
+			kfree(urb->setup_packet);
+			usb_unanchor_urb(urb);
+		}
+		usb_free_urb(urb);
 	}
-
-	usb_free_urb(urb);
-
+	spin_unlock(&data->lock);
 	return err;
 }
 
@@ -672,8 +705,19 @@ static void btusb_notify(struct hci_dev
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -723,20 +767,19 @@ static void btusb_work(struct work_struc
 {
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
-
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
+	int err;
 
 	if (hdev->conn_hash.sco_num > 0) {
+		if (!data->did_iso_resume) {
+			err = usb_autopm_get_interface(data->isoc);
+			if (!err) {
+				data->did_iso_resume = 1;
+			} else {
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->isoc_anchor);
+				return;
+			}
+		}
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			usb_kill_anchored_urbs(&data->isoc_anchor);
@@ -756,9 +799,26 @@ static void btusb_work(struct work_struc
 		usb_kill_anchored_urbs(&data->isoc_anchor);
 
 		__set_isoc_interface(hdev, 0);
+		if (data->did_iso_resume) {
+			data->did_iso_resume = 0;
+			usb_autopm_put_interface(data->isoc);
+		}
 	}
 }
 
+static void btusb_waker(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, waker);
+	int err;
+
+	BUG_ON(data == NULL);
+	BT_DBG("about to resume");
+	BUG_ON(data->acl == NULL);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err)
+		usb_autopm_put_interface(data->acl);
+}
+
 static int btusb_probe(struct usb_interface *intf,
 				const struct usb_device_id *id)
 {
@@ -821,15 +881,18 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
 	INIT_WORK(&data->work, btusb_work);
+	INIT_WORK(&data->waker, btusb_waker);
 
 	init_usb_anchor(&data->tx_anchor);
 	init_usb_anchor(&data->intr_anchor);
 	init_usb_anchor(&data->bulk_anchor);
 	init_usb_anchor(&data->isoc_anchor);
+	init_usb_anchor(&data->deferred);
 
 	hdev = hci_alloc_dev();
 	if (!hdev) {
@@ -889,7 +952,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,21 +984,128 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	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("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+	spin_lock_irq(&data->lock);
+	if (	interface_to_usbdev(intf)->auto_pm &&
+		!usb_anchor_empty(&data->tx_anchor)) {
+		spin_unlock_irq(&data->lock);
+		return -EBUSY;
+	}
+
+	set_bit(BTUSB_SUSPENDING, &data->flags);
+	spin_unlock_irq(&data->lock);
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_anchor);
+	return 0;
+}
+
+static int play_deferred(struct btusb_data *data)
+{
+	struct urb *urb;
+	int err = 0;
+
+	while ((urb = usb_get_from_anchor(&data->tx_anchor))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0)
+			break;
+	}
+	usb_scuttle_anchored_urbs(&data->tx_anchor);
+	return err;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+	struct hci_dev *hdev = data->hdev;
+	int ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+
+		spin_lock_irq(&data->lock);
+		ret = play_deferred(data);
+		clear_bit(BTUSB_SUSPENDING, &data->flags);
+		spin_unlock_irq(&data->lock);
+
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
+	.supports_autosuspend = 1,
 };
 
 static int __init btusb_init(void)

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-22 14:31     ` Marcel Holtmann
                         ` (2 preceding siblings ...)
  2008-08-25 10:43       ` Oliver Neukum
@ 2008-08-25 10:43       ` Oliver Neukum
  2008-08-25 11:37       ` btusb hibernation/suspend breakage in current -git Rafael J. Wysocki
  2008-08-25 11:37       ` Rafael J. Wysocki
  5 siblings, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-25 10:43 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-bluetooth, Pavel Machek, linux-usb, Stefan Seyfried, linux-pm

Am Freitag 22 August 2008 16:31:10 schrieb Marcel Holtmann:
> > > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > > anchor all TX URBs anyway.
> > 
> > The completion of an URB may happen after the autosuspend timeout passed.
> > But we cannot use the pm counters as they are not accessible in interrupt.
> > Hence we must maintain a counter ourselves.
> 
> Can we not just check the number of URBs in the anchor? I am against
> just duplicating a counter, but then lets call it it what it is to make
> it gets not misused. It is a purely a PM counter.
> 

An excellent suggestion allowing major simplifications. This version works
for me. It required an extension of the anchor API, so it obsoletes
the split-up you've done. Therefore the whole thing comes as a big patch
against rc4, easy to test.

	Regards
		Oliver

----

--- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000 +0200
+++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-22 17:25:49.000000000 +0200
@@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 {
 	struct urb *victim;
+	unsigned long flags;
 
-	spin_lock_irq(&anchor->lock);
+	spin_lock_irqsave(&anchor->lock, flags);
 	while (!list_empty(&anchor->urb_list)) {
 		victim = list_entry(anchor->urb_list.prev, struct urb,
 				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
 		/* this will unanchor the URB */
 		usb_unlink_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
 	}
-	spin_unlock_irq(&anchor->lock);
+	spin_unlock_irqrestore(&anchor->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 
@@ -628,3 +633,61 @@ int usb_wait_anchor_empty_timeout(struct
 				  msecs_to_jiffies(timeout));
 }
 EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
+struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	if (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.next, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		usb_unanchor_urb(victim);
+	} else {
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		victim = NULL;
+	}
+
+	return victim;
+}
+
+EXPORT_SYMBOL_GPL(usb_get_from_anchor);
+
+void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		/* this will unanchor the URB */
+		usb_unanchor_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
+	}
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);
+
+int usb_anchor_empty(struct usb_anchor *anchor)
+{
+	unsigned long flags;
+	int rv;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	rv = list_empty(&anchor->urb_list);
+	spin_unlock_irqrestore(&anchor->lock, flags);
+
+	return rv;
+}
+
+EXPORT_SYMBOL_GPL(usb_anchor_empty);
+
--- linux-2.6.27-rc4/include/linux/usb.h	2008-08-21 10:04:29.000000000 +0200
+++ linux-2.6.27-rc3/include/linux/usb.h	2008-08-22 17:25:47.000000000 +0200
@@ -110,8 +110,6 @@ enum usb_interface_condition {
  * @sysfs_files_created: sysfs attributes exist
  * @needs_remote_wakeup: flag set when the driver requires remote-wakeup
  *	capability during autosuspend.
- * @needs_binding: flag set when the driver should be re-probed or unbound
- *	following a reset or suspend operation it doesn't support.
  * @dev: driver model's view of this device
  * @usb_dev: if an interface is bound to the USB major, this will point
  *	to the sysfs representation for that device.
@@ -1462,6 +1460,9 @@ extern void usb_anchor_urb(struct urb *u
 extern void usb_unanchor_urb(struct urb *urb);
 extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
 					 unsigned int timeout);
+extern struct urb *usb_get_from_anchor(struct usb_anchor *anchor);
+extern void usb_scuttle_anchored_urbs(struct usb_anchor *anchor);
+extern int usb_anchor_empty(struct usb_anchor *anchor);
 
 /**
  * usb_urb_dir_in - check if an URB describes an IN transfer
--- linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-21 10:04:11.000000000 +0200
+++ linux-2.6.27-rc3/drivers/bluetooth/btusb.c	2008-08-25 12:01:15.000000000 +0200
@@ -35,13 +35,13 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-//#define CONFIG_BT_HCIBTUSB_DEBUG
+#define CONFIG_BT_HCIBTUSB_DEBUG
 #ifndef CONFIG_BT_HCIBTUSB_DEBUG
 #undef  BT_DBG
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "0.3"
+#define VERSION "0.4"
 
 static int ignore_dga;
 static int ignore_csr;
@@ -165,10 +165,12 @@ static struct usb_device_id blacklist_ta
 #define BTUSB_INTR_RUNNING	0
 #define BTUSB_BULK_RUNNING	1
 #define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
 
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,11 +178,13 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
 	struct usb_anchor bulk_anchor;
 	struct usb_anchor isoc_anchor;
+	struct usb_anchor deferred;
 
 	struct usb_endpoint_descriptor *intr_ep;
 	struct usb_endpoint_descriptor *bulk_tx_ep;
@@ -189,6 +193,8 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int did_iso_resume:1;
+	int susp_count;
 };
 
 static void btusb_intr_complete(struct urb *urb)
@@ -217,6 +223,7 @@ static void btusb_intr_complete(struct u
 	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->intr_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -227,7 +234,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +247,13 @@ static int btusb_submit_intr_urb(struct
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +269,7 @@ static int btusb_submit_intr_urb(struct
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -301,6 +308,7 @@ static void btusb_bulk_complete(struct u
 	if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -311,7 +319,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +332,19 @@ static int btusb_submit_bulk_urb(struct
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +354,7 @@ static int btusb_submit_bulk_urb(struct
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -508,13 +517,19 @@ static int btusb_open(struct hci_dev *hd
 
 	BT_DBG("%s", hdev->name);
 
+	err = usb_autopm_get_interface(data->acl);
+	if (err < 0)
+		return err;
+	data->acl->needs_remote_wakeup = 1;
+	usb_autopm_put_interface(data->acl);
+
 	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_ATOMIC);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,24 +538,34 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
+	int err;
 
 	BT_DBG("%s", hdev->name);
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
-
+	btusb_stop_traffic(data);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err) {
+		data->acl->needs_remote_wakeup = 0;
+		usb_autopm_put_interface(data->acl);
+	}
 	return 0;
 }
 
@@ -562,7 +587,7 @@ static int btusb_send_frame(struct sk_bu
 	struct usb_ctrlrequest *dr;
 	struct urb *urb;
 	unsigned int pipe;
-	int err;
+	int err, susp;
 
 	BT_DBG("%s", hdev->name);
 
@@ -571,6 +596,7 @@ static int btusb_send_frame(struct sk_bu
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
+		BT_DBG("HCI_COMMAND_PKT");
 		urb = usb_alloc_urb(0, GFP_ATOMIC);
 		if (!urb)
 			return -ENOMEM;
@@ -596,6 +622,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_ACLDATA_PKT:
+		BT_DBG("HCI_ACLDATA_PKT");
 		if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
 			return -ENODEV;
 
@@ -613,6 +640,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_SCODATA_PKT:
+		BT_DBG("HCI_SCODATA_PKT");
 		if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
 			return -ENODEV;
 
@@ -643,17 +671,22 @@ static int btusb_send_frame(struct sk_bu
 		return -EILSEQ;
 	}
 
+	spin_lock(&data->lock);
+	susp = test_bit(BTUSB_SUSPENDING, &data->flags);
 	usb_anchor_urb(urb, &data->tx_anchor);
-
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err < 0) {
-		BT_ERR("%s urb %p submission failed", hdev->name, urb);
-		kfree(urb->setup_packet);
-		usb_unanchor_urb(urb);
+	if (susp) {
+		schedule_work(&data->waker);
+		err = 0;
+	} else {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0) {
+			BT_ERR("%s urb %p submission failed", hdev->name, urb);
+			kfree(urb->setup_packet);
+			usb_unanchor_urb(urb);
+		}
+		usb_free_urb(urb);
 	}
-
-	usb_free_urb(urb);
-
+	spin_unlock(&data->lock);
 	return err;
 }
 
@@ -672,8 +705,19 @@ static void btusb_notify(struct hci_dev
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -723,20 +767,19 @@ static void btusb_work(struct work_struc
 {
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
-
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
+	int err;
 
 	if (hdev->conn_hash.sco_num > 0) {
+		if (!data->did_iso_resume) {
+			err = usb_autopm_get_interface(data->isoc);
+			if (!err) {
+				data->did_iso_resume = 1;
+			} else {
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->isoc_anchor);
+				return;
+			}
+		}
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			usb_kill_anchored_urbs(&data->isoc_anchor);
@@ -756,9 +799,26 @@ static void btusb_work(struct work_struc
 		usb_kill_anchored_urbs(&data->isoc_anchor);
 
 		__set_isoc_interface(hdev, 0);
+		if (data->did_iso_resume) {
+			data->did_iso_resume = 0;
+			usb_autopm_put_interface(data->isoc);
+		}
 	}
 }
 
+static void btusb_waker(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, waker);
+	int err;
+
+	BUG_ON(data == NULL);
+	BT_DBG("about to resume");
+	BUG_ON(data->acl == NULL);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err)
+		usb_autopm_put_interface(data->acl);
+}
+
 static int btusb_probe(struct usb_interface *intf,
 				const struct usb_device_id *id)
 {
@@ -821,15 +881,18 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
 	INIT_WORK(&data->work, btusb_work);
+	INIT_WORK(&data->waker, btusb_waker);
 
 	init_usb_anchor(&data->tx_anchor);
 	init_usb_anchor(&data->intr_anchor);
 	init_usb_anchor(&data->bulk_anchor);
 	init_usb_anchor(&data->isoc_anchor);
+	init_usb_anchor(&data->deferred);
 
 	hdev = hci_alloc_dev();
 	if (!hdev) {
@@ -889,7 +952,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,21 +984,128 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	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("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+	spin_lock_irq(&data->lock);
+	if (	interface_to_usbdev(intf)->auto_pm &&
+		!usb_anchor_empty(&data->tx_anchor)) {
+		spin_unlock_irq(&data->lock);
+		return -EBUSY;
+	}
+
+	set_bit(BTUSB_SUSPENDING, &data->flags);
+	spin_unlock_irq(&data->lock);
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_anchor);
+	return 0;
+}
+
+static int play_deferred(struct btusb_data *data)
+{
+	struct urb *urb;
+	int err = 0;
+
+	while ((urb = usb_get_from_anchor(&data->tx_anchor))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0)
+			break;
+	}
+	usb_scuttle_anchored_urbs(&data->tx_anchor);
+	return err;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+	struct hci_dev *hdev = data->hdev;
+	int ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+
+		spin_lock_irq(&data->lock);
+		ret = play_deferred(data);
+		clear_bit(BTUSB_SUSPENDING, &data->flags);
+		spin_unlock_irq(&data->lock);
+
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
+	.supports_autosuspend = 1,
 };
 
 static int __init btusb_init(void)

^ permalink raw reply	[flat|nested] 73+ messages in thread

* btusb hibernation/suspend breakage in current -git
  2008-08-22 14:31     ` Marcel Holtmann
                         ` (4 preceding siblings ...)
  2008-08-25 11:37       ` btusb hibernation/suspend breakage in current -git Rafael J. Wysocki
@ 2008-08-25 11:37       ` Rafael J. Wysocki
  2008-08-25 11:53         ` Oliver Neukum
                           ` (4 more replies)
  5 siblings, 5 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-25 11:37 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-pm, Oliver Neukum, linux-bluetooth, Pavel Machek,
	linux-usb, Stefan Seyfried

Hi,

Speaking of btusb, I'm obseving a reproducible breakage of suspend and
hibernation (for hibernation it's 100% reproducible, for suspend to RAM about
50%) on my hp nx6325 with the current -git kernel.

The system crashes as soon as user space is thawed after a resume and it's
sufficient to switch the bluetooth subsystem off to avoid this.  After such a
crash I'm usually unable to get any information from the box (from the stack
traces I've seen once after such a crash it looks like interrupt handlers are
involved somehow).

Since the bluetooth adapter in this box is apparently handled by btusb, I'm
suspecting the problem may be related to it or to the USB subsystem (ehci-hcd
in particular).  I'm going to debug it further, but if you have any ideas to
try, I'll appreciate any help.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* btusb hibernation/suspend breakage in current -git
  2008-08-22 14:31     ` Marcel Holtmann
                         ` (3 preceding siblings ...)
  2008-08-25 10:43       ` Oliver Neukum
@ 2008-08-25 11:37       ` Rafael J. Wysocki
  2008-08-25 11:37       ` Rafael J. Wysocki
  5 siblings, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-25 11:37 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

Hi,

Speaking of btusb, I'm obseving a reproducible breakage of suspend and
hibernation (for hibernation it's 100% reproducible, for suspend to RAM about
50%) on my hp nx6325 with the current -git kernel.

The system crashes as soon as user space is thawed after a resume and it's
sufficient to switch the bluetooth subsystem off to avoid this.  After such a
crash I'm usually unable to get any information from the box (from the stack
traces I've seen once after such a crash it looks like interrupt handlers are
involved somehow).

Since the bluetooth adapter in this box is apparently handled by btusb, I'm
suspecting the problem may be related to it or to the USB subsystem (ehci-hcd
in particular).  I'm going to debug it further, but if you have any ideas to
try, I'll appreciate any help.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-25 10:43       ` Oliver Neukum
  2008-08-25 11:51         ` Marcel Holtmann
@ 2008-08-25 11:51         ` Marcel Holtmann
  2009-02-11 16:52           ` [linux-pm] " Matthew Garrett
  2009-02-11 16:52           ` Matthew Garrett
  2008-08-26  9:56         ` Pavel Machek
  2008-08-26  9:56         ` Pavel Machek
  3 siblings, 2 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-25 11:51 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Pavel Machek, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

Hi Oliver,

>>>> Please explain the tx_in_flight stuff to me. It looks unneeded  
>>>> since we
>>>> anchor all TX URBs anyway.
>>>
>>> The completion of an URB may happen after the autosuspend timeout  
>>> passed.
>>> But we cannot use the pm counters as they are not accessible in  
>>> interrupt.
>>> Hence we must maintain a counter ourselves.
>>
>> Can we not just check the number of URBs in the anchor? I am against
>> just duplicating a counter, but then lets call it it what it is to  
>> make
>> it gets not misused. It is a purely a PM counter.
>>
>
> An excellent suggestion allowing major simplifications. This version  
> works
> for me. It required an extension of the anchor API, so it obsoletes
> the split-up you've done. Therefore the whole thing comes as a big  
> patch
> against rc4, easy to test.

I will break up the patch into pieces. However right now I am missing  
my beloved test machine since I am traveling. So everybody reporting  
success or failure would be nice.

Can you break out the USB anchor extensions and make sure they get  
merged into 2.6.28 in an early stage of the merge window. It really  
wanna get all of this stuff into the next kernel release. Finally we  
are getting somewhere with this driver and can kill the broken hci_usb.

Regards

Marcel


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-25 10:43       ` Oliver Neukum
@ 2008-08-25 11:51         ` Marcel Holtmann
  2008-08-25 11:51         ` Marcel Holtmann
                           ` (2 subsequent siblings)
  3 siblings, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-25 11:51 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-bluetooth, Pavel Machek, linux-usb, Stefan Seyfried, linux-pm

Hi Oliver,

>>>> Please explain the tx_in_flight stuff to me. It looks unneeded  
>>>> since we
>>>> anchor all TX URBs anyway.
>>>
>>> The completion of an URB may happen after the autosuspend timeout  
>>> passed.
>>> But we cannot use the pm counters as they are not accessible in  
>>> interrupt.
>>> Hence we must maintain a counter ourselves.
>>
>> Can we not just check the number of URBs in the anchor? I am against
>> just duplicating a counter, but then lets call it it what it is to  
>> make
>> it gets not misused. It is a purely a PM counter.
>>
>
> An excellent suggestion allowing major simplifications. This version  
> works
> for me. It required an extension of the anchor API, so it obsoletes
> the split-up you've done. Therefore the whole thing comes as a big  
> patch
> against rc4, easy to test.

I will break up the patch into pieces. However right now I am missing  
my beloved test machine since I am traveling. So everybody reporting  
success or failure would be nice.

Can you break out the USB anchor extensions and make sure they get  
merged into 2.6.28 in an early stage of the merge window. It really  
wanna get all of this stuff into the next kernel release. Finally we  
are getting somewhere with this driver and can kill the broken hci_usb.

Regards

Marcel

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:37       ` Rafael J. Wysocki
@ 2008-08-25 11:53         ` Oliver Neukum
  2008-08-25 11:55           ` Marcel Holtmann
  2008-08-25 11:55           ` Marcel Holtmann
  2008-08-25 11:53         ` Oliver Neukum
                           ` (3 subsequent siblings)
  4 siblings, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-25 11:53 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Marcel Holtmann, linux-pm, linux-bluetooth, Pavel Machek,
	linux-usb, Stefan Seyfried

Am Montag 25 August 2008 13:37:14 schrieb Rafael J. Wysocki:
> Since the bluetooth adapter in this box is apparently handled by btusb, I=
'm
> suspecting the problem may be related to it or to the USB subsystem (ehci=
=2Dhcd
> in particular). =A0I'm going to debug it further, but if you have any ide=
as to
> try, I'll appreciate any help.

btusb in current git doesn't implement suspend/resume. Thus the driver will=
 be
unbound. Possibly the bluetooth subsystem cannot stand that while suspended.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:37       ` Rafael J. Wysocki
  2008-08-25 11:53         ` Oliver Neukum
@ 2008-08-25 11:53         ` Oliver Neukum
  2008-08-25 11:54         ` Marcel Holtmann
                           ` (2 subsequent siblings)
  4 siblings, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-25 11:53 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

Am Montag 25 August 2008 13:37:14 schrieb Rafael J. Wysocki:
> Since the bluetooth adapter in this box is apparently handled by btusb, I'm
> suspecting the problem may be related to it or to the USB subsystem (ehci-hcd
> in particular).  I'm going to debug it further, but if you have any ideas to
> try, I'll appreciate any help.

btusb in current git doesn't implement suspend/resume. Thus the driver will be
unbound. Possibly the bluetooth subsystem cannot stand that while suspended.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:37       ` Rafael J. Wysocki
  2008-08-25 11:53         ` Oliver Neukum
  2008-08-25 11:53         ` Oliver Neukum
@ 2008-08-25 11:54         ` Marcel Holtmann
  2008-08-25 13:50           ` Oliver Neukum
  2008-08-25 13:50           ` Oliver Neukum
  2008-08-25 12:45         ` Pavel Machek
  2008-08-25 12:45         ` Pavel Machek
  4 siblings, 2 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-25 11:54 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

Hi Rafael,

> Speaking of btusb, I'm obseving a reproducible breakage of suspend and
> hibernation (for hibernation it's 100% reproducible, for suspend to  
> RAM about
> 50%) on my hp nx6325 with the current -git kernel.
>
> The system crashes as soon as user space is thawed after a resume  
> and it's
> sufficient to switch the bluetooth subsystem off to avoid this.   
> After such a
> crash I'm usually unable to get any information from the box (from  
> the stack
> traces I've seen once after such a crash it looks like interrupt  
> handlers are
> involved somehow).
>
> Since the bluetooth adapter in this box is apparently handled by  
> btusb, I'm
> suspecting the problem may be related to it or to the USB subsystem  
> (ehci-hcd
> in particular).  I'm going to debug it further, but if you have any  
> ideas to
> try, I'll appreciate any help.

the current version has no suspend/resume callbacks. However in that  
case the USB subystem should take of it, but I might be wrong here. So  
in case of btusb and not using it, we only have interrupt URBs in fly.  
However these might get screwed up.

So does unloading the driver before suspend fixes it? What about the  
latest patch from Oliver. It will add the suspend/resume callbacks  
that are needed.

Oliver, can you come up with a small test patch, that just kills all  
URB when suspend and submits the interrupt ones at resume. Such a  
patch might have to be merged for 2.6.27 if it fixes this problem.

Regards

Marcel

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:53         ` Oliver Neukum
@ 2008-08-25 11:55           ` Marcel Holtmann
  2008-08-25 11:55           ` Marcel Holtmann
  1 sibling, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-25 11:55 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Rafael J. Wysocki, linux-pm, linux-bluetooth, Pavel Machek,
	linux-usb, Stefan Seyfried

Hi Oliver,

>> Since the bluetooth adapter in this box is apparently handled by  
>> btusb, I'm
>> suspecting the problem may be related to it or to the USB subsystem  
>> (ehci-hcd
>> in particular).  I'm going to debug it further, but if you have any  
>> ideas to
>> try, I'll appreciate any help.
>
> btusb in current git doesn't implement suspend/resume. Thus the  
> driver will be
> unbound. Possibly the bluetooth subsystem cannot stand that while  
> suspended.

that should work. It then really looks like a bug. Something more  
weird must be going on here.

Regards

Marcel


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:53         ` Oliver Neukum
  2008-08-25 11:55           ` Marcel Holtmann
@ 2008-08-25 11:55           ` Marcel Holtmann
  1 sibling, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-25 11:55 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

Hi Oliver,

>> Since the bluetooth adapter in this box is apparently handled by  
>> btusb, I'm
>> suspecting the problem may be related to it or to the USB subsystem  
>> (ehci-hcd
>> in particular).  I'm going to debug it further, but if you have any  
>> ideas to
>> try, I'll appreciate any help.
>
> btusb in current git doesn't implement suspend/resume. Thus the  
> driver will be
> unbound. Possibly the bluetooth subsystem cannot stand that while  
> suspended.

that should work. It then really looks like a bug. Something more  
weird must be going on here.

Regards

Marcel

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:37       ` Rafael J. Wysocki
                           ` (3 preceding siblings ...)
  2008-08-25 12:45         ` Pavel Machek
@ 2008-08-25 12:45         ` Pavel Machek
  4 siblings, 0 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-25 12:45 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Marcel Holtmann, linux-pm, Oliver Neukum, linux-bluetooth,
	linux-usb, Stefan Seyfried

Hi!

> Speaking of btusb, I'm obseving a reproducible breakage of suspend and
> hibernation (for hibernation it's 100% reproducible, for suspend to RAM about
> 50%) on my hp nx6325 with the current -git kernel.

I have been using btusb + s2ram for quite a long now... (and major
difference between hci_usb and btusb is that btusb will not randomly
corrupt memory when suspending with bluetooth in use...) lets see if I
can update to latest git...
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:37       ` Rafael J. Wysocki
                           ` (2 preceding siblings ...)
  2008-08-25 11:54         ` Marcel Holtmann
@ 2008-08-25 12:45         ` Pavel Machek
  2008-08-25 12:45         ` Pavel Machek
  4 siblings, 0 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-25 12:45 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-usb, Marcel Holtmann, linux-bluetooth, linux-pm, Stefan Seyfried

Hi!

> Speaking of btusb, I'm obseving a reproducible breakage of suspend and
> hibernation (for hibernation it's 100% reproducible, for suspend to RAM about
> 50%) on my hp nx6325 with the current -git kernel.

I have been using btusb + s2ram for quite a long now... (and major
difference between hci_usb and btusb is that btusb will not randomly
corrupt memory when suspending with bluetooth in use...) lets see if I
can update to latest git...
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:54         ` Marcel Holtmann
@ 2008-08-25 13:50           ` Oliver Neukum
  2008-08-26  9:36             ` Rafael J. Wysocki
  2008-08-26  9:36             ` Rafael J. Wysocki
  2008-08-25 13:50           ` Oliver Neukum
  1 sibling, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-25 13:50 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Rafael J.Wysocki, linux-pm, linux-bluetooth, Pavel Machek,
	linux-usb, Stefan Seyfried

Am Montag 25 August 2008 13:54:31 schrieb Marcel Holtmann:
> Oliver, can you come up with a small test patch, that just kills all =A0
> URB when suspend and submits the interrupt ones at resume. Such a =A0
> patch might have to be merged for 2.6.27 if it fixes this problem.

Rafael,

this patch implemts suspend/resume for btusb and fixes a disconnect
problem. Does it help you?

	Regards
		Oliver

=2D--

=2D-- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000=
 +0200
+++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-22 17:25:49.000000000 +=
0200
@@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 {
 	struct urb *victim;
+	unsigned long flags;
=20
=2D	spin_lock_irq(&anchor->lock);
+	spin_lock_irqsave(&anchor->lock, flags);
 	while (!list_empty(&anchor->urb_list)) {
 		victim =3D list_entry(anchor->urb_list.prev, struct urb,
 				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
 		/* this will unanchor the URB */
 		usb_unlink_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
 	}
=2D	spin_unlock_irq(&anchor->lock);
+	spin_unlock_irqrestore(&anchor->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
=20
=2D-- linux-2.6.27-rc4/drivers/bluetooth/btusb.c.alt	2008-08-25 15:02:14.00=
0000000 +0200
+++ linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-25 15:44:25.00000000=
0 +0200
@@ -169,6 +169,7 @@ static struct usb_device_id blacklist_ta
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
=20
 	spinlock_t lock;
@@ -176,6 +177,7 @@ struct btusb_data {
 	unsigned long flags;
=20
 	struct work_struct work;
+	struct work_struct waker;
=20
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
@@ -189,6 +191,7 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
=20
 	int isoc_altsetting;
+	int susp_count;
 };
=20
 static void btusb_intr_complete(struct urb *urb)
@@ -227,7 +230,7 @@ static void btusb_intr_complete(struct u
 	}
 }
=20
=2Dstatic int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data =3D hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +243,13 @@ static int btusb_submit_intr_urb(struct
 	if (!data->intr_ep)
 		return -ENODEV;
=20
=2D	urb =3D usb_alloc_urb(0, GFP_ATOMIC);
+	urb =3D usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
=20
 	size =3D le16_to_cpu(data->intr_ep->wMaxPacketSize);
=20
=2D	buf =3D kmalloc(size, GFP_ATOMIC);
+	buf =3D kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +265,7 @@ static int btusb_submit_intr_urb(struct
=20
 	usb_anchor_urb(urb, &data->intr_anchor);
=20
=2D	err =3D usb_submit_urb(urb, GFP_ATOMIC);
+	err =3D usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -311,7 +314,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
=20
=2Dstatic int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data =3D hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +327,19 @@ static int btusb_submit_bulk_urb(struct
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
=20
=2D	urb =3D usb_alloc_urb(0, GFP_KERNEL);
+	urb =3D usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
=20
 	size =3D le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
=20
=2D	buf =3D kmalloc(size, GFP_KERNEL);
+	buf =3D kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
=20
+	usb_mark_last_busy(data->udev);
 	pipe =3D usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
=20
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +349,7 @@ static int btusb_submit_bulk_urb(struct
=20
 	usb_anchor_urb(urb, &data->bulk_anchor);
=20
=2D	err =3D usb_submit_urb(urb, GFP_KERNEL);
+	err =3D usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -514,7 +518,7 @@ static int btusb_open(struct hci_dev *hd
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
=20
=2D	err =3D btusb_submit_intr_urb(hdev);
+	err =3D btusb_submit_intr_urb(hdev, GFP_KERNEL);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,6 +527,13 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
=20
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data =3D hdev->driver_data;
@@ -532,14 +543,12 @@ static int btusb_close(struct hci_dev *h
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
=20
=2D	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
=2D	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
=20
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
=2D	usb_kill_anchored_urbs(&data->bulk_anchor);
=2D
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
=2D	usb_kill_anchored_urbs(&data->intr_anchor);
+	btusb_stop_traffic(data);
=20
 	return 0;
 }
@@ -672,8 +681,19 @@ static void btusb_notify(struct hci_dev
=20
 	BT_DBG("%s evt %d", hdev->name, evt);
=20
=2D	if (evt =3D=3D HCI_NOTIFY_CONN_ADD || evt =3D=3D HCI_NOTIFY_CONN_DEL)
=2D		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
=20
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsettin=
g)
@@ -724,18 +744,6 @@ static void btusb_work(struct work_struc
 	struct btusb_data *data =3D container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev =3D data->hdev;
=20
=2D	if (hdev->conn_hash.acl_num > 0) {
=2D		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
=2D			if (btusb_submit_bulk_urb(hdev) < 0)
=2D				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
=2D			else
=2D				btusb_submit_bulk_urb(hdev);
=2D		}
=2D	} else {
=2D		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
=2D		usb_kill_anchored_urbs(&data->bulk_anchor);
=2D	}
=2D
 	if (hdev->conn_hash.sco_num > 0) {
 		if (data->isoc_altsetting !=3D 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
@@ -821,6 +829,7 @@ static int btusb_probe(struct usb_interf
 	}
=20
 	data->udev =3D interface_to_usbdev(intf);
+	data->acl =3D intf;
=20
 	spin_lock_init(&data->lock);
=20
@@ -889,7 +898,7 @@ static int btusb_probe(struct usb_interf
=20
 	if (data->isoc) {
 		err =3D usb_driver_claim_interface(&btusb_driver,
=2D							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,20 +930,92 @@ static void btusb_disconnect(struct usb_
=20
 	hdev =3D data->hdev;
=20
=2D	if (data->isoc)
=2D		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
=20
=2D	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
=20
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
=20
+	if (intf =3D=3D data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	hci_free_dev(hdev);
 }
=20
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct btusb_data *data =3D usb_get_intfdata(intf);
+
+	BT_DBG("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_anchor);
+	return 0;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+	struct btusb_data *data =3D usb_get_intfdata(intf);
+	struct hci_dev *hdev =3D data->hdev;
+	int ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+		ret =3D btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret =3D btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret =3D btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret =3D btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	return 0;
+}
+
 static struct usb_driver btusb_driver =3D {
 	.name		=3D "btusb",
 	.probe		=3D btusb_probe,
 	.disconnect	=3D btusb_disconnect,
+	.suspend	=3D btusb_suspend,
+	.resume		=3D btusb_resume,
 	.id_table	=3D btusb_table,
 };
=20

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 11:54         ` Marcel Holtmann
  2008-08-25 13:50           ` Oliver Neukum
@ 2008-08-25 13:50           ` Oliver Neukum
  1 sibling, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-25 13:50 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

Am Montag 25 August 2008 13:54:31 schrieb Marcel Holtmann:
> Oliver, can you come up with a small test patch, that just kills all  
> URB when suspend and submits the interrupt ones at resume. Such a  
> patch might have to be merged for 2.6.27 if it fixes this problem.

Rafael,

this patch implemts suspend/resume for btusb and fixes a disconnect
problem. Does it help you?

	Regards
		Oliver

---

--- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000 +0200
+++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-22 17:25:49.000000000 +0200
@@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 {
 	struct urb *victim;
+	unsigned long flags;
 
-	spin_lock_irq(&anchor->lock);
+	spin_lock_irqsave(&anchor->lock, flags);
 	while (!list_empty(&anchor->urb_list)) {
 		victim = list_entry(anchor->urb_list.prev, struct urb,
 				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
 		/* this will unanchor the URB */
 		usb_unlink_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
 	}
-	spin_unlock_irq(&anchor->lock);
+	spin_unlock_irqrestore(&anchor->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 
--- linux-2.6.27-rc4/drivers/bluetooth/btusb.c.alt	2008-08-25 15:02:14.000000000 +0200
+++ linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-25 15:44:25.000000000 +0200
@@ -169,6 +169,7 @@ static struct usb_device_id blacklist_ta
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,6 +177,7 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
@@ -189,6 +191,7 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int susp_count;
 };
 
 static void btusb_intr_complete(struct urb *urb)
@@ -227,7 +230,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +243,13 @@ static int btusb_submit_intr_urb(struct
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +265,7 @@ static int btusb_submit_intr_urb(struct
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -311,7 +314,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +327,19 @@ static int btusb_submit_bulk_urb(struct
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +349,7 @@ static int btusb_submit_bulk_urb(struct
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -514,7 +518,7 @@ static int btusb_open(struct hci_dev *hd
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,6 +527,13 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
@@ -532,14 +543,12 @@ static int btusb_close(struct hci_dev *h
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	btusb_stop_traffic(data);
 
 	return 0;
 }
@@ -672,8 +681,19 @@ static void btusb_notify(struct hci_dev
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -724,18 +744,6 @@ static void btusb_work(struct work_struc
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
 
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
-
 	if (hdev->conn_hash.sco_num > 0) {
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
@@ -821,6 +829,7 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
@@ -889,7 +898,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,20 +930,92 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	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("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_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 ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
 };
 

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 13:50           ` Oliver Neukum
@ 2008-08-26  9:36             ` Rafael J. Wysocki
  2008-08-26  9:43               ` Oliver Neukum
  2008-08-26  9:43               ` Oliver Neukum
  2008-08-26  9:36             ` Rafael J. Wysocki
  1 sibling, 2 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-26  9:36 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, linux-pm, linux-bluetooth, Pavel Machek,
	linux-usb, Stefan Seyfried

[Sorry for the delayed reply.]

On Monday, 25 of August 2008, Oliver Neukum wrote:
> Am Montag 25 August 2008 13:54:31 schrieb Marcel Holtmann:
> > Oliver, can you come up with a small test patch, that just kills all  
> > URB when suspend and submits the interrupt ones at resume. Such a  
> > patch might have to be merged for 2.6.27 if it fixes this problem.
> 
> Rafael,
> 
> this patch implemts suspend/resume for btusb and fixes a disconnect
> problem. Does it help you?

Yes, the patch appears to help.

I haven't had a single crash since I applied it.  I'm going to test it a bit
more today, though.

> ---

BTW, this change:

> --- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000 +0200
> +++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-22 17:25:49.000000000 +0200
> @@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
>  void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
>  {
>  	struct urb *victim;
> +	unsigned long flags;
>  
> -	spin_lock_irq(&anchor->lock);
> +	spin_lock_irqsave(&anchor->lock, flags);
>  	while (!list_empty(&anchor->urb_list)) {
>  		victim = list_entry(anchor->urb_list.prev, struct urb,
>  				    anchor_list);
> +		usb_get_urb(victim);
> +		spin_unlock_irqrestore(&anchor->lock, flags);
>  		/* this will unanchor the URB */
>  		usb_unlink_urb(victim);
> +		usb_put_urb(victim);
> +		spin_lock_irqsave(&anchor->lock, flags);
>  	}
> -	spin_unlock_irq(&anchor->lock);
> +	spin_unlock_irqrestore(&anchor->lock, flags);
>  }
>  EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
>

is apparently already in the mainline.

Thanks,
Rafael


> --- linux-2.6.27-rc4/drivers/bluetooth/btusb.c.alt	2008-08-25 15:02:14.000000000 +0200
> +++ linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-25 15:44:25.000000000 +0200
> @@ -169,6 +169,7 @@ static struct usb_device_id blacklist_ta
>  struct btusb_data {
>  	struct hci_dev       *hdev;
>  	struct usb_device    *udev;
> +	struct usb_interface *acl;
>  	struct usb_interface *isoc;
>  
>  	spinlock_t lock;
> @@ -176,6 +177,7 @@ struct btusb_data {
>  	unsigned long flags;
>  
>  	struct work_struct work;
> +	struct work_struct waker;
>  
>  	struct usb_anchor tx_anchor;
>  	struct usb_anchor intr_anchor;
> @@ -189,6 +191,7 @@ struct btusb_data {
>  	struct usb_endpoint_descriptor *isoc_rx_ep;
>  
>  	int isoc_altsetting;
> +	int susp_count;
>  };
>  
>  static void btusb_intr_complete(struct urb *urb)
> @@ -227,7 +230,7 @@ static void btusb_intr_complete(struct u
>  	}
>  }
>  
> -static int btusb_submit_intr_urb(struct hci_dev *hdev)
> +static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
>  {
>  	struct btusb_data *data = hdev->driver_data;
>  	struct urb *urb;
> @@ -240,13 +243,13 @@ static int btusb_submit_intr_urb(struct
>  	if (!data->intr_ep)
>  		return -ENODEV;
>  
> -	urb = usb_alloc_urb(0, GFP_ATOMIC);
> +	urb = usb_alloc_urb(0, gfp);
>  	if (!urb)
>  		return -ENOMEM;
>  
>  	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
>  
> -	buf = kmalloc(size, GFP_ATOMIC);
> +	buf = kmalloc(size, gfp);
>  	if (!buf) {
>  		usb_free_urb(urb);
>  		return -ENOMEM;
> @@ -262,7 +265,7 @@ static int btusb_submit_intr_urb(struct
>  
>  	usb_anchor_urb(urb, &data->intr_anchor);
>  
> -	err = usb_submit_urb(urb, GFP_ATOMIC);
> +	err = usb_submit_urb(urb, gfp);
>  	if (err < 0) {
>  		BT_ERR("%s urb %p submission failed (%d)",
>  						hdev->name, urb, -err);
> @@ -311,7 +314,7 @@ static void btusb_bulk_complete(struct u
>  	}
>  }
>  
> -static int btusb_submit_bulk_urb(struct hci_dev *hdev)
> +static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
>  {
>  	struct btusb_data *data = hdev->driver_data;
>  	struct urb *urb;
> @@ -324,18 +327,19 @@ static int btusb_submit_bulk_urb(struct
>  	if (!data->bulk_rx_ep)
>  		return -ENODEV;
>  
> -	urb = usb_alloc_urb(0, GFP_KERNEL);
> +	urb = usb_alloc_urb(0, gfp);
>  	if (!urb)
>  		return -ENOMEM;
>  
>  	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
>  
> -	buf = kmalloc(size, GFP_KERNEL);
> +	buf = kmalloc(size, gfp);
>  	if (!buf) {
>  		usb_free_urb(urb);
>  		return -ENOMEM;
>  	}
>  
> +	usb_mark_last_busy(data->udev);
>  	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
>  
>  	usb_fill_bulk_urb(urb, data->udev, pipe,
> @@ -345,7 +349,7 @@ static int btusb_submit_bulk_urb(struct
>  
>  	usb_anchor_urb(urb, &data->bulk_anchor);
>  
> -	err = usb_submit_urb(urb, GFP_KERNEL);
> +	err = usb_submit_urb(urb, gfp);
>  	if (err < 0) {
>  		BT_ERR("%s urb %p submission failed (%d)",
>  						hdev->name, urb, -err);
> @@ -514,7 +518,7 @@ static int btusb_open(struct hci_dev *hd
>  	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
>  		return 0;
>  
> -	err = btusb_submit_intr_urb(hdev);
> +	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
>  	if (err < 0) {
>  		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
>  		clear_bit(HCI_RUNNING, &hdev->flags);
> @@ -523,6 +527,13 @@ static int btusb_open(struct hci_dev *hd
>  	return err;
>  }
>  
> +static void btusb_stop_traffic(struct btusb_data *data)
> +{
> +	usb_kill_anchored_urbs(&data->intr_anchor);
> +	usb_kill_anchored_urbs(&data->bulk_anchor);
> +	usb_kill_anchored_urbs(&data->isoc_anchor);
> +}
> +
>  static int btusb_close(struct hci_dev *hdev)
>  {
>  	struct btusb_data *data = hdev->driver_data;
> @@ -532,14 +543,12 @@ static int btusb_close(struct hci_dev *h
>  	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
>  		return 0;
>  
> -	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> -	usb_kill_anchored_urbs(&data->intr_anchor);
> +	flush_work(&data->work);
>  
> +	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
>  	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> -	usb_kill_anchored_urbs(&data->bulk_anchor);
> -
>  	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
> -	usb_kill_anchored_urbs(&data->intr_anchor);
> +	btusb_stop_traffic(data);
>  
>  	return 0;
>  }
> @@ -672,8 +681,19 @@ static void btusb_notify(struct hci_dev
>  
>  	BT_DBG("%s evt %d", hdev->name, evt);
>  
> -	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
> -		schedule_work(&data->work);
> +	if (hdev->conn_hash.acl_num > 0) {
> +		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
> +			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
> +				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +			else
> +				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
> +		}
> +	} else {
> +		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +		usb_unlink_anchored_urbs(&data->bulk_anchor);
> +	}
> +
> +	schedule_work(&data->work);
>  }
>  
>  static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
> @@ -724,18 +744,6 @@ static void btusb_work(struct work_struc
>  	struct btusb_data *data = container_of(work, struct btusb_data, work);
>  	struct hci_dev *hdev = data->hdev;
>  
> -	if (hdev->conn_hash.acl_num > 0) {
> -		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
> -			if (btusb_submit_bulk_urb(hdev) < 0)
> -				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> -			else
> -				btusb_submit_bulk_urb(hdev);
> -		}
> -	} else {
> -		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> -		usb_kill_anchored_urbs(&data->bulk_anchor);
> -	}
> -
>  	if (hdev->conn_hash.sco_num > 0) {
>  		if (data->isoc_altsetting != 2) {
>  			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> @@ -821,6 +829,7 @@ static int btusb_probe(struct usb_interf
>  	}
>  
>  	data->udev = interface_to_usbdev(intf);
> +	data->acl = intf;
>  
>  	spin_lock_init(&data->lock);
>  
> @@ -889,7 +898,7 @@ static int btusb_probe(struct usb_interf
>  
>  	if (data->isoc) {
>  		err = usb_driver_claim_interface(&btusb_driver,
> -							data->isoc, NULL);
> +							data->isoc, data);
>  		if (err < 0) {
>  			hci_free_dev(hdev);
>  			kfree(data);
> @@ -921,20 +930,92 @@ static void btusb_disconnect(struct usb_
>  
>  	hdev = data->hdev;
>  
> -	if (data->isoc)
> -		usb_driver_release_interface(&btusb_driver, data->isoc);
> +	/* make sure we have a reference */
> +	__hci_dev_hold(hdev);
>  
> -	usb_set_intfdata(intf, NULL);
> +	usb_set_intfdata(data->acl, NULL);
> +	if (data->isoc)
> +		usb_set_intfdata(data->isoc, NULL);
>  
> +	/* unregister before releasing any interface */
>  	hci_unregister_dev(hdev);
>  
> +	if (intf == data->isoc)
> +		usb_driver_release_interface(&btusb_driver, data->acl);
> +	else if (data->isoc)
> +		usb_driver_release_interface(&btusb_driver, data->isoc);
> +
> +	/* release the reference */
> +	__hci_dev_put(hdev);
>  	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("%s called\n", __func__);
> +
> +	if (data->susp_count++)
> +		return 0;
> +
> +	cancel_work_sync(&data->work);
> +	btusb_stop_traffic(data);
> +	usb_kill_anchored_urbs(&data->tx_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 ret;
> +
> +	if (--data->susp_count)
> +		return 0;
> +	if (test_bit(HCI_RUNNING, &hdev->flags)) {
> +		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
> +		if (ret < 0) {
> +			clear_bit(HCI_RUNNING, &hdev->flags);
> +			return ret;
> +		}
> +	}
> +
> +	if (hdev->conn_hash.acl_num > 0) {
> +		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
> +		if (ret < 0) {
> +			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +			return ret;
> +		} else {
> +			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
> +			if (ret < 0) {
> +				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +				usb_kill_anchored_urbs(&data->bulk_anchor);
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	if (data->isoc) {
> +		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
> +			ret = btusb_submit_isoc_urb(hdev);
> +			if (ret < 0)
> +				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> +			else
> +				btusb_submit_isoc_urb(hdev);
> +		}
> +	}
> +
> +	schedule_work(&data->work);
> +	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,
>  };
>  
> 
> 
> 



^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-25 13:50           ` Oliver Neukum
  2008-08-26  9:36             ` Rafael J. Wysocki
@ 2008-08-26  9:36             ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-26  9:36 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

[Sorry for the delayed reply.]

On Monday, 25 of August 2008, Oliver Neukum wrote:
> Am Montag 25 August 2008 13:54:31 schrieb Marcel Holtmann:
> > Oliver, can you come up with a small test patch, that just kills all  
> > URB when suspend and submits the interrupt ones at resume. Such a  
> > patch might have to be merged for 2.6.27 if it fixes this problem.
> 
> Rafael,
> 
> this patch implemts suspend/resume for btusb and fixes a disconnect
> problem. Does it help you?

Yes, the patch appears to help.

I haven't had a single crash since I applied it.  I'm going to test it a bit
more today, though.

> ---

BTW, this change:

> --- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000 +0200
> +++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-22 17:25:49.000000000 +0200
> @@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
>  void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
>  {
>  	struct urb *victim;
> +	unsigned long flags;
>  
> -	spin_lock_irq(&anchor->lock);
> +	spin_lock_irqsave(&anchor->lock, flags);
>  	while (!list_empty(&anchor->urb_list)) {
>  		victim = list_entry(anchor->urb_list.prev, struct urb,
>  				    anchor_list);
> +		usb_get_urb(victim);
> +		spin_unlock_irqrestore(&anchor->lock, flags);
>  		/* this will unanchor the URB */
>  		usb_unlink_urb(victim);
> +		usb_put_urb(victim);
> +		spin_lock_irqsave(&anchor->lock, flags);
>  	}
> -	spin_unlock_irq(&anchor->lock);
> +	spin_unlock_irqrestore(&anchor->lock, flags);
>  }
>  EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
>

is apparently already in the mainline.

Thanks,
Rafael


> --- linux-2.6.27-rc4/drivers/bluetooth/btusb.c.alt	2008-08-25 15:02:14.000000000 +0200
> +++ linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-25 15:44:25.000000000 +0200
> @@ -169,6 +169,7 @@ static struct usb_device_id blacklist_ta
>  struct btusb_data {
>  	struct hci_dev       *hdev;
>  	struct usb_device    *udev;
> +	struct usb_interface *acl;
>  	struct usb_interface *isoc;
>  
>  	spinlock_t lock;
> @@ -176,6 +177,7 @@ struct btusb_data {
>  	unsigned long flags;
>  
>  	struct work_struct work;
> +	struct work_struct waker;
>  
>  	struct usb_anchor tx_anchor;
>  	struct usb_anchor intr_anchor;
> @@ -189,6 +191,7 @@ struct btusb_data {
>  	struct usb_endpoint_descriptor *isoc_rx_ep;
>  
>  	int isoc_altsetting;
> +	int susp_count;
>  };
>  
>  static void btusb_intr_complete(struct urb *urb)
> @@ -227,7 +230,7 @@ static void btusb_intr_complete(struct u
>  	}
>  }
>  
> -static int btusb_submit_intr_urb(struct hci_dev *hdev)
> +static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
>  {
>  	struct btusb_data *data = hdev->driver_data;
>  	struct urb *urb;
> @@ -240,13 +243,13 @@ static int btusb_submit_intr_urb(struct
>  	if (!data->intr_ep)
>  		return -ENODEV;
>  
> -	urb = usb_alloc_urb(0, GFP_ATOMIC);
> +	urb = usb_alloc_urb(0, gfp);
>  	if (!urb)
>  		return -ENOMEM;
>  
>  	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
>  
> -	buf = kmalloc(size, GFP_ATOMIC);
> +	buf = kmalloc(size, gfp);
>  	if (!buf) {
>  		usb_free_urb(urb);
>  		return -ENOMEM;
> @@ -262,7 +265,7 @@ static int btusb_submit_intr_urb(struct
>  
>  	usb_anchor_urb(urb, &data->intr_anchor);
>  
> -	err = usb_submit_urb(urb, GFP_ATOMIC);
> +	err = usb_submit_urb(urb, gfp);
>  	if (err < 0) {
>  		BT_ERR("%s urb %p submission failed (%d)",
>  						hdev->name, urb, -err);
> @@ -311,7 +314,7 @@ static void btusb_bulk_complete(struct u
>  	}
>  }
>  
> -static int btusb_submit_bulk_urb(struct hci_dev *hdev)
> +static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
>  {
>  	struct btusb_data *data = hdev->driver_data;
>  	struct urb *urb;
> @@ -324,18 +327,19 @@ static int btusb_submit_bulk_urb(struct
>  	if (!data->bulk_rx_ep)
>  		return -ENODEV;
>  
> -	urb = usb_alloc_urb(0, GFP_KERNEL);
> +	urb = usb_alloc_urb(0, gfp);
>  	if (!urb)
>  		return -ENOMEM;
>  
>  	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
>  
> -	buf = kmalloc(size, GFP_KERNEL);
> +	buf = kmalloc(size, gfp);
>  	if (!buf) {
>  		usb_free_urb(urb);
>  		return -ENOMEM;
>  	}
>  
> +	usb_mark_last_busy(data->udev);
>  	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
>  
>  	usb_fill_bulk_urb(urb, data->udev, pipe,
> @@ -345,7 +349,7 @@ static int btusb_submit_bulk_urb(struct
>  
>  	usb_anchor_urb(urb, &data->bulk_anchor);
>  
> -	err = usb_submit_urb(urb, GFP_KERNEL);
> +	err = usb_submit_urb(urb, gfp);
>  	if (err < 0) {
>  		BT_ERR("%s urb %p submission failed (%d)",
>  						hdev->name, urb, -err);
> @@ -514,7 +518,7 @@ static int btusb_open(struct hci_dev *hd
>  	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
>  		return 0;
>  
> -	err = btusb_submit_intr_urb(hdev);
> +	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
>  	if (err < 0) {
>  		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
>  		clear_bit(HCI_RUNNING, &hdev->flags);
> @@ -523,6 +527,13 @@ static int btusb_open(struct hci_dev *hd
>  	return err;
>  }
>  
> +static void btusb_stop_traffic(struct btusb_data *data)
> +{
> +	usb_kill_anchored_urbs(&data->intr_anchor);
> +	usb_kill_anchored_urbs(&data->bulk_anchor);
> +	usb_kill_anchored_urbs(&data->isoc_anchor);
> +}
> +
>  static int btusb_close(struct hci_dev *hdev)
>  {
>  	struct btusb_data *data = hdev->driver_data;
> @@ -532,14 +543,12 @@ static int btusb_close(struct hci_dev *h
>  	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
>  		return 0;
>  
> -	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> -	usb_kill_anchored_urbs(&data->intr_anchor);
> +	flush_work(&data->work);
>  
> +	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
>  	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> -	usb_kill_anchored_urbs(&data->bulk_anchor);
> -
>  	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
> -	usb_kill_anchored_urbs(&data->intr_anchor);
> +	btusb_stop_traffic(data);
>  
>  	return 0;
>  }
> @@ -672,8 +681,19 @@ static void btusb_notify(struct hci_dev
>  
>  	BT_DBG("%s evt %d", hdev->name, evt);
>  
> -	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
> -		schedule_work(&data->work);
> +	if (hdev->conn_hash.acl_num > 0) {
> +		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
> +			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
> +				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +			else
> +				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
> +		}
> +	} else {
> +		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +		usb_unlink_anchored_urbs(&data->bulk_anchor);
> +	}
> +
> +	schedule_work(&data->work);
>  }
>  
>  static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
> @@ -724,18 +744,6 @@ static void btusb_work(struct work_struc
>  	struct btusb_data *data = container_of(work, struct btusb_data, work);
>  	struct hci_dev *hdev = data->hdev;
>  
> -	if (hdev->conn_hash.acl_num > 0) {
> -		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
> -			if (btusb_submit_bulk_urb(hdev) < 0)
> -				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> -			else
> -				btusb_submit_bulk_urb(hdev);
> -		}
> -	} else {
> -		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> -		usb_kill_anchored_urbs(&data->bulk_anchor);
> -	}
> -
>  	if (hdev->conn_hash.sco_num > 0) {
>  		if (data->isoc_altsetting != 2) {
>  			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> @@ -821,6 +829,7 @@ static int btusb_probe(struct usb_interf
>  	}
>  
>  	data->udev = interface_to_usbdev(intf);
> +	data->acl = intf;
>  
>  	spin_lock_init(&data->lock);
>  
> @@ -889,7 +898,7 @@ static int btusb_probe(struct usb_interf
>  
>  	if (data->isoc) {
>  		err = usb_driver_claim_interface(&btusb_driver,
> -							data->isoc, NULL);
> +							data->isoc, data);
>  		if (err < 0) {
>  			hci_free_dev(hdev);
>  			kfree(data);
> @@ -921,20 +930,92 @@ static void btusb_disconnect(struct usb_
>  
>  	hdev = data->hdev;
>  
> -	if (data->isoc)
> -		usb_driver_release_interface(&btusb_driver, data->isoc);
> +	/* make sure we have a reference */
> +	__hci_dev_hold(hdev);
>  
> -	usb_set_intfdata(intf, NULL);
> +	usb_set_intfdata(data->acl, NULL);
> +	if (data->isoc)
> +		usb_set_intfdata(data->isoc, NULL);
>  
> +	/* unregister before releasing any interface */
>  	hci_unregister_dev(hdev);
>  
> +	if (intf == data->isoc)
> +		usb_driver_release_interface(&btusb_driver, data->acl);
> +	else if (data->isoc)
> +		usb_driver_release_interface(&btusb_driver, data->isoc);
> +
> +	/* release the reference */
> +	__hci_dev_put(hdev);
>  	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("%s called\n", __func__);
> +
> +	if (data->susp_count++)
> +		return 0;
> +
> +	cancel_work_sync(&data->work);
> +	btusb_stop_traffic(data);
> +	usb_kill_anchored_urbs(&data->tx_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 ret;
> +
> +	if (--data->susp_count)
> +		return 0;
> +	if (test_bit(HCI_RUNNING, &hdev->flags)) {
> +		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
> +		if (ret < 0) {
> +			clear_bit(HCI_RUNNING, &hdev->flags);
> +			return ret;
> +		}
> +	}
> +
> +	if (hdev->conn_hash.acl_num > 0) {
> +		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
> +		if (ret < 0) {
> +			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +			return ret;
> +		} else {
> +			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
> +			if (ret < 0) {
> +				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +				usb_kill_anchored_urbs(&data->bulk_anchor);
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	if (data->isoc) {
> +		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
> +			ret = btusb_submit_isoc_urb(hdev);
> +			if (ret < 0)
> +				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> +			else
> +				btusb_submit_isoc_urb(hdev);
> +		}
> +	}
> +
> +	schedule_work(&data->work);
> +	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,
>  };
>  
> 
> 
> 

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26  9:36             ` Rafael J. Wysocki
  2008-08-26  9:43               ` Oliver Neukum
@ 2008-08-26  9:43               ` Oliver Neukum
  2008-08-26 10:10                 ` Rafael J. Wysocki
  2008-08-26 10:10                 ` Rafael J. Wysocki
  1 sibling, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-26  9:43 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Marcel Holtmann, linux-pm, linux-bluetooth, Pavel Machek,
	linux-usb, Stefan Seyfried

Am Dienstag 26 August 2008 11:36:04 schrieb Rafael J. Wysocki:
> [Sorry for the delayed reply.]
>=20
> On Monday, 25 of August 2008, Oliver Neukum wrote:
> > Am Montag 25 August 2008 13:54:31 schrieb Marcel Holtmann:
> > > Oliver, can you come up with a small test patch, that just kills all =
=A0
> > > URB when suspend and submits the interrupt ones at resume. Such a =A0
> > > patch might have to be merged for 2.6.27 if it fixes this problem.
> >=20
> > Rafael,
> >=20
> > this patch implemts suspend/resume for btusb and fixes a disconnect
> > problem. Does it help you?
>=20
> Yes, the patch appears to help.
>=20
> I haven't had a single crash since I applied it.  I'm going to test it a =
bit
> more today, though.

Good. Can you test what happens if you unplug the device while suspended
and hibernated?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26  9:36             ` Rafael J. Wysocki
@ 2008-08-26  9:43               ` Oliver Neukum
  2008-08-26  9:43               ` Oliver Neukum
  1 sibling, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-26  9:43 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

Am Dienstag 26 August 2008 11:36:04 schrieb Rafael J. Wysocki:
> [Sorry for the delayed reply.]
> 
> On Monday, 25 of August 2008, Oliver Neukum wrote:
> > Am Montag 25 August 2008 13:54:31 schrieb Marcel Holtmann:
> > > Oliver, can you come up with a small test patch, that just kills all  
> > > URB when suspend and submits the interrupt ones at resume. Such a  
> > > patch might have to be merged for 2.6.27 if it fixes this problem.
> > 
> > Rafael,
> > 
> > this patch implemts suspend/resume for btusb and fixes a disconnect
> > problem. Does it help you?
> 
> Yes, the patch appears to help.
> 
> I haven't had a single crash since I applied it.  I'm going to test it a bit
> more today, though.

Good. Can you test what happens if you unplug the device while suspended
and hibernated?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-25 10:43       ` Oliver Neukum
  2008-08-25 11:51         ` Marcel Holtmann
  2008-08-25 11:51         ` Marcel Holtmann
@ 2008-08-26  9:56         ` Pavel Machek
  2008-08-26 10:05           ` Pavel Machek
  2008-08-26 10:05           ` Pavel Machek
  2008-08-26  9:56         ` Pavel Machek
  3 siblings, 2 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-26  9:56 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

On Mon 2008-08-25 12:43:46, Oliver Neukum wrote:
> Am Freitag 22 August 2008 16:31:10 schrieb Marcel Holtmann:
> > > > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > > > anchor all TX URBs anyway.
> > > 
> > > The completion of an URB may happen after the autosuspend timeout passed.
> > > But we cannot use the pm counters as they are not accessible in interrupt.
> > > Hence we must maintain a counter ourselves.
> > 
> > Can we not just check the number of URBs in the anchor? I am against
> > just duplicating a counter, but then lets call it it what it is to make
> > it gets not misused. It is a purely a PM counter.
> > 
> 
> An excellent suggestion allowing major simplifications. This version works
> for me. It required an extension of the anchor API, so it obsoletes
> the split-up you've done. Therefore the whole thing comes as a big patch
> against rc4, easy to test.

I could not get it to apply over -rc4-git, usb seems to be changing
under us :-(. After manual fixup (conflict was in comment), I still
get

  LD      .tmp_vmlinux1
drivers/built-in.o: In function `btusb_suspend':
btusb.c:(.text+0x20a578): undefined reference to `usb_anchor_empty'
drivers/built-in.o: In function `btusb_resume':
btusb.c:(.text+0x20ade6): undefined reference to `usb_get_from_anchor'
btusb.c:(.text+0x20ae01): undefined reference to
`usb_scuttle_anchored_urbs'
make: *** [.tmp_vmlinux1] Error 1
40.09user 7.55system 47.97 (0m47.971s) elapsed 99.34%CPU
pavel@amd:/data/l/linux-good$

...probably the "already applied" patch it complained about was not so
applied after all.

...ok, I got it to apply, compile, resulting patch is attached.
									Pavel

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 6a01068..62a9a5b 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -35,13 +35,13 @@ #include <linux/usb.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-//#define CONFIG_BT_HCIBTUSB_DEBUG
+#define CONFIG_BT_HCIBTUSB_DEBUG
 #ifndef CONFIG_BT_HCIBTUSB_DEBUG
 #undef  BT_DBG
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "0.3"
+#define VERSION "0.4"
 
 static int ignore_dga;
 static int ignore_csr;
@@ -165,10 +165,12 @@ #define BTUSB_MAX_ISOC_FRAMES	10
 #define BTUSB_INTR_RUNNING	0
 #define BTUSB_BULK_RUNNING	1
 #define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
 
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,11 +178,13 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
 	struct usb_anchor bulk_anchor;
 	struct usb_anchor isoc_anchor;
+	struct usb_anchor deferred;
 
 	struct usb_endpoint_descriptor *intr_ep;
 	struct usb_endpoint_descriptor *bulk_tx_ep;
@@ -189,6 +193,8 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int did_iso_resume:1;
+	int susp_count;
 };
 
 static void btusb_intr_complete(struct urb *urb)
@@ -217,6 +223,7 @@ static void btusb_intr_complete(struct u
 	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->intr_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -227,7 +234,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +247,13 @@ static int btusb_submit_intr_urb(struct 
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +269,7 @@ static int btusb_submit_intr_urb(struct 
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -301,6 +308,7 @@ static void btusb_bulk_complete(struct u
 	if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -311,7 +319,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +332,19 @@ static int btusb_submit_bulk_urb(struct 
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +354,7 @@ static int btusb_submit_bulk_urb(struct 
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -508,13 +517,19 @@ static int btusb_open(struct hci_dev *hd
 
 	BT_DBG("%s", hdev->name);
 
+	err = usb_autopm_get_interface(data->acl);
+	if (err < 0)
+		return err;
+	data->acl->needs_remote_wakeup = 1;
+	usb_autopm_put_interface(data->acl);
+
 	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_ATOMIC);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,24 +538,34 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
+	int err;
 
 	BT_DBG("%s", hdev->name);
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
-
+	btusb_stop_traffic(data);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err) {
+		data->acl->needs_remote_wakeup = 0;
+		usb_autopm_put_interface(data->acl);
+	}
 	return 0;
 }
 
@@ -562,7 +587,7 @@ static int btusb_send_frame(struct sk_bu
 	struct usb_ctrlrequest *dr;
 	struct urb *urb;
 	unsigned int pipe;
-	int err;
+	int err, susp;
 
 	BT_DBG("%s", hdev->name);
 
@@ -571,6 +596,7 @@ static int btusb_send_frame(struct sk_bu
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
+		BT_DBG("HCI_COMMAND_PKT");
 		urb = usb_alloc_urb(0, GFP_ATOMIC);
 		if (!urb)
 			return -ENOMEM;
@@ -596,6 +622,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_ACLDATA_PKT:
+		BT_DBG("HCI_ACLDATA_PKT");
 		if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
 			return -ENODEV;
 
@@ -613,6 +640,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_SCODATA_PKT:
+		BT_DBG("HCI_SCODATA_PKT");
 		if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
 			return -ENODEV;
 
@@ -643,17 +671,22 @@ static int btusb_send_frame(struct sk_bu
 		return -EILSEQ;
 	}
 
+	spin_lock(&data->lock);
+	susp = test_bit(BTUSB_SUSPENDING, &data->flags);
 	usb_anchor_urb(urb, &data->tx_anchor);
-
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err < 0) {
-		BT_ERR("%s urb %p submission failed", hdev->name, urb);
-		kfree(urb->setup_packet);
-		usb_unanchor_urb(urb);
+	if (susp) {
+		schedule_work(&data->waker);
+		err = 0;
+	} else {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0) {
+			BT_ERR("%s urb %p submission failed", hdev->name, urb);
+			kfree(urb->setup_packet);
+			usb_unanchor_urb(urb);
+		}
+		usb_free_urb(urb);
 	}
-
-	usb_free_urb(urb);
-
+	spin_unlock(&data->lock);
 	return err;
 }
 
@@ -672,8 +705,19 @@ static void btusb_notify(struct hci_dev 
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -723,20 +767,19 @@ static void btusb_work(struct work_struc
 {
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
-
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
+	int err;
 
 	if (hdev->conn_hash.sco_num > 0) {
+		if (!data->did_iso_resume) {
+			err = usb_autopm_get_interface(data->isoc);
+			if (!err) {
+				data->did_iso_resume = 1;
+			} else {
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->isoc_anchor);
+				return;
+			}
+		}
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			usb_kill_anchored_urbs(&data->isoc_anchor);
@@ -756,9 +799,26 @@ static void btusb_work(struct work_struc
 		usb_kill_anchored_urbs(&data->isoc_anchor);
 
 		__set_isoc_interface(hdev, 0);
+		if (data->did_iso_resume) {
+			data->did_iso_resume = 0;
+			usb_autopm_put_interface(data->isoc);
+		}
 	}
 }
 
+static void btusb_waker(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, waker);
+	int err;
+
+	BUG_ON(data == NULL);
+	BT_DBG("about to resume");
+	BUG_ON(data->acl == NULL);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err)
+		usb_autopm_put_interface(data->acl);
+}
+
 static int btusb_probe(struct usb_interface *intf,
 				const struct usb_device_id *id)
 {
@@ -821,15 +881,18 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
 	INIT_WORK(&data->work, btusb_work);
+	INIT_WORK(&data->waker, btusb_waker);
 
 	init_usb_anchor(&data->tx_anchor);
 	init_usb_anchor(&data->intr_anchor);
 	init_usb_anchor(&data->bulk_anchor);
 	init_usb_anchor(&data->isoc_anchor);
+	init_usb_anchor(&data->deferred);
 
 	hdev = hci_alloc_dev();
 	if (!hdev) {
@@ -889,7 +952,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,21 +984,128 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	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("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+	spin_lock_irq(&data->lock);
+	if (	interface_to_usbdev(intf)->auto_pm &&
+		!usb_anchor_empty(&data->tx_anchor)) {
+		spin_unlock_irq(&data->lock);
+		return -EBUSY;
+	}
+
+	set_bit(BTUSB_SUSPENDING, &data->flags);
+	spin_unlock_irq(&data->lock);
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_anchor);
+	return 0;
+}
+
+static int play_deferred(struct btusb_data *data)
+{
+	struct urb *urb;
+	int err = 0;
+
+	while ((urb = usb_get_from_anchor(&data->tx_anchor))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0)
+			break;
+	}
+	usb_scuttle_anchored_urbs(&data->tx_anchor);
+	return err;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+	struct hci_dev *hdev = data->hdev;
+	int ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+
+		spin_lock_irq(&data->lock);
+		ret = play_deferred(data);
+		clear_bit(BTUSB_SUSPENDING, &data->flags);
+		spin_unlock_irq(&data->lock);
+
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
+	.supports_autosuspend = 1,
 };
 
 static int __init btusb_init(void)
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 47111e8..0ceb312 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -633,3 +633,61 @@ int usb_wait_anchor_empty_timeout(struct
 				  msecs_to_jiffies(timeout));
 }
 EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
+struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	if (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.next, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		usb_unanchor_urb(victim);
+	} else {
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		victim = NULL;
+	}
+
+	return victim;
+}
+
+EXPORT_SYMBOL_GPL(usb_get_from_anchor);
+
+void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		/* this will unanchor the URB */
+		usb_unanchor_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
+	}
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);
+
+int usb_anchor_empty(struct usb_anchor *anchor)
+{
+	unsigned long flags;
+	int rv;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	rv = list_empty(&anchor->urb_list);
+	spin_unlock_irqrestore(&anchor->lock, flags);
+
+	return rv;
+}
+
+EXPORT_SYMBOL_GPL(usb_anchor_empty);
+
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 94ac74a..4f09a57 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -112,8 +112,6 @@ enum usb_interface_condition {
  *	capability during autosuspend.
  * @needs_altsetting0: flag set when a set-interface request for altsetting 0
  *	has been deferred.
- * @needs_binding: flag set when the driver should be re-probed or unbound
- *	following a reset or suspend operation it doesn't support.
  * @dev: driver model's view of this device
  * @usb_dev: if an interface is bound to the USB major, this will point
  *	to the sysfs representation for that device.
@@ -1465,6 +1463,9 @@ extern void usb_anchor_urb(struct urb *u
 extern void usb_unanchor_urb(struct urb *urb);
 extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
 					 unsigned int timeout);
+extern struct urb *usb_get_from_anchor(struct usb_anchor *anchor);
+extern void usb_scuttle_anchored_urbs(struct usb_anchor *anchor);
+extern int usb_anchor_empty(struct usb_anchor *anchor);
 
 /**
  * usb_urb_dir_in - check if an URB describes an IN transfer

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply related	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-25 10:43       ` Oliver Neukum
                           ` (2 preceding siblings ...)
  2008-08-26  9:56         ` Pavel Machek
@ 2008-08-26  9:56         ` Pavel Machek
  3 siblings, 0 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-26  9:56 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-bluetooth, Marcel Holtmann, linux-usb, Stefan Seyfried, linux-pm

On Mon 2008-08-25 12:43:46, Oliver Neukum wrote:
> Am Freitag 22 August 2008 16:31:10 schrieb Marcel Holtmann:
> > > > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > > > anchor all TX URBs anyway.
> > > 
> > > The completion of an URB may happen after the autosuspend timeout passed.
> > > But we cannot use the pm counters as they are not accessible in interrupt.
> > > Hence we must maintain a counter ourselves.
> > 
> > Can we not just check the number of URBs in the anchor? I am against
> > just duplicating a counter, but then lets call it it what it is to make
> > it gets not misused. It is a purely a PM counter.
> > 
> 
> An excellent suggestion allowing major simplifications. This version works
> for me. It required an extension of the anchor API, so it obsoletes
> the split-up you've done. Therefore the whole thing comes as a big patch
> against rc4, easy to test.

I could not get it to apply over -rc4-git, usb seems to be changing
under us :-(. After manual fixup (conflict was in comment), I still
get

  LD      .tmp_vmlinux1
drivers/built-in.o: In function `btusb_suspend':
btusb.c:(.text+0x20a578): undefined reference to `usb_anchor_empty'
drivers/built-in.o: In function `btusb_resume':
btusb.c:(.text+0x20ade6): undefined reference to `usb_get_from_anchor'
btusb.c:(.text+0x20ae01): undefined reference to
`usb_scuttle_anchored_urbs'
make: *** [.tmp_vmlinux1] Error 1
40.09user 7.55system 47.97 (0m47.971s) elapsed 99.34%CPU
pavel@amd:/data/l/linux-good$

...probably the "already applied" patch it complained about was not so
applied after all.

...ok, I got it to apply, compile, resulting patch is attached.
									Pavel

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 6a01068..62a9a5b 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -35,13 +35,13 @@ #include <linux/usb.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-//#define CONFIG_BT_HCIBTUSB_DEBUG
+#define CONFIG_BT_HCIBTUSB_DEBUG
 #ifndef CONFIG_BT_HCIBTUSB_DEBUG
 #undef  BT_DBG
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "0.3"
+#define VERSION "0.4"
 
 static int ignore_dga;
 static int ignore_csr;
@@ -165,10 +165,12 @@ #define BTUSB_MAX_ISOC_FRAMES	10
 #define BTUSB_INTR_RUNNING	0
 #define BTUSB_BULK_RUNNING	1
 #define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
 
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,11 +178,13 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
 	struct usb_anchor bulk_anchor;
 	struct usb_anchor isoc_anchor;
+	struct usb_anchor deferred;
 
 	struct usb_endpoint_descriptor *intr_ep;
 	struct usb_endpoint_descriptor *bulk_tx_ep;
@@ -189,6 +193,8 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int did_iso_resume:1;
+	int susp_count;
 };
 
 static void btusb_intr_complete(struct urb *urb)
@@ -217,6 +223,7 @@ static void btusb_intr_complete(struct u
 	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->intr_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -227,7 +234,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +247,13 @@ static int btusb_submit_intr_urb(struct 
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +269,7 @@ static int btusb_submit_intr_urb(struct 
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -301,6 +308,7 @@ static void btusb_bulk_complete(struct u
 	if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -311,7 +319,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +332,19 @@ static int btusb_submit_bulk_urb(struct 
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +354,7 @@ static int btusb_submit_bulk_urb(struct 
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -508,13 +517,19 @@ static int btusb_open(struct hci_dev *hd
 
 	BT_DBG("%s", hdev->name);
 
+	err = usb_autopm_get_interface(data->acl);
+	if (err < 0)
+		return err;
+	data->acl->needs_remote_wakeup = 1;
+	usb_autopm_put_interface(data->acl);
+
 	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_ATOMIC);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,24 +538,34 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
+	int err;
 
 	BT_DBG("%s", hdev->name);
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
-
+	btusb_stop_traffic(data);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err) {
+		data->acl->needs_remote_wakeup = 0;
+		usb_autopm_put_interface(data->acl);
+	}
 	return 0;
 }
 
@@ -562,7 +587,7 @@ static int btusb_send_frame(struct sk_bu
 	struct usb_ctrlrequest *dr;
 	struct urb *urb;
 	unsigned int pipe;
-	int err;
+	int err, susp;
 
 	BT_DBG("%s", hdev->name);
 
@@ -571,6 +596,7 @@ static int btusb_send_frame(struct sk_bu
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
+		BT_DBG("HCI_COMMAND_PKT");
 		urb = usb_alloc_urb(0, GFP_ATOMIC);
 		if (!urb)
 			return -ENOMEM;
@@ -596,6 +622,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_ACLDATA_PKT:
+		BT_DBG("HCI_ACLDATA_PKT");
 		if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
 			return -ENODEV;
 
@@ -613,6 +640,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_SCODATA_PKT:
+		BT_DBG("HCI_SCODATA_PKT");
 		if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
 			return -ENODEV;
 
@@ -643,17 +671,22 @@ static int btusb_send_frame(struct sk_bu
 		return -EILSEQ;
 	}
 
+	spin_lock(&data->lock);
+	susp = test_bit(BTUSB_SUSPENDING, &data->flags);
 	usb_anchor_urb(urb, &data->tx_anchor);
-
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err < 0) {
-		BT_ERR("%s urb %p submission failed", hdev->name, urb);
-		kfree(urb->setup_packet);
-		usb_unanchor_urb(urb);
+	if (susp) {
+		schedule_work(&data->waker);
+		err = 0;
+	} else {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0) {
+			BT_ERR("%s urb %p submission failed", hdev->name, urb);
+			kfree(urb->setup_packet);
+			usb_unanchor_urb(urb);
+		}
+		usb_free_urb(urb);
 	}
-
-	usb_free_urb(urb);
-
+	spin_unlock(&data->lock);
 	return err;
 }
 
@@ -672,8 +705,19 @@ static void btusb_notify(struct hci_dev 
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -723,20 +767,19 @@ static void btusb_work(struct work_struc
 {
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
-
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
+	int err;
 
 	if (hdev->conn_hash.sco_num > 0) {
+		if (!data->did_iso_resume) {
+			err = usb_autopm_get_interface(data->isoc);
+			if (!err) {
+				data->did_iso_resume = 1;
+			} else {
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->isoc_anchor);
+				return;
+			}
+		}
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			usb_kill_anchored_urbs(&data->isoc_anchor);
@@ -756,9 +799,26 @@ static void btusb_work(struct work_struc
 		usb_kill_anchored_urbs(&data->isoc_anchor);
 
 		__set_isoc_interface(hdev, 0);
+		if (data->did_iso_resume) {
+			data->did_iso_resume = 0;
+			usb_autopm_put_interface(data->isoc);
+		}
 	}
 }
 
+static void btusb_waker(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, waker);
+	int err;
+
+	BUG_ON(data == NULL);
+	BT_DBG("about to resume");
+	BUG_ON(data->acl == NULL);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err)
+		usb_autopm_put_interface(data->acl);
+}
+
 static int btusb_probe(struct usb_interface *intf,
 				const struct usb_device_id *id)
 {
@@ -821,15 +881,18 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
 	INIT_WORK(&data->work, btusb_work);
+	INIT_WORK(&data->waker, btusb_waker);
 
 	init_usb_anchor(&data->tx_anchor);
 	init_usb_anchor(&data->intr_anchor);
 	init_usb_anchor(&data->bulk_anchor);
 	init_usb_anchor(&data->isoc_anchor);
+	init_usb_anchor(&data->deferred);
 
 	hdev = hci_alloc_dev();
 	if (!hdev) {
@@ -889,7 +952,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,21 +984,128 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	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("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+	spin_lock_irq(&data->lock);
+	if (	interface_to_usbdev(intf)->auto_pm &&
+		!usb_anchor_empty(&data->tx_anchor)) {
+		spin_unlock_irq(&data->lock);
+		return -EBUSY;
+	}
+
+	set_bit(BTUSB_SUSPENDING, &data->flags);
+	spin_unlock_irq(&data->lock);
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_anchor);
+	return 0;
+}
+
+static int play_deferred(struct btusb_data *data)
+{
+	struct urb *urb;
+	int err = 0;
+
+	while ((urb = usb_get_from_anchor(&data->tx_anchor))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0)
+			break;
+	}
+	usb_scuttle_anchored_urbs(&data->tx_anchor);
+	return err;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+	struct hci_dev *hdev = data->hdev;
+	int ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+
+		spin_lock_irq(&data->lock);
+		ret = play_deferred(data);
+		clear_bit(BTUSB_SUSPENDING, &data->flags);
+		spin_unlock_irq(&data->lock);
+
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
+	.supports_autosuspend = 1,
 };
 
 static int __init btusb_init(void)
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 47111e8..0ceb312 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -633,3 +633,61 @@ int usb_wait_anchor_empty_timeout(struct
 				  msecs_to_jiffies(timeout));
 }
 EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
+struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	if (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.next, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		usb_unanchor_urb(victim);
+	} else {
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		victim = NULL;
+	}
+
+	return victim;
+}
+
+EXPORT_SYMBOL_GPL(usb_get_from_anchor);
+
+void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		/* this will unanchor the URB */
+		usb_unanchor_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
+	}
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);
+
+int usb_anchor_empty(struct usb_anchor *anchor)
+{
+	unsigned long flags;
+	int rv;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	rv = list_empty(&anchor->urb_list);
+	spin_unlock_irqrestore(&anchor->lock, flags);
+
+	return rv;
+}
+
+EXPORT_SYMBOL_GPL(usb_anchor_empty);
+
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 94ac74a..4f09a57 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -112,8 +112,6 @@ enum usb_interface_condition {
  *	capability during autosuspend.
  * @needs_altsetting0: flag set when a set-interface request for altsetting 0
  *	has been deferred.
- * @needs_binding: flag set when the driver should be re-probed or unbound
- *	following a reset or suspend operation it doesn't support.
  * @dev: driver model's view of this device
  * @usb_dev: if an interface is bound to the USB major, this will point
  *	to the sysfs representation for that device.
@@ -1465,6 +1463,9 @@ extern void usb_anchor_urb(struct urb *u
 extern void usb_unanchor_urb(struct urb *urb);
 extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
 					 unsigned int timeout);
+extern struct urb *usb_get_from_anchor(struct usb_anchor *anchor);
+extern void usb_scuttle_anchored_urbs(struct usb_anchor *anchor);
+extern int usb_anchor_empty(struct usb_anchor *anchor);
 
 /**
  * usb_urb_dir_in - check if an URB describes an IN transfer

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply related	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-26  9:56         ` Pavel Machek
  2008-08-26 10:05           ` Pavel Machek
@ 2008-08-26 10:05           ` Pavel Machek
  2008-08-26 11:02             ` Oliver Neukum
  2008-08-26 11:02             ` Oliver Neukum
  1 sibling, 2 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-26 10:05 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

On Tue 2008-08-26 11:56:47, Pavel Machek wrote:
> On Mon 2008-08-25 12:43:46, Oliver Neukum wrote:
> > Am Freitag 22 August 2008 16:31:10 schrieb Marcel Holtmann:
> > > > > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > > > > anchor all TX URBs anyway.
> > > > 
> > > > The completion of an URB may happen after the autosuspend timeout passed.
> > > > But we cannot use the pm counters as they are not accessible in interrupt.
> > > > Hence we must maintain a counter ourselves.
> > > 
> > > Can we not just check the number of URBs in the anchor? I am against
> > > just duplicating a counter, but then lets call it it what it is to make
> > > it gets not misused. It is a purely a PM counter.
> > > 
> > 
> > An excellent suggestion allowing major simplifications. This version works
> > for me. It required an extension of the anchor API, so it obsoletes
> > the split-up you've done. Therefore the whole thing comes as a big patch
> > against rc4, easy to test.
> 
> I could not get it to apply over -rc4-git, usb seems to be changing
> under us :-(. After manual fixup (conflict was in comment), I still
> get
> 
>   LD      .tmp_vmlinux1
> drivers/built-in.o: In function `btusb_suspend':
> btusb.c:(.text+0x20a578): undefined reference to `usb_anchor_empty'
> drivers/built-in.o: In function `btusb_resume':
> btusb.c:(.text+0x20ade6): undefined reference to `usb_get_from_anchor'
> btusb.c:(.text+0x20ae01): undefined reference to
> `usb_scuttle_anchored_urbs'
> make: *** [.tmp_vmlinux1] Error 1
> 40.09user 7.55system 47.97 (0m47.971s) elapsed 99.34%CPU
> pavel@amd:/data/l/linux-good$
> 
> ...probably the "already applied" patch it complained about was not so
> applied after all.
> 
> ...ok, I got it to apply, compile, resulting patch is attached.


And it worked for me, but suspend died. I'll verify if that's a
problem in vanilla -rc4-git, too.
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-26  9:56         ` Pavel Machek
@ 2008-08-26 10:05           ` Pavel Machek
  2008-08-26 10:05           ` Pavel Machek
  1 sibling, 0 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-26 10:05 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-bluetooth, Marcel Holtmann, linux-usb, Stefan Seyfried, linux-pm

On Tue 2008-08-26 11:56:47, Pavel Machek wrote:
> On Mon 2008-08-25 12:43:46, Oliver Neukum wrote:
> > Am Freitag 22 August 2008 16:31:10 schrieb Marcel Holtmann:
> > > > > Please explain the tx_in_flight stuff to me. It looks unneeded since we
> > > > > anchor all TX URBs anyway.
> > > > 
> > > > The completion of an URB may happen after the autosuspend timeout passed.
> > > > But we cannot use the pm counters as they are not accessible in interrupt.
> > > > Hence we must maintain a counter ourselves.
> > > 
> > > Can we not just check the number of URBs in the anchor? I am against
> > > just duplicating a counter, but then lets call it it what it is to make
> > > it gets not misused. It is a purely a PM counter.
> > > 
> > 
> > An excellent suggestion allowing major simplifications. This version works
> > for me. It required an extension of the anchor API, so it obsoletes
> > the split-up you've done. Therefore the whole thing comes as a big patch
> > against rc4, easy to test.
> 
> I could not get it to apply over -rc4-git, usb seems to be changing
> under us :-(. After manual fixup (conflict was in comment), I still
> get
> 
>   LD      .tmp_vmlinux1
> drivers/built-in.o: In function `btusb_suspend':
> btusb.c:(.text+0x20a578): undefined reference to `usb_anchor_empty'
> drivers/built-in.o: In function `btusb_resume':
> btusb.c:(.text+0x20ade6): undefined reference to `usb_get_from_anchor'
> btusb.c:(.text+0x20ae01): undefined reference to
> `usb_scuttle_anchored_urbs'
> make: *** [.tmp_vmlinux1] Error 1
> 40.09user 7.55system 47.97 (0m47.971s) elapsed 99.34%CPU
> pavel@amd:/data/l/linux-good$
> 
> ...probably the "already applied" patch it complained about was not so
> applied after all.
> 
> ...ok, I got it to apply, compile, resulting patch is attached.


And it worked for me, but suspend died. I'll verify if that's a
problem in vanilla -rc4-git, too.
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26  9:43               ` Oliver Neukum
  2008-08-26 10:10                 ` Rafael J. Wysocki
@ 2008-08-26 10:10                 ` Rafael J. Wysocki
  2008-08-26 11:41                   ` Stefan Seyfried
  2008-08-26 11:41                   ` Stefan Seyfried
  1 sibling, 2 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-26 10:10 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, linux-pm, linux-bluetooth, Pavel Machek,
	linux-usb, Stefan Seyfried

On Tuesday, 26 of August 2008, Oliver Neukum wrote:
> Am Dienstag 26 August 2008 11:36:04 schrieb Rafael J. Wysocki:
> > [Sorry for the delayed reply.]
> >=20
> > On Monday, 25 of August 2008, Oliver Neukum wrote:
> > > Am Montag 25 August 2008 13:54:31 schrieb Marcel Holtmann:
> > > > Oliver, can you come up with a small test patch, that just kills al=
l =A0
> > > > URB when suspend and submits the interrupt ones at resume. Such a =
=A0
> > > > patch might have to be merged for 2.6.27 if it fixes this problem.
> > >=20
> > > Rafael,
> > >=20
> > > this patch implemts suspend/resume for btusb and fixes a disconnect
> > > problem. Does it help you?
> >=20
> > Yes, the patch appears to help.
> >=20
> > I haven't had a single crash since I applied it.  I'm going to test it =
a bit
> > more today, though.
>=20
> Good. Can you test what happens if you unplug the device while suspended
> and hibernated?

It's built-in, I can't unplug it. :-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26  9:43               ` Oliver Neukum
@ 2008-08-26 10:10                 ` Rafael J. Wysocki
  2008-08-26 10:10                 ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-26 10:10 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

On Tuesday, 26 of August 2008, Oliver Neukum wrote:
> Am Dienstag 26 August 2008 11:36:04 schrieb Rafael J. Wysocki:
> > [Sorry for the delayed reply.]
> > 
> > On Monday, 25 of August 2008, Oliver Neukum wrote:
> > > Am Montag 25 August 2008 13:54:31 schrieb Marcel Holtmann:
> > > > Oliver, can you come up with a small test patch, that just kills all  
> > > > URB when suspend and submits the interrupt ones at resume. Such a  
> > > > patch might have to be merged for 2.6.27 if it fixes this problem.
> > > 
> > > Rafael,
> > > 
> > > this patch implemts suspend/resume for btusb and fixes a disconnect
> > > problem. Does it help you?
> > 
> > Yes, the patch appears to help.
> > 
> > I haven't had a single crash since I applied it.  I'm going to test it a bit
> > more today, though.
> 
> Good. Can you test what happens if you unplug the device while suspended
> and hibernated?

It's built-in, I can't unplug it. :-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-26 10:05           ` Pavel Machek
@ 2008-08-26 11:02             ` Oliver Neukum
  2008-08-28  8:06               ` Pavel Machek
  2008-08-28  8:06               ` Pavel Machek
  2008-08-26 11:02             ` Oliver Neukum
  1 sibling, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-26 11:02 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Marcel Holtmann, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

Am Dienstag 26 August 2008 12:05:19 schrieb Pavel Machek:

Hi,

> And it worked for me, but suspend died. I'll verify if that's a
> problem in vanilla -rc4-git, too.

what exactly does "died" mean?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-26 10:05           ` Pavel Machek
  2008-08-26 11:02             ` Oliver Neukum
@ 2008-08-26 11:02             ` Oliver Neukum
  1 sibling, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-26 11:02 UTC (permalink / raw)
  To: Pavel Machek
  Cc: linux-bluetooth, Marcel Holtmann, linux-usb, Stefan Seyfried, linux-pm

Am Dienstag 26 August 2008 12:05:19 schrieb Pavel Machek:

Hi,

> And it worked for me, but suspend died. I'll verify if that's a
> problem in vanilla -rc4-git, too.

what exactly does "died" mean?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 10:10                 ` Rafael J. Wysocki
@ 2008-08-26 11:41                   ` Stefan Seyfried
  2008-08-26 18:44                     ` Rafael J. Wysocki
  2008-08-26 18:44                     ` Rafael J. Wysocki
  2008-08-26 11:41                   ` Stefan Seyfried
  1 sibling, 2 replies; 73+ messages in thread
From: Stefan Seyfried @ 2008-08-26 11:41 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Oliver Neukum, Marcel Holtmann, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

On Tue, Aug 26, 2008 at 12:10:08PM +0200, Rafael J. Wysocki wrote:
> > Good. Can you test what happens if you unplug the device while suspended
> > and hibernated?
> 
> It's built-in, I can't unplug it. :-)

Maybe you can disable it in the BIOS, but this might change the DSDT / other
system configuration, so it might break resume in other ways :-(
-- 
Stefan Seyfried
R&D Team Mobile Devices            |              "Any ideas, John?"
SUSE LINUX Products GmbH, Nürnberg | "Well, surrounding them's out." 

This footer brought to you by insane German lawmakers:
SUSE Linux Products GmbH, GF: Markus Rex, HRB 16746 (AG Nürnberg)

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 10:10                 ` Rafael J. Wysocki
  2008-08-26 11:41                   ` Stefan Seyfried
@ 2008-08-26 11:41                   ` Stefan Seyfried
  1 sibling, 0 replies; 73+ messages in thread
From: Stefan Seyfried @ 2008-08-26 11:41 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-usb, Marcel Holtmann, linux-bluetooth, Pavel Machek, linux-pm

On Tue, Aug 26, 2008 at 12:10:08PM +0200, Rafael J. Wysocki wrote:
> > Good. Can you test what happens if you unplug the device while suspended
> > and hibernated?
> 
> It's built-in, I can't unplug it. :-)

Maybe you can disable it in the BIOS, but this might change the DSDT / other
system configuration, so it might break resume in other ways :-(
-- 
Stefan Seyfried
R&D Team Mobile Devices            |              "Any ideas, John?"
SUSE LINUX Products GmbH, Nürnberg | "Well, surrounding them's out." 

This footer brought to you by insane German lawmakers:
SUSE Linux Products GmbH, GF: Markus Rex, HRB 16746 (AG Nürnberg)

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 11:41                   ` Stefan Seyfried
  2008-08-26 18:44                     ` Rafael J. Wysocki
@ 2008-08-26 18:44                     ` Rafael J. Wysocki
  2008-08-26 19:53                       ` Oliver Neukum
  2008-08-26 19:53                       ` Oliver Neukum
  1 sibling, 2 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-26 18:44 UTC (permalink / raw)
  To: Stefan Seyfried
  Cc: Oliver Neukum, Marcel Holtmann, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

On Tuesday, 26 of August 2008, Stefan Seyfried wrote:
> On Tue, Aug 26, 2008 at 12:10:08PM +0200, Rafael J. Wysocki wrote:
> > > Good. Can you test what happens if you unplug the device while suspended
> > > and hibernated?
> > 
> > It's built-in, I can't unplug it. :-)
> 
> Maybe you can disable it in the BIOS, but this might change the DSDT / other
> system configuration, so it might break resume in other ways :-(

There is a switch that's supposed to disable the radio (rfkill or something).
I used it to switch the radio off while the box was waking up from hibernation
and kbluetooth didn't find the adapter after the resume.  After I've pressed
the "radio off" button again, the bluetooth appears to be functional again.

However, this "radio off" button is shared between bluetooth and wireless
(b43) and there are some surprising interactions.  Nothing seems to be broken,
though.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 11:41                   ` Stefan Seyfried
@ 2008-08-26 18:44                     ` Rafael J. Wysocki
  2008-08-26 18:44                     ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-26 18:44 UTC (permalink / raw)
  To: Stefan Seyfried
  Cc: linux-usb, Marcel Holtmann, linux-bluetooth, Pavel Machek, linux-pm

On Tuesday, 26 of August 2008, Stefan Seyfried wrote:
> On Tue, Aug 26, 2008 at 12:10:08PM +0200, Rafael J. Wysocki wrote:
> > > Good. Can you test what happens if you unplug the device while suspended
> > > and hibernated?
> > 
> > It's built-in, I can't unplug it. :-)
> 
> Maybe you can disable it in the BIOS, but this might change the DSDT / other
> system configuration, so it might break resume in other ways :-(

There is a switch that's supposed to disable the radio (rfkill or something).
I used it to switch the radio off while the box was waking up from hibernation
and kbluetooth didn't find the adapter after the resume.  After I've pressed
the "radio off" button again, the bluetooth appears to be functional again.

However, this "radio off" button is shared between bluetooth and wireless
(b43) and there are some surprising interactions.  Nothing seems to be broken,
though.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 18:44                     ` Rafael J. Wysocki
@ 2008-08-26 19:53                       ` Oliver Neukum
  2008-08-26 23:28                         ` Rafael J. Wysocki
  2008-08-26 23:28                         ` Rafael J. Wysocki
  2008-08-26 19:53                       ` Oliver Neukum
  1 sibling, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-26 19:53 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Stefan Seyfried, Marcel Holtmann, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

Am Dienstag 26 August 2008 20:44:53 schrieb Rafael J. Wysocki:
> On Tuesday, 26 of August 2008, Stefan Seyfried wrote:
> > On Tue, Aug 26, 2008 at 12:10:08PM +0200, Rafael J. Wysocki wrote:
> > > > Good. Can you test what happens if you unplug the device while suspended
> > > > and hibernated?
> > > 
> > > It's built-in, I can't unplug it. :-)
> > 
> > Maybe you can disable it in the BIOS, but this might change the DSDT / other
> > system configuration, so it might break resume in other ways :-(
> 
> There is a switch that's supposed to disable the radio (rfkill or something).
> I used it to switch the radio off while the box was waking up from hibernation
> and kbluetooth didn't find the adapter after the resume.  After I've pressed
> the "radio off" button again, the bluetooth appears to be functional again.
> 
> However, this "radio off" button is shared between bluetooth and wireless
> (b43) and there are some surprising interactions.  Nothing seems to be broken,
> though.

This doesn't explain the original failure. Can you comment out the support
for suspend/resume in the driver and try again? The patch also fixes a
race in disconnect that you may be hitting.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 18:44                     ` Rafael J. Wysocki
  2008-08-26 19:53                       ` Oliver Neukum
@ 2008-08-26 19:53                       ` Oliver Neukum
  1 sibling, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-26 19:53 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

Am Dienstag 26 August 2008 20:44:53 schrieb Rafael J. Wysocki:
> On Tuesday, 26 of August 2008, Stefan Seyfried wrote:
> > On Tue, Aug 26, 2008 at 12:10:08PM +0200, Rafael J. Wysocki wrote:
> > > > Good. Can you test what happens if you unplug the device while suspended
> > > > and hibernated?
> > > 
> > > It's built-in, I can't unplug it. :-)
> > 
> > Maybe you can disable it in the BIOS, but this might change the DSDT / other
> > system configuration, so it might break resume in other ways :-(
> 
> There is a switch that's supposed to disable the radio (rfkill or something).
> I used it to switch the radio off while the box was waking up from hibernation
> and kbluetooth didn't find the adapter after the resume.  After I've pressed
> the "radio off" button again, the bluetooth appears to be functional again.
> 
> However, this "radio off" button is shared between bluetooth and wireless
> (b43) and there are some surprising interactions.  Nothing seems to be broken,
> though.

This doesn't explain the original failure. Can you comment out the support
for suspend/resume in the driver and try again? The patch also fixes a
race in disconnect that you may be hitting.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 19:53                       ` Oliver Neukum
  2008-08-26 23:28                         ` Rafael J. Wysocki
@ 2008-08-26 23:28                         ` Rafael J. Wysocki
  2008-08-27  5:22                           ` Marcel Holtmann
                                             ` (4 more replies)
  1 sibling, 5 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-26 23:28 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Stefan Seyfried, Marcel Holtmann, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

On Tuesday, 26 of August 2008, Oliver Neukum wrote:
> Am Dienstag 26 August 2008 20:44:53 schrieb Rafael J. Wysocki:
> > On Tuesday, 26 of August 2008, Stefan Seyfried wrote:
> > > On Tue, Aug 26, 2008 at 12:10:08PM +0200, Rafael J. Wysocki wrote:
> > > > > Good. Can you test what happens if you unplug the device while suspended
> > > > > and hibernated?
> > > > 
> > > > It's built-in, I can't unplug it. :-)
> > > 
> > > Maybe you can disable it in the BIOS, but this might change the DSDT / other
> > > system configuration, so it might break resume in other ways :-(
> > 
> > There is a switch that's supposed to disable the radio (rfkill or something).
> > I used it to switch the radio off while the box was waking up from hibernation
> > and kbluetooth didn't find the adapter after the resume.  After I've pressed
> > the "radio off" button again, the bluetooth appears to be functional again.
> > 
> > However, this "radio off" button is shared between bluetooth and wireless
> > (b43) and there are some surprising interactions.  Nothing seems to be broken,
> > though.
> 
> This doesn't explain the original failure. Can you comment out the support
> for suspend/resume in the driver and try again?

With that commented out, I'm able to reproduce the failure.  With the original
patch, I'm not.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 19:53                       ` Oliver Neukum
@ 2008-08-26 23:28                         ` Rafael J. Wysocki
  2008-08-26 23:28                         ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-26 23:28 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

On Tuesday, 26 of August 2008, Oliver Neukum wrote:
> Am Dienstag 26 August 2008 20:44:53 schrieb Rafael J. Wysocki:
> > On Tuesday, 26 of August 2008, Stefan Seyfried wrote:
> > > On Tue, Aug 26, 2008 at 12:10:08PM +0200, Rafael J. Wysocki wrote:
> > > > > Good. Can you test what happens if you unplug the device while suspended
> > > > > and hibernated?
> > > > 
> > > > It's built-in, I can't unplug it. :-)
> > > 
> > > Maybe you can disable it in the BIOS, but this might change the DSDT / other
> > > system configuration, so it might break resume in other ways :-(
> > 
> > There is a switch that's supposed to disable the radio (rfkill or something).
> > I used it to switch the radio off while the box was waking up from hibernation
> > and kbluetooth didn't find the adapter after the resume.  After I've pressed
> > the "radio off" button again, the bluetooth appears to be functional again.
> > 
> > However, this "radio off" button is shared between bluetooth and wireless
> > (b43) and there are some surprising interactions.  Nothing seems to be broken,
> > though.
> 
> This doesn't explain the original failure. Can you comment out the support
> for suspend/resume in the driver and try again?

With that commented out, I'm able to reproduce the failure.  With the original
patch, I'm not.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 23:28                         ` Rafael J. Wysocki
@ 2008-08-27  5:22                           ` Marcel Holtmann
  2008-08-27  9:10                             ` Pavel Machek
  2008-08-27  9:10                             ` Pavel Machek
  2008-08-27  7:55                           ` Oliver Neukum
                                             ` (3 subsequent siblings)
  4 siblings, 2 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-27  5:22 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

Hi Rafael,

>>>>>> Good. Can you test what happens if you unplug the device while  
>>>>>> suspended
>>>>>> and hibernated?
>>>>>
>>>>> It's built-in, I can't unplug it. :-)
>>>>
>>>> Maybe you can disable it in the BIOS, but this might change the  
>>>> DSDT / other
>>>> system configuration, so it might break resume in other ways :-(
>>>
>>> There is a switch that's supposed to disable the radio (rfkill or  
>>> something).
>>> I used it to switch the radio off while the box was waking up from  
>>> hibernation
>>> and kbluetooth didn't find the adapter after the resume.  After  
>>> I've pressed
>>> the "radio off" button again, the bluetooth appears to be  
>>> functional again.
>>>
>>> However, this "radio off" button is shared between bluetooth and  
>>> wireless
>>> (b43) and there are some surprising interactions.  Nothing seems  
>>> to be broken,
>>> though.
>>
>> This doesn't explain the original failure. Can you comment out the  
>> support
>> for suspend/resume in the driver and try again?
>
> With that commented out, I'm able to reproduce the failure.  With  
> the original
> patch, I'm not.

I've never seen any issues with the suspend/resume and btusb, but I  
must admit that I am using an X61 and in that case pm-utils has a  
magic hack to disable Bluetooth before suspend and this means a clean  
disconnect from the USB bus.

Anyway, killing all URBs in-fly on suspend and bringing up the  
interrupt one on resume should do the right thing. However we have to  
check if we not just better resume all URBs and let the Bluetooth core  
handle lost connection during the suspended time.

Regards

Marcel

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 23:28                         ` Rafael J. Wysocki
  2008-08-27  5:22                           ` Marcel Holtmann
@ 2008-08-27  7:55                           ` Oliver Neukum
  2008-08-27 13:04                             ` Rafael J. Wysocki
  2008-08-27 13:04                             ` Rafael J. Wysocki
  2008-08-27  7:55                           ` Oliver Neukum
                                             ` (2 subsequent siblings)
  4 siblings, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-27  7:55 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Stefan Seyfried, Marcel Holtmann, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

Am Mittwoch 27 August 2008 01:28:15 schrieb Rafael J. Wysocki:
> > This doesn't explain the original failure. Can you comment out the supp=
ort
> > for suspend/resume in the driver and try again?
>=20
> With that commented out, I'm able to reproduce the failure. =A0With the o=
riginal
> patch, I'm not.

Now we know a driver without support for suspend/resume breaks suspend and
hibernate. We need to find out whether this is limited to btusb or everythi=
ng.

Can you test with anotherrrrr driver, like a mouse and remove suspend/resume
from usbhid? If this is specific to btusb we have a fix, if not, I am afraid
this has to be bisected.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 23:28                         ` Rafael J. Wysocki
  2008-08-27  5:22                           ` Marcel Holtmann
  2008-08-27  7:55                           ` Oliver Neukum
@ 2008-08-27  7:55                           ` Oliver Neukum
  2008-08-27  9:56                           ` Marcel Holtmann
  2008-08-27  9:56                           ` Marcel Holtmann
  4 siblings, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-27  7:55 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

Am Mittwoch 27 August 2008 01:28:15 schrieb Rafael J. Wysocki:
> > This doesn't explain the original failure. Can you comment out the support
> > for suspend/resume in the driver and try again?
> 
> With that commented out, I'm able to reproduce the failure.  With the original
> patch, I'm not.

Now we know a driver without support for suspend/resume breaks suspend and
hibernate. We need to find out whether this is limited to btusb or everything.

Can you test with anotherrrrr driver, like a mouse and remove suspend/resume
from usbhid? If this is specific to btusb we have a fix, if not, I am afraid
this has to be bisected.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27  5:22                           ` Marcel Holtmann
@ 2008-08-27  9:10                             ` Pavel Machek
  2008-08-27 13:29                               ` Rafael J. Wysocki
  2008-08-27 13:29                               ` Rafael J. Wysocki
  2008-08-27  9:10                             ` Pavel Machek
  1 sibling, 2 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-27  9:10 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Rafael J.Wysocki, Oliver Neukum, Stefan Seyfried, linux-pm,
	linux-bluetooth, linux-usb

On Wed 2008-08-27 07:22:58, Marcel Holtmann wrote:
> Hi Rafael,
>
>>>>>>> Good. Can you test what happens if you unplug the device while 
>>>>>>> suspended
>>>>>>> and hibernated?
>>>>>>
>>>>>> It's built-in, I can't unplug it. :-)
>>>>>
>>>>> Maybe you can disable it in the BIOS, but this might change the DSDT / 
>>>>> other
>>>>> system configuration, so it might break resume in other ways :-(
>>>>
>>>> There is a switch that's supposed to disable the radio (rfkill or 
>>>> something).
>>>> I used it to switch the radio off while the box was waking up from 
>>>> hibernation
>>>> and kbluetooth didn't find the adapter after the resume.  After I've 
>>>> pressed
>>>> the "radio off" button again, the bluetooth appears to be functional 
>>>> again.
>>>>
>>>> However, this "radio off" button is shared between bluetooth and 
>>>> wireless
>>>> (b43) and there are some surprising interactions.  Nothing seems to be 
>>>> broken,
>>>> though.
>>>
>>> This doesn't explain the original failure. Can you comment out the 
>>> support
>>> for suspend/resume in the driver and try again?
>>
>> With that commented out, I'm able to reproduce the failure.  With the 
>> original
>> patch, I'm not.
>
> I've never seen any issues with the suspend/resume and btusb, but I must 
> admit that I am using an X61 and in that case pm-utils has a magic hack to 
> disable Bluetooth before suspend and this means a clean disconnect from the 
> USB bus.

I was using x60 _without_ that script, and it was okay for long long
time, but it has problems now. Something regressed somewhere.

Rafael, could you try if 2.6.26 suspends ok if you don't unload
anything?

								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27  5:22                           ` Marcel Holtmann
  2008-08-27  9:10                             ` Pavel Machek
@ 2008-08-27  9:10                             ` Pavel Machek
  1 sibling, 0 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-27  9:10 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-usb, linux-bluetooth, linux-pm, Stefan Seyfried

On Wed 2008-08-27 07:22:58, Marcel Holtmann wrote:
> Hi Rafael,
>
>>>>>>> Good. Can you test what happens if you unplug the device while 
>>>>>>> suspended
>>>>>>> and hibernated?
>>>>>>
>>>>>> It's built-in, I can't unplug it. :-)
>>>>>
>>>>> Maybe you can disable it in the BIOS, but this might change the DSDT / 
>>>>> other
>>>>> system configuration, so it might break resume in other ways :-(
>>>>
>>>> There is a switch that's supposed to disable the radio (rfkill or 
>>>> something).
>>>> I used it to switch the radio off while the box was waking up from 
>>>> hibernation
>>>> and kbluetooth didn't find the adapter after the resume.  After I've 
>>>> pressed
>>>> the "radio off" button again, the bluetooth appears to be functional 
>>>> again.
>>>>
>>>> However, this "radio off" button is shared between bluetooth and 
>>>> wireless
>>>> (b43) and there are some surprising interactions.  Nothing seems to be 
>>>> broken,
>>>> though.
>>>
>>> This doesn't explain the original failure. Can you comment out the 
>>> support
>>> for suspend/resume in the driver and try again?
>>
>> With that commented out, I'm able to reproduce the failure.  With the 
>> original
>> patch, I'm not.
>
> I've never seen any issues with the suspend/resume and btusb, but I must 
> admit that I am using an X61 and in that case pm-utils has a magic hack to 
> disable Bluetooth before suspend and this means a clean disconnect from the 
> USB bus.

I was using x60 _without_ that script, and it was okay for long long
time, but it has problems now. Something regressed somewhere.

Rafael, could you try if 2.6.26 suspends ok if you don't unload
anything?

								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 23:28                         ` Rafael J. Wysocki
                                             ` (2 preceding siblings ...)
  2008-08-27  7:55                           ` Oliver Neukum
@ 2008-08-27  9:56                           ` Marcel Holtmann
  2008-08-27  9:56                           ` Marcel Holtmann
  4 siblings, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-27  9:56 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Oliver Neukum, Stefan Seyfried, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

Hi Rafael,

> > > > > > Good. Can you test what happens if you unplug the device while suspended
> > > > > > and hibernated?
> > > > > 
> > > > > It's built-in, I can't unplug it. :-)
> > > > 
> > > > Maybe you can disable it in the BIOS, but this might change the DSDT / other
> > > > system configuration, so it might break resume in other ways :-(
> > > 
> > > There is a switch that's supposed to disable the radio (rfkill or something).
> > > I used it to switch the radio off while the box was waking up from hibernation
> > > and kbluetooth didn't find the adapter after the resume.  After I've pressed
> > > the "radio off" button again, the bluetooth appears to be functional again.
> > > 
> > > However, this "radio off" button is shared between bluetooth and wireless
> > > (b43) and there are some surprising interactions.  Nothing seems to be broken,
> > > though.
> > 
> > This doesn't explain the original failure. Can you comment out the support
> > for suspend/resume in the driver and try again?
> 
> With that commented out, I'm able to reproduce the failure.  With the original
> patch, I'm not.

I've never seen any issues with the suspend/resume and btusb, but I
must admit that I am using an X61 and in that case pm-utils has a magic
hack to disable Bluetooth before suspend and this means a clean
disconnect from the USB bus.

Anyway, killing all URBs in-fly on suspend and bringing up the interrupt
one on resume should do the right thing. However we have to check if we
not just better resume all URBs and let the Bluetooth core handle lost
connection during the suspended time.

Regards

Marcel



^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-26 23:28                         ` Rafael J. Wysocki
                                             ` (3 preceding siblings ...)
  2008-08-27  9:56                           ` Marcel Holtmann
@ 2008-08-27  9:56                           ` Marcel Holtmann
  4 siblings, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-08-27  9:56 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

Hi Rafael,

> > > > > > Good. Can you test what happens if you unplug the device while suspended
> > > > > > and hibernated?
> > > > > 
> > > > > It's built-in, I can't unplug it. :-)
> > > > 
> > > > Maybe you can disable it in the BIOS, but this might change the DSDT / other
> > > > system configuration, so it might break resume in other ways :-(
> > > 
> > > There is a switch that's supposed to disable the radio (rfkill or something).
> > > I used it to switch the radio off while the box was waking up from hibernation
> > > and kbluetooth didn't find the adapter after the resume.  After I've pressed
> > > the "radio off" button again, the bluetooth appears to be functional again.
> > > 
> > > However, this "radio off" button is shared between bluetooth and wireless
> > > (b43) and there are some surprising interactions.  Nothing seems to be broken,
> > > though.
> > 
> > This doesn't explain the original failure. Can you comment out the support
> > for suspend/resume in the driver and try again?
> 
> With that commented out, I'm able to reproduce the failure.  With the original
> patch, I'm not.

I've never seen any issues with the suspend/resume and btusb, but I
must admit that I am using an X61 and in that case pm-utils has a magic
hack to disable Bluetooth before suspend and this means a clean
disconnect from the USB bus.

Anyway, killing all URBs in-fly on suspend and bringing up the interrupt
one on resume should do the right thing. However we have to check if we
not just better resume all URBs and let the Bluetooth core handle lost
connection during the suspended time.

Regards

Marcel

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27  7:55                           ` Oliver Neukum
  2008-08-27 13:04                             ` Rafael J. Wysocki
@ 2008-08-27 13:04                             ` Rafael J. Wysocki
  2008-08-27 13:09                               ` Oliver Neukum
  2008-08-27 13:09                               ` Oliver Neukum
  1 sibling, 2 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-27 13:04 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Stefan Seyfried, Marcel Holtmann, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

On Wednesday, 27 of August 2008, Oliver Neukum wrote:
> Am Mittwoch 27 August 2008 01:28:15 schrieb Rafael J. Wysocki:
> > > This doesn't explain the original failure. Can you comment out the su=
pport
> > > for suspend/resume in the driver and try again?
> >=20
> > With that commented out, I'm able to reproduce the failure. =A0With the=
 original
> > patch, I'm not.
>=20
> Now we know a driver without support for suspend/resume breaks suspend and
> hibernate. We need to find out whether this is limited to btusb or everyt=
hing.

I'm quite confident it's specific to bluetooth, because I only need to turn
the bluetooth user space off, most importantly hcid, to make the problem go
away even without the patch (that is, without the patch I don't even have to
uload btusb before suspend if the bluetoot user land is not running).

> Can you test with anotherrrrr driver, like a mouse and remove suspend/res=
ume
> from usbhid? If this is specific to btusb we have a fix, if not, I am afr=
aid
> this has to be bisected.

Is that really necessary?

I can do that, but first I'll check if 2.6.26 is fine.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27  7:55                           ` Oliver Neukum
@ 2008-08-27 13:04                             ` Rafael J. Wysocki
  2008-08-27 13:04                             ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-27 13:04 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

On Wednesday, 27 of August 2008, Oliver Neukum wrote:
> Am Mittwoch 27 August 2008 01:28:15 schrieb Rafael J. Wysocki:
> > > This doesn't explain the original failure. Can you comment out the support
> > > for suspend/resume in the driver and try again?
> > 
> > With that commented out, I'm able to reproduce the failure.  With the original
> > patch, I'm not.
> 
> Now we know a driver without support for suspend/resume breaks suspend and
> hibernate. We need to find out whether this is limited to btusb or everything.

I'm quite confident it's specific to bluetooth, because I only need to turn
the bluetooth user space off, most importantly hcid, to make the problem go
away even without the patch (that is, without the patch I don't even have to
uload btusb before suspend if the bluetoot user land is not running).

> Can you test with anotherrrrr driver, like a mouse and remove suspend/resume
> from usbhid? If this is specific to btusb we have a fix, if not, I am afraid
> this has to be bisected.

Is that really necessary?

I can do that, but first I'll check if 2.6.26 is fine.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27 13:04                             ` Rafael J. Wysocki
  2008-08-27 13:09                               ` Oliver Neukum
@ 2008-08-27 13:09                               ` Oliver Neukum
  2008-08-27 13:28                                 ` Rafael J. Wysocki
  2008-08-27 13:28                                 ` Rafael J. Wysocki
  1 sibling, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-27 13:09 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Stefan Seyfried, Marcel Holtmann, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

Am Mittwoch 27 August 2008 15:04:55 schrieb Rafael J. Wysocki:
> I'm quite confident it's specific to bluetooth, because I only need to turn
> the bluetooth user space off, most importantly hcid, to make the problem go
> away even without the patch (that is, without the patch I don't even have to
> uload btusb before suspend if the bluetoot user land is not running).

But do you use any other usb driver without support for suspend/resume?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27 13:04                             ` Rafael J. Wysocki
@ 2008-08-27 13:09                               ` Oliver Neukum
  2008-08-27 13:09                               ` Oliver Neukum
  1 sibling, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-27 13:09 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

Am Mittwoch 27 August 2008 15:04:55 schrieb Rafael J. Wysocki:
> I'm quite confident it's specific to bluetooth, because I only need to turn
> the bluetooth user space off, most importantly hcid, to make the problem go
> away even without the patch (that is, without the patch I don't even have to
> uload btusb before suspend if the bluetoot user land is not running).

But do you use any other usb driver without support for suspend/resume?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27 13:09                               ` Oliver Neukum
@ 2008-08-27 13:28                                 ` Rafael J. Wysocki
  2008-08-27 22:33                                   ` Rafael J. Wysocki
  2008-08-27 22:33                                   ` [linux-pm] " Rafael J. Wysocki
  2008-08-27 13:28                                 ` Rafael J. Wysocki
  1 sibling, 2 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-27 13:28 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Stefan Seyfried, Marcel Holtmann, linux-pm, linux-bluetooth,
	Pavel Machek, linux-usb

On Wednesday, 27 of August 2008, Oliver Neukum wrote:
> Am Mittwoch 27 August 2008 15:04:55 schrieb Rafael J. Wysocki:
> > I'm quite confident it's specific to bluetooth, because I only need to turn
> > the bluetooth user space off, most importantly hcid, to make the problem go
> > away even without the patch (that is, without the patch I don't even have to
> > uload btusb before suspend if the bluetoot user land is not running).
> 
> But do you use any other usb driver without support for suspend/resume?

Well, probably not.

I'll check the hid, then.

BTW, 2.6.26 appears to be fine.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27 13:09                               ` Oliver Neukum
  2008-08-27 13:28                                 ` Rafael J. Wysocki
@ 2008-08-27 13:28                                 ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-27 13:28 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, linux-usb, linux-bluetooth, Pavel Machek,
	linux-pm, Stefan Seyfried

On Wednesday, 27 of August 2008, Oliver Neukum wrote:
> Am Mittwoch 27 August 2008 15:04:55 schrieb Rafael J. Wysocki:
> > I'm quite confident it's specific to bluetooth, because I only need to turn
> > the bluetooth user space off, most importantly hcid, to make the problem go
> > away even without the patch (that is, without the patch I don't even have to
> > uload btusb before suspend if the bluetoot user land is not running).
> 
> But do you use any other usb driver without support for suspend/resume?

Well, probably not.

I'll check the hid, then.

BTW, 2.6.26 appears to be fine.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27  9:10                             ` Pavel Machek
@ 2008-08-27 13:29                               ` Rafael J. Wysocki
  2008-08-27 13:29                               ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-27 13:29 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Marcel Holtmann, Oliver Neukum, Stefan Seyfried, linux-pm,
	linux-bluetooth, linux-usb

On Wednesday, 27 of August 2008, Pavel Machek wrote:
> On Wed 2008-08-27 07:22:58, Marcel Holtmann wrote:
> > Hi Rafael,
> >
> >>>>>>> Good. Can you test what happens if you unplug the device while 
> >>>>>>> suspended
> >>>>>>> and hibernated?
> >>>>>>
> >>>>>> It's built-in, I can't unplug it. :-)
> >>>>>
> >>>>> Maybe you can disable it in the BIOS, but this might change the DSDT / 
> >>>>> other
> >>>>> system configuration, so it might break resume in other ways :-(
> >>>>
> >>>> There is a switch that's supposed to disable the radio (rfkill or 
> >>>> something).
> >>>> I used it to switch the radio off while the box was waking up from 
> >>>> hibernation
> >>>> and kbluetooth didn't find the adapter after the resume.  After I've 
> >>>> pressed
> >>>> the "radio off" button again, the bluetooth appears to be functional 
> >>>> again.
> >>>>
> >>>> However, this "radio off" button is shared between bluetooth and 
> >>>> wireless
> >>>> (b43) and there are some surprising interactions.  Nothing seems to be 
> >>>> broken,
> >>>> though.
> >>>
> >>> This doesn't explain the original failure. Can you comment out the 
> >>> support
> >>> for suspend/resume in the driver and try again?
> >>
> >> With that commented out, I'm able to reproduce the failure.  With the 
> >> original
> >> patch, I'm not.
> >
> > I've never seen any issues with the suspend/resume and btusb, but I must 
> > admit that I am using an X61 and in that case pm-utils has a magic hack to 
> > disable Bluetooth before suspend and this means a clean disconnect from the 
> > USB bus.
> 
> I was using x60 _without_ that script, and it was okay for long long
> time, but it has problems now. Something regressed somewhere.
> 
> Rafael, could you try if 2.6.26 suspends ok if you don't unload
> anything?

It appears to be fine (I couldn't reproduce the problem on it).

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27  9:10                             ` Pavel Machek
  2008-08-27 13:29                               ` Rafael J. Wysocki
@ 2008-08-27 13:29                               ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-27 13:29 UTC (permalink / raw)
  To: Pavel Machek
  Cc: linux-usb, Marcel Holtmann, linux-bluetooth, linux-pm, Stefan Seyfried

On Wednesday, 27 of August 2008, Pavel Machek wrote:
> On Wed 2008-08-27 07:22:58, Marcel Holtmann wrote:
> > Hi Rafael,
> >
> >>>>>>> Good. Can you test what happens if you unplug the device while 
> >>>>>>> suspended
> >>>>>>> and hibernated?
> >>>>>>
> >>>>>> It's built-in, I can't unplug it. :-)
> >>>>>
> >>>>> Maybe you can disable it in the BIOS, but this might change the DSDT / 
> >>>>> other
> >>>>> system configuration, so it might break resume in other ways :-(
> >>>>
> >>>> There is a switch that's supposed to disable the radio (rfkill or 
> >>>> something).
> >>>> I used it to switch the radio off while the box was waking up from 
> >>>> hibernation
> >>>> and kbluetooth didn't find the adapter after the resume.  After I've 
> >>>> pressed
> >>>> the "radio off" button again, the bluetooth appears to be functional 
> >>>> again.
> >>>>
> >>>> However, this "radio off" button is shared between bluetooth and 
> >>>> wireless
> >>>> (b43) and there are some surprising interactions.  Nothing seems to be 
> >>>> broken,
> >>>> though.
> >>>
> >>> This doesn't explain the original failure. Can you comment out the 
> >>> support
> >>> for suspend/resume in the driver and try again?
> >>
> >> With that commented out, I'm able to reproduce the failure.  With the 
> >> original
> >> patch, I'm not.
> >
> > I've never seen any issues with the suspend/resume and btusb, but I must 
> > admit that I am using an X61 and in that case pm-utils has a magic hack to 
> > disable Bluetooth before suspend and this means a clean disconnect from the 
> > USB bus.
> 
> I was using x60 _without_ that script, and it was okay for long long
> time, but it has problems now. Something regressed somewhere.
> 
> Rafael, could you try if 2.6.26 suspends ok if you don't unload
> anything?

It appears to be fine (I couldn't reproduce the problem on it).

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [linux-pm] btusb hibernation/suspend breakage in current -git
  2008-08-27 13:28                                 ` Rafael J. Wysocki
  2008-08-27 22:33                                   ` Rafael J. Wysocki
@ 2008-08-27 22:33                                   ` Rafael J. Wysocki
  2008-08-28  7:17                                     ` Oliver Neukum
  1 sibling, 1 reply; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-27 22:33 UTC (permalink / raw)
  To: linux-pm
  Cc: Oliver Neukum, Marcel Holtmann, linux-usb, linux-bluetooth,
	Pavel Machek, Stefan Seyfried

On Wednesday, 27 of August 2008, Rafael J. Wysocki wrote:
> On Wednesday, 27 of August 2008, Oliver Neukum wrote:
> > Am Mittwoch 27 August 2008 15:04:55 schrieb Rafael J. Wysocki:
> > > I'm quite confident it's specific to bluetooth, because I only need to turn
> > > the bluetooth user space off, most importantly hcid, to make the problem go
> > > away even without the patch (that is, without the patch I don't even have to
> > > uload btusb before suspend if the bluetoot user land is not running).
> > 
> > But do you use any other usb driver without support for suspend/resume?
> 
> Well, probably not.
> 
> I'll check the hid, then.

I couldn't reproduce the breakage with suspend/resume support removed from
usbhid.

Let's just say it's related to BT, since your patch evidently fixes the problem
for me and I have just no reason to believe it's anything else than BT.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-27 13:28                                 ` Rafael J. Wysocki
@ 2008-08-27 22:33                                   ` Rafael J. Wysocki
  2008-08-27 22:33                                   ` [linux-pm] " Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-08-27 22:33 UTC (permalink / raw)
  To: linux-pm
  Cc: linux-usb, Marcel Holtmann, linux-bluetooth, Pavel Machek,
	Stefan Seyfried

On Wednesday, 27 of August 2008, Rafael J. Wysocki wrote:
> On Wednesday, 27 of August 2008, Oliver Neukum wrote:
> > Am Mittwoch 27 August 2008 15:04:55 schrieb Rafael J. Wysocki:
> > > I'm quite confident it's specific to bluetooth, because I only need to turn
> > > the bluetooth user space off, most importantly hcid, to make the problem go
> > > away even without the patch (that is, without the patch I don't even have to
> > > uload btusb before suspend if the bluetoot user land is not running).
> > 
> > But do you use any other usb driver without support for suspend/resume?
> 
> Well, probably not.
> 
> I'll check the hid, then.

I couldn't reproduce the breakage with suspend/resume support removed from
usbhid.

Let's just say it's related to BT, since your patch evidently fixes the problem
for me and I have just no reason to believe it's anything else than BT.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [linux-pm] btusb hibernation/suspend breakage in current -git
  2008-08-27 22:33                                   ` [linux-pm] " Rafael J. Wysocki
@ 2008-08-28  7:17                                     ` Oliver Neukum
  2008-09-08 20:49                                       ` Marcel Holtmann
  2008-09-08 20:49                                       ` [linux-pm] " Marcel Holtmann
  0 siblings, 2 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-28  7:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-pm, Marcel Holtmann, linux-usb, linux-bluetooth,
	Pavel Machek, Stefan Seyfried

Am Donnerstag 28 August 2008 00:33:06 schrieb Rafael J. Wysocki:
> On Wednesday, 27 of August 2008, Rafael J. Wysocki wrote:
> > On Wednesday, 27 of August 2008, Oliver Neukum wrote:
> > > Am Mittwoch 27 August 2008 15:04:55 schrieb Rafael J. Wysocki:
> > > > I'm quite confident it's specific to bluetooth, because I only need to turn
> > > > the bluetooth user space off, most importantly hcid, to make the problem go
> > > > away even without the patch (that is, without the patch I don't even have to
> > > > uload btusb before suspend if the bluetoot user land is not running).
> > > 
> > > But do you use any other usb driver without support for suspend/resume?
> > 
> > Well, probably not.
> > 
> > I'll check the hid, then.
> 
> I couldn't reproduce the breakage with suspend/resume support removed from
> usbhid.
> 
> Let's just say it's related to BT, since your patch evidently fixes the problem
> for me and I have just no reason to believe it's anything else than BT.
> 
> Thanks,
> Rafael
> 

Very good.

Marcel, here it is again. You may need to cut the USB bits depending
on which tree you want to apply it to.

Signed-off-by: Oliver Neukum <oneukum@suse.de>

	Regards
		Oliver
---

--- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000 +0200
+++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-22 17:25:49.000000000 +0200
@@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 {
 	struct urb *victim;
+	unsigned long flags;
 
-	spin_lock_irq(&anchor->lock);
+	spin_lock_irqsave(&anchor->lock, flags);
 	while (!list_empty(&anchor->urb_list)) {
 		victim = list_entry(anchor->urb_list.prev, struct urb,
 				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
 		/* this will unanchor the URB */
 		usb_unlink_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
 	}
-	spin_unlock_irq(&anchor->lock);
+	spin_unlock_irqrestore(&anchor->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 
--- linux-2.6.27-rc4/drivers/bluetooth/btusb.c.alt	2008-08-25 15:02:14.000000000 +0200
+++ linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-25 15:44:25.000000000 +0200
@@ -169,6 +169,7 @@ static struct usb_device_id blacklist_ta
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,6 +177,7 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
@@ -189,6 +191,7 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int susp_count;
 };
 
 static void btusb_intr_complete(struct urb *urb)
@@ -227,7 +230,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +243,13 @@ static int btusb_submit_intr_urb(struct
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +265,7 @@ static int btusb_submit_intr_urb(struct
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -311,7 +314,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +327,19 @@ static int btusb_submit_bulk_urb(struct
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +349,7 @@ static int btusb_submit_bulk_urb(struct
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -514,7 +518,7 @@ static int btusb_open(struct hci_dev *hd
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,6 +527,13 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
@@ -532,14 +543,12 @@ static int btusb_close(struct hci_dev *h
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	btusb_stop_traffic(data);
 
 	return 0;
 }
@@ -672,8 +681,19 @@ static void btusb_notify(struct hci_dev
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -724,18 +744,6 @@ static void btusb_work(struct work_struc
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
 
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
-
 	if (hdev->conn_hash.sco_num > 0) {
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
@@ -821,6 +829,7 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
@@ -889,7 +898,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,20 +930,92 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	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("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_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 ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
 };
 

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-26 11:02             ` Oliver Neukum
  2008-08-28  8:06               ` Pavel Machek
@ 2008-08-28  8:06               ` Pavel Machek
  1 sibling, 0 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-28  8:06 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Marcel Holtmann, Stefan Seyfried, linux-bluetooth, linux-pm, linux-usb

On Tue 2008-08-26 13:02:51, Oliver Neukum wrote:
> Am Dienstag 26 August 2008 12:05:19 schrieb Pavel Machek:
> 
> Hi,
> 
> > And it worked for me, but suspend died. I'll verify if that's a
> > problem in vanilla -rc4-git, too.
> 
> what exactly does "died" mean?

Black screen during resume, backlight on, but otherwise unresposnive machine.
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-26 11:02             ` Oliver Neukum
@ 2008-08-28  8:06               ` Pavel Machek
  2008-08-28  8:06               ` Pavel Machek
  1 sibling, 0 replies; 73+ messages in thread
From: Pavel Machek @ 2008-08-28  8:06 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-bluetooth, Marcel Holtmann, linux-usb, Stefan Seyfried, linux-pm

On Tue 2008-08-26 13:02:51, Oliver Neukum wrote:
> Am Dienstag 26 August 2008 12:05:19 schrieb Pavel Machek:
> 
> Hi,
> 
> > And it worked for me, but suspend died. I'll verify if that's a
> > problem in vanilla -rc4-git, too.
> 
> what exactly does "died" mean?

Black screen during resume, backlight on, but otherwise unresposnive machine.
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [linux-pm] btusb hibernation/suspend breakage in current -git
  2008-08-28  7:17                                     ` Oliver Neukum
  2008-09-08 20:49                                       ` Marcel Holtmann
@ 2008-09-08 20:49                                       ` Marcel Holtmann
  2008-09-08 21:45                                         ` Rafael J. Wysocki
  2008-09-08 21:45                                         ` Rafael J. Wysocki
  1 sibling, 2 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-09-08 20:49 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: Rafael J. Wysocki, linux-pm, linux-usb, linux-bluetooth,
	Pavel Machek, Stefan Seyfried

Hi Oliver,

> > I couldn't reproduce the breakage with suspend/resume support removed from
> > usbhid.
> > 
> > Let's just say it's related to BT, since your patch evidently fixes the problem
> > for me and I have just no reason to believe it's anything else than BT.
>
> Very good.
> 
> here it is again. You may need to cut the USB bits depending
> on which tree you want to apply it to.

do we have a version that could be applied to 2.6.27-rc5, because the
patch depends on unmerged USB stuff. I tend to leave this for 2.6.28 for
now.

Regards

Marcel



^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-08-28  7:17                                     ` Oliver Neukum
@ 2008-09-08 20:49                                       ` Marcel Holtmann
  2008-09-08 20:49                                       ` [linux-pm] " Marcel Holtmann
  1 sibling, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2008-09-08 20:49 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

Hi Oliver,

> > I couldn't reproduce the breakage with suspend/resume support removed from
> > usbhid.
> > 
> > Let's just say it's related to BT, since your patch evidently fixes the problem
> > for me and I have just no reason to believe it's anything else than BT.
>
> Very good.
> 
> here it is again. You may need to cut the USB bits depending
> on which tree you want to apply it to.

do we have a version that could be applied to 2.6.27-rc5, because the
patch depends on unmerged USB stuff. I tend to leave this for 2.6.28 for
now.

Regards

Marcel

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [linux-pm] btusb hibernation/suspend breakage in current -git
  2008-09-08 20:49                                       ` [linux-pm] " Marcel Holtmann
@ 2008-09-08 21:45                                         ` Rafael J. Wysocki
  2008-09-08 21:45                                         ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-09-08 21:45 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Oliver Neukum, linux-pm, linux-usb, linux-bluetooth,
	Pavel Machek, Stefan Seyfried

On Monday, 8 of September 2008, Marcel Holtmann wrote:
> Hi Oliver,
> 
> > > I couldn't reproduce the breakage with suspend/resume support removed from
> > > usbhid.
> > > 
> > > Let's just say it's related to BT, since your patch evidently fixes the problem
> > > for me and I have just no reason to believe it's anything else than BT.
> >
> > Very good.
> > 
> > here it is again. You may need to cut the USB bits depending
> > on which tree you want to apply it to.
> 
> do we have a version that could be applied to 2.6.27-rc5, because the
> patch depends on unmerged USB stuff. I tend to leave this for 2.6.28 for
> now.

I've been using the one below recently.  It doesn't seem to depend on anything
unmerged.

Thanks,
Rafael


---
 drivers/bluetooth/btusb.c |  145 +++++++++++++++++++++++++++++++++++-----------
 1 file changed, 113 insertions(+), 32 deletions(-)

Index: linux-2.6/drivers/bluetooth/btusb.c
===================================================================
--- linux-2.6.orig/drivers/bluetooth/btusb.c
+++ linux-2.6/drivers/bluetooth/btusb.c
@@ -169,6 +169,7 @@ static struct usb_device_id blacklist_ta
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,6 +177,7 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
@@ -189,6 +191,7 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int susp_count;
 };
 
 static void btusb_intr_complete(struct urb *urb)
@@ -227,7 +230,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +243,13 @@ static int btusb_submit_intr_urb(struct 
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +265,7 @@ static int btusb_submit_intr_urb(struct 
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -311,7 +314,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +327,19 @@ static int btusb_submit_bulk_urb(struct 
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +349,7 @@ static int btusb_submit_bulk_urb(struct 
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -514,7 +518,7 @@ static int btusb_open(struct hci_dev *hd
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,6 +527,13 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
@@ -532,14 +543,12 @@ static int btusb_close(struct hci_dev *h
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	btusb_stop_traffic(data);
 
 	return 0;
 }
@@ -672,8 +681,19 @@ static void btusb_notify(struct hci_dev 
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -724,18 +744,6 @@ static void btusb_work(struct work_struc
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
 
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
-
 	if (hdev->conn_hash.sco_num > 0) {
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
@@ -821,6 +829,7 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
@@ -889,7 +898,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,20 +930,92 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	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("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_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 ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
 };
 

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: btusb hibernation/suspend breakage in current -git
  2008-09-08 20:49                                       ` [linux-pm] " Marcel Holtmann
  2008-09-08 21:45                                         ` Rafael J. Wysocki
@ 2008-09-08 21:45                                         ` Rafael J. Wysocki
  1 sibling, 0 replies; 73+ messages in thread
From: Rafael J. Wysocki @ 2008-09-08 21:45 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

On Monday, 8 of September 2008, Marcel Holtmann wrote:
> Hi Oliver,
> 
> > > I couldn't reproduce the breakage with suspend/resume support removed from
> > > usbhid.
> > > 
> > > Let's just say it's related to BT, since your patch evidently fixes the problem
> > > for me and I have just no reason to believe it's anything else than BT.
> >
> > Very good.
> > 
> > here it is again. You may need to cut the USB bits depending
> > on which tree you want to apply it to.
> 
> do we have a version that could be applied to 2.6.27-rc5, because the
> patch depends on unmerged USB stuff. I tend to leave this for 2.6.28 for
> now.

I've been using the one below recently.  It doesn't seem to depend on anything
unmerged.

Thanks,
Rafael


---
 drivers/bluetooth/btusb.c |  145 +++++++++++++++++++++++++++++++++++-----------
 1 file changed, 113 insertions(+), 32 deletions(-)

Index: linux-2.6/drivers/bluetooth/btusb.c
===================================================================
--- linux-2.6.orig/drivers/bluetooth/btusb.c
+++ linux-2.6/drivers/bluetooth/btusb.c
@@ -169,6 +169,7 @@ static struct usb_device_id blacklist_ta
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,6 +177,7 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
@@ -189,6 +191,7 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int susp_count;
 };
 
 static void btusb_intr_complete(struct urb *urb)
@@ -227,7 +230,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +243,13 @@ static int btusb_submit_intr_urb(struct 
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +265,7 @@ static int btusb_submit_intr_urb(struct 
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -311,7 +314,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +327,19 @@ static int btusb_submit_bulk_urb(struct 
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +349,7 @@ static int btusb_submit_bulk_urb(struct 
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -514,7 +518,7 @@ static int btusb_open(struct hci_dev *hd
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,6 +527,13 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
@@ -532,14 +543,12 @@ static int btusb_close(struct hci_dev *h
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	btusb_stop_traffic(data);
 
 	return 0;
 }
@@ -672,8 +681,19 @@ static void btusb_notify(struct hci_dev 
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -724,18 +744,6 @@ static void btusb_work(struct work_struc
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
 
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
-
 	if (hdev->conn_hash.sco_num > 0) {
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
@@ -821,6 +829,7 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
@@ -889,7 +898,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,20 +930,92 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	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("%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_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 ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
 };
 

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [linux-pm] [rft]autosuspend for btusb
  2008-08-25 11:51         ` Marcel Holtmann
@ 2009-02-11 16:52           ` Matthew Garrett
  2009-02-11 16:55             ` Marcel Holtmann
  2009-02-11 16:55             ` Marcel Holtmann
  2009-02-11 16:52           ` Matthew Garrett
  1 sibling, 2 replies; 73+ messages in thread
From: Matthew Garrett @ 2009-02-11 16:52 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Oliver Neukum, linux-bluetooth, Pavel Machek, linux-usb,
	Stefan Seyfried, linux-pm

On Mon, Aug 25, 2008 at 01:51:30PM +0200, Marcel Holtmann wrote:

> Can you break out the USB anchor extensions and make sure they get  
> merged into 2.6.28 in an early stage of the merge window. It really  
> wanna get all of this stuff into the next kernel release. Finally we  
> are getting somewhere with this driver and can kill the broken hci_usb.

The anchor extensions have hit mainline, but there doesn't seem to have 
been any progress on the btusb autosuspend front. Are there any plans 
for this?

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2008-08-25 11:51         ` Marcel Holtmann
  2009-02-11 16:52           ` [linux-pm] " Matthew Garrett
@ 2009-02-11 16:52           ` Matthew Garrett
  1 sibling, 0 replies; 73+ messages in thread
From: Matthew Garrett @ 2009-02-11 16:52 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

On Mon, Aug 25, 2008 at 01:51:30PM +0200, Marcel Holtmann wrote:

> Can you break out the USB anchor extensions and make sure they get  
> merged into 2.6.28 in an early stage of the merge window. It really  
> wanna get all of this stuff into the next kernel release. Finally we  
> are getting somewhere with this driver and can kill the broken hci_usb.

The anchor extensions have hit mainline, but there doesn't seem to have 
been any progress on the btusb autosuspend front. Are there any plans 
for this?

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [linux-pm] [rft]autosuspend for btusb
  2009-02-11 16:52           ` [linux-pm] " Matthew Garrett
@ 2009-02-11 16:55             ` Marcel Holtmann
  2009-02-11 16:55             ` Marcel Holtmann
  1 sibling, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2009-02-11 16:55 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Oliver Neukum, linux-bluetooth, Pavel Machek, linux-usb,
	Stefan Seyfried, linux-pm

Hi Matthew,

> > Can you break out the USB anchor extensions and make sure they get  
> > merged into 2.6.28 in an early stage of the merge window. It really  
> > wanna get all of this stuff into the next kernel release. Finally we  
> > are getting somewhere with this driver and can kill the broken hci_usb.
> 
> The anchor extensions have hit mainline, but there doesn't seem to have 
> been any progress on the btusb autosuspend front. Are there any plans 
> for this?

not right now, but my bluetooth-testing.git tree has one change for the
bulk URBs. It seems that some dongles really require them to be present
and won't work otherwise.

Regards

Marcel



^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [rft]autosuspend for btusb
  2009-02-11 16:52           ` [linux-pm] " Matthew Garrett
  2009-02-11 16:55             ` Marcel Holtmann
@ 2009-02-11 16:55             ` Marcel Holtmann
  1 sibling, 0 replies; 73+ messages in thread
From: Marcel Holtmann @ 2009-02-11 16:55 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: linux-usb, linux-bluetooth, Pavel Machek, linux-pm, Stefan Seyfried

Hi Matthew,

> > Can you break out the USB anchor extensions and make sure they get  
> > merged into 2.6.28 in an early stage of the merge window. It really  
> > wanna get all of this stuff into the next kernel release. Finally we  
> > are getting somewhere with this driver and can kill the broken hci_usb.
> 
> The anchor extensions have hit mainline, but there doesn't seem to have 
> been any progress on the btusb autosuspend front. Are there any plans 
> for this?

not right now, but my bluetooth-testing.git tree has one change for the
bulk URBs. It seems that some dongles really require them to be present
and won't work otherwise.

Regards

Marcel

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [rft]autosuspend for btusb
@ 2008-08-22 13:20 Oliver Neukum
  0 siblings, 0 replies; 73+ messages in thread
From: Oliver Neukum @ 2008-08-22 13:20 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-bluetooth, Pavel Machek, linux-usb, Stefan Seyfried, linux-pm

Hi,

this patch against vanilla 2.6.27-rc4 implements full autosuspend for
btusb. It should allow the HCI to be suspended during periods of inactivity
while retaining full service if the device supports USB remote wakeup.

Please test and/or comment on the code.
It works for me with a few glitches but still needs to be a bit polished.

	Regards
		Oliver

---

--- linux-2.6.27-rc4/drivers/usb/core/urb.c	2008-08-21 10:03:44.000000000 +0200
+++ linux-2.6.27-rc3/drivers/usb/core/urb.c	2008-08-20 17:21:24.000000000 +0200
@@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 {
 	struct urb *victim;
+	unsigned long flags;
 
-	spin_lock_irq(&anchor->lock);
+	spin_lock_irqsave(&anchor->lock, flags);
 	while (!list_empty(&anchor->urb_list)) {
 		victim = list_entry(anchor->urb_list.prev, struct urb,
 				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
 		/* this will unanchor the URB */
 		usb_unlink_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
 	}
-	spin_unlock_irq(&anchor->lock);
+	spin_unlock_irqrestore(&anchor->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 
@@ -628,3 +633,47 @@ int usb_wait_anchor_empty_timeout(struct
 				  msecs_to_jiffies(timeout));
 }
 EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
+struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	if (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.next, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		usb_unanchor_urb(victim);
+	} else {
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		victim = NULL;
+	}
+
+	return victim;
+}
+
+EXPORT_SYMBOL_GPL(usb_get_from_anchor);
+
+void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		/* this will unanchor the URB */
+		usb_unanchor_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irqsave(&anchor->lock, flags);
+	}
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);
+
--- linux-2.6.27-rc4/include/linux/usb.h	2008-08-21 10:04:29.000000000 +0200
+++ linux-2.6.27-rc3/include/linux/usb.h	2008-08-20 17:09:57.000000000 +0200
@@ -1462,6 +1460,8 @@ extern void usb_anchor_urb(struct urb *u
 extern void usb_unanchor_urb(struct urb *urb);
 extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
 					 unsigned int timeout);
+extern struct urb *usb_get_from_anchor(struct usb_anchor *anchor);
+extern void usb_scuttle_anchored_urbs(struct usb_anchor *anchor);
 
 /**
  * usb_urb_dir_in - check if an URB describes an IN transfer
--- linux-2.6.27-rc4/drivers/bluetooth/btusb.c	2008-08-21 10:04:11.000000000 +0200
+++ linux-2.6.27-rc3/drivers/bluetooth/btusb.c	2008-08-22 14:39:05.000000000 +0200
@@ -35,13 +35,13 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-//#define CONFIG_BT_HCIBTUSB_DEBUG
+#define CONFIG_BT_HCIBTUSB_DEBUG
 #ifndef CONFIG_BT_HCIBTUSB_DEBUG
 #undef  BT_DBG
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "0.3"
+#define VERSION "0.4"
 
 static int ignore_dga;
 static int ignore_csr;
@@ -165,10 +165,12 @@ static struct usb_device_id blacklist_ta
 #define BTUSB_INTR_RUNNING	0
 #define BTUSB_BULK_RUNNING	1
 #define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
 
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
+	struct usb_interface *acl;
 	struct usb_interface *isoc;
 
 	spinlock_t lock;
@@ -176,11 +178,15 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
 	struct usb_anchor bulk_anchor;
 	struct usb_anchor isoc_anchor;
+	struct usb_anchor deferred;
+	int tx_in_flight;
+	spinlock_t txlock;
 
 	struct usb_endpoint_descriptor *intr_ep;
 	struct usb_endpoint_descriptor *bulk_tx_ep;
@@ -189,8 +195,26 @@ struct btusb_data {
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	int isoc_altsetting;
+	int did_iso_resume:1;
+	int susp_count;
 };
 
+static int inc_tx(struct btusb_data *data)
+{
+	unsigned long flags;
+	int rv;
+
+	BT_DBG("entered");
+	spin_lock_irqsave(&data->txlock, flags);
+	rv = test_bit(BTUSB_SUSPENDING, &data->flags);
+	if (!rv)
+		data->tx_in_flight++;
+	spin_unlock_irqrestore(&data->txlock, flags);
+	BT_DBG("returning %d", rv);
+
+	return rv;
+}
+
 static void btusb_intr_complete(struct urb *urb)
 {
 	struct hci_dev *hdev = urb->context;
@@ -217,6 +241,7 @@ static void btusb_intr_complete(struct u
 	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->intr_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -227,7 +252,7 @@ static void btusb_intr_complete(struct u
 	}
 }
 
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -240,13 +265,13 @@ static int btusb_submit_intr_urb(struct
 	if (!data->intr_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_ATOMIC);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@@ -262,7 +287,7 @@ static int btusb_submit_intr_urb(struct
 
 	usb_anchor_urb(urb, &data->intr_anchor);
 
-	err = usb_submit_urb(urb, GFP_ATOMIC);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -301,6 +326,7 @@ static void btusb_bulk_complete(struct u
 	if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -311,7 +337,7 @@ static void btusb_bulk_complete(struct u
 	}
 }
 
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t gfp)
 {
 	struct btusb_data *data = hdev->driver_data;
 	struct urb *urb;
@@ -324,18 +350,19 @@ static int btusb_submit_bulk_urb(struct
 	if (!data->bulk_rx_ep)
 		return -ENODEV;
 
-	urb = usb_alloc_urb(0, GFP_KERNEL);
+	urb = usb_alloc_urb(0, gfp);
 	if (!urb)
 		return -ENOMEM;
 
 	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
 
-	buf = kmalloc(size, GFP_KERNEL);
+	buf = kmalloc(size, gfp);
 	if (!buf) {
 		usb_free_urb(urb);
 		return -ENOMEM;
 	}
 
+	usb_mark_last_busy(data->udev);
 	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
 
 	usb_fill_bulk_urb(urb, data->udev, pipe,
@@ -345,7 +372,7 @@ static int btusb_submit_bulk_urb(struct
 
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
-	err = usb_submit_urb(urb, GFP_KERNEL);
+	err = usb_submit_urb(urb, gfp);
 	if (err < 0) {
 		BT_ERR("%s urb %p submission failed (%d)",
 						hdev->name, urb, -err);
@@ -483,6 +510,33 @@ static void btusb_tx_complete(struct urb
 {
 	struct sk_buff *skb = urb->context;
 	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct btusb_data *data = hdev->driver_data;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		goto done;
+
+	if (!urb->status)
+		hdev->stat.byte_tx += urb->transfer_buffer_length;
+	else
+		hdev->stat.err_tx++;
+
+done:
+	spin_lock(&data->txlock);
+	data->tx_in_flight--;
+	spin_unlock(&data->txlock);
+
+	kfree(urb->setup_packet);
+
+	kfree_skb(skb);
+}
+
+static void btusb_isoc_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = urb->context;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
 
 	BT_DBG("%s urb %p status %d count %d", hdev->name,
 					urb, urb->status, urb->actual_length);
@@ -508,13 +562,19 @@ static int btusb_open(struct hci_dev *hd
 
 	BT_DBG("%s", hdev->name);
 
+	err = usb_autopm_get_interface(data->acl);
+	if (err < 0)
+		return err;
+	data->acl->needs_remote_wakeup = 1;
+	usb_autopm_put_interface(data->acl);
+
 	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return 0;
 
-	err = btusb_submit_intr_urb(hdev);
+	err = btusb_submit_intr_urb(hdev, GFP_ATOMIC);
 	if (err < 0) {
 		clear_bit(BTUSB_INTR_RUNNING, &hdev->flags);
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -523,24 +583,34 @@ static int btusb_open(struct hci_dev *hd
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
+	int err;
 
 	BT_DBG("%s", hdev->name);
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+	flush_work(&data->work);
 
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
-
+	btusb_stop_traffic(data);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err) {
+		data->acl->needs_remote_wakeup = 0;
+		usb_autopm_put_interface(data->acl);
+	}
 	return 0;
 }
 
@@ -571,6 +641,7 @@ static int btusb_send_frame(struct sk_bu
 
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
+		BT_DBG("HCI_COMMAND_PKT");
 		urb = usb_alloc_urb(0, GFP_ATOMIC);
 		if (!urb)
 			return -ENOMEM;
@@ -596,6 +667,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_ACLDATA_PKT:
+		BT_DBG("HCI_ACLDATA_PKT");
 		if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
 			return -ENODEV;
 
@@ -613,6 +685,7 @@ static int btusb_send_frame(struct sk_bu
 		break;
 
 	case HCI_SCODATA_PKT:
+		BT_DBG("HCI_SCODATA_PKT");
 		if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
 			return -ENODEV;
 
@@ -626,7 +699,7 @@ static int btusb_send_frame(struct sk_bu
 		urb->dev      = data->udev;
 		urb->pipe     = pipe;
 		urb->context  = skb;
-		urb->complete = btusb_tx_complete;
+		urb->complete = btusb_isoc_tx_complete;
 		urb->interval = data->isoc_tx_ep->bInterval;
 
 		urb->transfer_flags  = URB_ISO_ASAP;
@@ -637,12 +710,21 @@ static int btusb_send_frame(struct sk_bu
 				le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
 
 		hdev->stat.sco_tx++;
-		break;
+		goto skip_waking;
 
 	default:
 		return -EILSEQ;
 	}
 
+	err = inc_tx(data);
+	if (err) {
+		printk(KERN_ERR"Autosuspension detected\n");
+		usb_anchor_urb(urb, &data->deferred);
+		schedule_work(&data->waker);
+		err = 0;
+		goto out;
+	}
+skip_waking:
 	usb_anchor_urb(urb, &data->tx_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -654,6 +736,7 @@ static int btusb_send_frame(struct sk_bu
 
 	usb_free_urb(urb);
 
+out:
 	return err;
 }
 
@@ -672,8 +755,19 @@ static void btusb_notify(struct hci_dev
 
 	BT_DBG("%s evt %d", hdev->name, evt);
 
-	if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
-		schedule_work(&data->work);
+	if (hdev->conn_hash.acl_num > 0) {
+		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+			if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			else
+				btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+		}
+	} else {
+		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+		usb_unlink_anchored_urbs(&data->bulk_anchor);
+	}
+
+	schedule_work(&data->work);
 }
 
 static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -723,20 +817,19 @@ static void btusb_work(struct work_struc
 {
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
-
-	if (hdev->conn_hash.acl_num > 0) {
-		if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-			if (btusb_submit_bulk_urb(hdev) < 0)
-				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			else
-				btusb_submit_bulk_urb(hdev);
-		}
-	} else {
-		clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->bulk_anchor);
-	}
+	int err;
 
 	if (hdev->conn_hash.sco_num > 0) {
+		if (!data->did_iso_resume) {
+			err = usb_autopm_get_interface(data->isoc);
+			if (!err) {
+				data->did_iso_resume = 1;
+			} else {
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->isoc_anchor);
+				return;
+			}
+		}
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			usb_kill_anchored_urbs(&data->isoc_anchor);
@@ -756,9 +849,26 @@ static void btusb_work(struct work_struc
 		usb_kill_anchored_urbs(&data->isoc_anchor);
 
 		__set_isoc_interface(hdev, 0);
+		if (data->did_iso_resume) {
+			data->did_iso_resume = 0;
+			usb_autopm_put_interface(data->isoc);
+		}
 	}
 }
 
+static void btusb_waker(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, waker);
+	int err;
+
+	BUG_ON(data == NULL);
+	BT_DBG("about to resume");
+	BUG_ON(data->acl == NULL);
+	err = usb_autopm_get_interface(data->acl);
+	if (!err)
+		usb_autopm_put_interface(data->acl);
+}
+
 static int btusb_probe(struct usb_interface *intf,
 				const struct usb_device_id *id)
 {
@@ -821,15 +931,19 @@ static int btusb_probe(struct usb_interf
 	}
 
 	data->udev = interface_to_usbdev(intf);
+	data->acl = intf;
 
 	spin_lock_init(&data->lock);
 
 	INIT_WORK(&data->work, btusb_work);
+	INIT_WORK(&data->waker, btusb_waker);
+	spin_lock_init(&data->txlock);
 
 	init_usb_anchor(&data->tx_anchor);
 	init_usb_anchor(&data->intr_anchor);
 	init_usb_anchor(&data->bulk_anchor);
 	init_usb_anchor(&data->isoc_anchor);
+	init_usb_anchor(&data->deferred);
 
 	hdev = hci_alloc_dev();
 	if (!hdev) {
@@ -889,7 +1003,7 @@ static int btusb_probe(struct usb_interf
 
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, NULL);
+							data->isoc, data);
 		if (err < 0) {
 			hci_free_dev(hdev);
 			kfree(data);
@@ -921,21 +1035,131 @@ static void btusb_disconnect(struct usb_
 
 	hdev = data->hdev;
 
-	if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
+	/* make sure we have a reference */
+	__hci_dev_hold(hdev);
 
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(data->acl, NULL);
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
 
+	/* unregister before releasing any interface */
 	hci_unregister_dev(hdev);
 
+	if (intf == data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->acl);
+	else if (data->isoc)
+		usb_driver_release_interface(&btusb_driver, data->isoc);
+
+	/* release the reference */
+	__hci_dev_put(hdev);
 	hci_free_dev(hdev);
 }
 
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+
+	printk(KERN_ERR"%s called\n", __func__);
+
+	if (data->susp_count++)
+		return 0;
+	spin_lock_irq(&data->txlock);
+	if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) {
+		set_bit(BTUSB_SUSPENDING, &data->flags);
+		printk(KERN_ERR"%s accepting\n", __func__);
+	} else {
+		spin_unlock_irq(&data->txlock);
+		printk(KERN_ERR"%s rejecting, count %d\n", __func__, data->tx_in_flight);
+		return -EBUSY;
+	}
+	spin_unlock_irq(&data->txlock);
+	cancel_work_sync(&data->work);
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_anchor);
+	BT_DBG("flags: %ld", data->flags);
+	return 0;
+}
+
+static int play_deferred(struct btusb_data *data)
+{
+	struct urb *urb;
+	int err = 0;
+
+	while ((urb = usb_get_from_anchor(&data->deferred))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0)
+			break;
+		else
+			data->tx_in_flight++;
+	}
+	usb_scuttle_anchored_urbs(&data->deferred);
+	return err;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+	struct hci_dev *hdev = data->hdev;
+	int ret;
+
+	if (--data->susp_count)
+		return 0;
+	if (test_bit(HCI_RUNNING, &hdev->flags)) {
+
+		spin_lock_irq(&data->txlock);
+		ret = play_deferred(data);
+		clear_bit(BTUSB_SUSPENDING, &data->flags);
+		spin_unlock_irq(&data->txlock);
+
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+
+		ret = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			return ret;
+		}
+	}
+
+	if (hdev->conn_hash.acl_num > 0) {
+		ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (ret < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			return ret;
+		} else {
+			ret = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			if (ret < 0) {
+				clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->bulk_anchor);
+				return ret;
+			}
+		}
+	}
+
+	if (data->isoc) {
+		if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			ret = btusb_submit_isoc_urb(hdev);
+			if (ret < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev);
+		}
+	}
+
+	schedule_work(&data->work);
+	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,
+	.supports_autosuspend = 1,
 };
 
 static int __init btusb_init(void)

^ permalink raw reply	[flat|nested] 73+ messages in thread

end of thread, other threads:[~2009-02-11 16:55 UTC | newest]

Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-22 13:20 [rft]autosuspend for btusb Oliver Neukum
2008-08-22 13:33 ` Marcel Holtmann
2008-08-22 13:51   ` Oliver Neukum
2008-08-22 14:31     ` Marcel Holtmann
2008-08-22 14:31     ` Marcel Holtmann
2008-08-22 14:38       ` Oliver Neukum
2008-08-22 14:38       ` Oliver Neukum
2008-08-25 10:43       ` Oliver Neukum
2008-08-25 11:51         ` Marcel Holtmann
2008-08-25 11:51         ` Marcel Holtmann
2009-02-11 16:52           ` [linux-pm] " Matthew Garrett
2009-02-11 16:55             ` Marcel Holtmann
2009-02-11 16:55             ` Marcel Holtmann
2009-02-11 16:52           ` Matthew Garrett
2008-08-26  9:56         ` Pavel Machek
2008-08-26 10:05           ` Pavel Machek
2008-08-26 10:05           ` Pavel Machek
2008-08-26 11:02             ` Oliver Neukum
2008-08-28  8:06               ` Pavel Machek
2008-08-28  8:06               ` Pavel Machek
2008-08-26 11:02             ` Oliver Neukum
2008-08-26  9:56         ` Pavel Machek
2008-08-25 10:43       ` Oliver Neukum
2008-08-25 11:37       ` btusb hibernation/suspend breakage in current -git Rafael J. Wysocki
2008-08-25 11:37       ` Rafael J. Wysocki
2008-08-25 11:53         ` Oliver Neukum
2008-08-25 11:55           ` Marcel Holtmann
2008-08-25 11:55           ` Marcel Holtmann
2008-08-25 11:53         ` Oliver Neukum
2008-08-25 11:54         ` Marcel Holtmann
2008-08-25 13:50           ` Oliver Neukum
2008-08-26  9:36             ` Rafael J. Wysocki
2008-08-26  9:43               ` Oliver Neukum
2008-08-26  9:43               ` Oliver Neukum
2008-08-26 10:10                 ` Rafael J. Wysocki
2008-08-26 10:10                 ` Rafael J. Wysocki
2008-08-26 11:41                   ` Stefan Seyfried
2008-08-26 18:44                     ` Rafael J. Wysocki
2008-08-26 18:44                     ` Rafael J. Wysocki
2008-08-26 19:53                       ` Oliver Neukum
2008-08-26 23:28                         ` Rafael J. Wysocki
2008-08-26 23:28                         ` Rafael J. Wysocki
2008-08-27  5:22                           ` Marcel Holtmann
2008-08-27  9:10                             ` Pavel Machek
2008-08-27 13:29                               ` Rafael J. Wysocki
2008-08-27 13:29                               ` Rafael J. Wysocki
2008-08-27  9:10                             ` Pavel Machek
2008-08-27  7:55                           ` Oliver Neukum
2008-08-27 13:04                             ` Rafael J. Wysocki
2008-08-27 13:04                             ` Rafael J. Wysocki
2008-08-27 13:09                               ` Oliver Neukum
2008-08-27 13:09                               ` Oliver Neukum
2008-08-27 13:28                                 ` Rafael J. Wysocki
2008-08-27 22:33                                   ` Rafael J. Wysocki
2008-08-27 22:33                                   ` [linux-pm] " Rafael J. Wysocki
2008-08-28  7:17                                     ` Oliver Neukum
2008-09-08 20:49                                       ` Marcel Holtmann
2008-09-08 20:49                                       ` [linux-pm] " Marcel Holtmann
2008-09-08 21:45                                         ` Rafael J. Wysocki
2008-09-08 21:45                                         ` Rafael J. Wysocki
2008-08-27 13:28                                 ` Rafael J. Wysocki
2008-08-27  7:55                           ` Oliver Neukum
2008-08-27  9:56                           ` Marcel Holtmann
2008-08-27  9:56                           ` Marcel Holtmann
2008-08-26 19:53                       ` Oliver Neukum
2008-08-26 11:41                   ` Stefan Seyfried
2008-08-26  9:36             ` Rafael J. Wysocki
2008-08-25 13:50           ` Oliver Neukum
2008-08-25 12:45         ` Pavel Machek
2008-08-25 12:45         ` Pavel Machek
2008-08-22 13:51   ` [rft]autosuspend for btusb Oliver Neukum
2008-08-22 13:33 ` Marcel Holtmann
  -- strict thread matches above, loose matches on Subject: below --
2008-08-22 13:20 Oliver Neukum

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.