Re: requirements for suspend()

Oliver Neukum <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
Am Samstag, 5. Mai 2007 17:30 schrieb Alan Stern:

> The bound isn't on the time a device may have data -- it's on the time 
> allowed for a transfer to complete.  And the upper bound would be imposed 
> by the driver.  How long is it willing to hold up suspend processing 
> because of ongoing I/O?  The driver must decide one way or another.

OK, what do you think of this approach?

	Regards
		Oliver

-----

--- a/include/linux/usb.h	2007-05-04 10:50:18.000000000 +0200
+++ b/include/linux/usb.h	2007-05-07 14:48:09.000000000 +0200
@@ -958,11 +958,25 @@ struct usb_iso_packet_descriptor {
 
 struct urb;
 
+struct usb_anchor {
+	struct list_head urb_list;
+	wait_queue_head_t wait;
+	spinlock_t lock;
+};
+
+static inline void init_usb_anchor(struct usb_anchor *anchor)
+{
+	INIT_LIST_HEAD(&anchor->urb_list);
+	init_waitqueue_head(&anchor->wait);
+	spin_lock_init(&anchor->lock);
+}
+
 typedef void (*usb_complete_t)(struct urb *);
 
 /**
  * struct urb - USB Request Block
  * @urb_list: For use by current owner of the URB.
+ * @anchor: to anchor URBs to a common mooring
  * @pipe: Holds endpoint number, direction, type, and more.
  *	Create these values with the eight macros available;
  *	usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl"
@@ -1135,6 +1149,8 @@ struct urb
 	/* public: documented fields in the urb that can be used by drivers */
 	struct list_head urb_list;	/* list head for use by the urb's
 					 * current owner */
+	struct list_head anchor_list;	/* the URB may be anchored by the driver */
+	struct usb_anchor *anchor;
 	struct usb_device *dev; 	/* (in) pointer to associated device */
 	unsigned int pipe;		/* (in) pipe information */
 	int status;			/* (return) non-ISO status */
@@ -1270,6 +1286,11 @@ extern struct urb *usb_get_urb(struct ur
 extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
 extern int usb_unlink_urb(struct urb *urb);
 extern void usb_kill_urb(struct urb *urb);
+extern void usb_kill_associated_urbs(struct usb_anchor *old, struct usb_anchor *new);
+extern void usb_associate_urb_to_anchor(struct urb *u, struct usb_anchor *a);
+extern void usb_unanchor_urb(struct urb *urb);
+extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, int timeout);
+extern void usb_free_anchored_urbs(struct usb_anchor *anchor);
 
 void *usb_buffer_alloc (struct usb_device *dev, size_t size,
 	gfp_t mem_flags, dma_addr_t *dma);
--- a/drivers/usb/core/hcd.c	2007-05-04 12:04:29.000000000 +0200
+++ b/drivers/usb/core/hcd.c	2007-05-07 15:19:01.000000000 +0200
@@ -1408,6 +1408,8 @@ void usb_hcd_giveback_urb (struct usb_hc
 	}
 
 	usbmon_urb_complete (&hcd->self, urb);
+	usb_unanchor_urb(urb);
+
 	/* pass ownership to the completion handler */
 	urb->complete (urb);
 	atomic_dec (&urb->use_count);
--- a/drivers/usb/core/urb.c	2007-05-04 10:53:14.000000000 +0200
+++ b/drivers/usb/core/urb.c	2007-05-07 14:48:12.000000000 +0200
@@ -4,6 +4,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/usb.h>
+#include <linux/wait.h>
 #include "hcd.h"
 
 #define to_urb(d) container_of(d, struct urb, kref)
@@ -11,6 +12,9 @@
 static void urb_destroy(struct kref *kref)
 {
 	struct urb *urb = to_urb(kref);
+	unsigned long flags;
+
+	usb_unanchor_urb(urb);
 	kfree(urb);
 }
 
@@ -34,6 +38,7 @@ void usb_init_urb(struct urb *urb)
 		memset(urb, 0, sizeof(*urb));
 		kref_init(&urb->kref);
 		spin_lock_init(&urb->lock);
+		INIT_LIST_HEAD(&urb->anchor_list);
 	}
 }
 
@@ -100,8 +105,46 @@ struct urb * usb_get_urb(struct urb *urb
 		kref_get(&urb->kref);
 	return urb;
 }
-		
-		
+
+/**
+ * usb_associate_urb_to_anchor - anchors an URB while it is processed
+ * @u: pointer to the urb to anchor
+ * @a: pointer to the anchor
+ *
+ * This can be called to have access to URBs which are to be executed
+ * without bothering to track them
+ */
+
+void usb_associate_urb_to_anchor(struct urb *u, struct usb_anchor *a)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&a->lock, flags);
+	list_add_tail(&u->anchor_list, &a->urb_list);
+	u->anchor = a;
+	spin_unlock_irqrestore(&a->lock, flags);
+}
+
+void usb_unanchor_urb(struct urb *urb)
+{
+	unsigned long flags;
+	struct usb_anchor *anchor;
+
+	if (!urb)
+		return;
+
+	anchor = urb->anchor;
+	if (!anchor)
+		return;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	urb->anchor = NULL;
+	list_del(&urb->anchor_list);
+	spin_unlock_irqrestore(&anchor->lock, flags);
+	if (list_empty(&anchor->urb_list))
+		wake_up(&anchor->wait);
+}
+
 /*-------------------------------------------------------------------*/
 
 /**
@@ -477,12 +520,67 @@ void usb_kill_urb(struct urb *urb)
 	--urb->reject;
 	spin_unlock_irq(&urb->lock);
 }
+/**
+ * usb_kill_associated_urbs - cancel transfer requests en masse
+ * @old: anchor the requests are bound to
+ * @new: anchor the request which were canceled shall be bound to, may be NULL
+ *
+ * this allows all outstanding URBs to be cancelled and bound to an anchor
+ * for later processing
+ */
+
+void usb_kill_associated_urbs(struct usb_anchor *old, struct usb_anchor *new)
+{
+	unsigned long flags;
+	struct urb *victim;
+
+	spin_lock_irqsave(&old->lock, flags);
+	while (!list_empty(&old->urb_list)) {
+		victim = list_entry(old->urb_list.prev, struct urb, anchor_list);
+		list_del(&victim->anchor_list);
+		/* we must make sure the URB isn't freed before we kill it*/
+		usb_get_urb(victim);
+		spin_unlock_irqrestore(&old->lock, flags);
+		usb_kill_urb(victim);
+		/* we may transfer only if we killed */
+		if (new && victim->status == -ENOENT)
+			list_add(&victim->anchor_list, &new->urb_list);
+		else
+			usb_put_urb(victim);
+		spin_lock_irqsave(&old->lock, flags);
+	}
+	spin_unlock_irqrestore(&old->lock, flags);
+}
+
+int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, int timeout)
+{
+	int res;
+
+	res = wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list), timeout);
+	return res;
+}
+
+void usb_free_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *lazarus, *n;
+
+	list_for_each_entry_safe(lazarus, n, &anchor->urb_list, urb_list) {
+		usb_buffer_free(lazarus->dev, lazarus->transfer_buffer_length,
+		lazarus->transfer_buffer, lazarus->transfer_dma);
+		list_del(&lazarus->anchor_list);
+		usb_free_urb(lazarus);	
+	}
+}
 
 EXPORT_SYMBOL(usb_init_urb);
 EXPORT_SYMBOL(usb_alloc_urb);
 EXPORT_SYMBOL(usb_free_urb);
 EXPORT_SYMBOL(usb_get_urb);
+EXPORT_SYMBOL(usb_associate_urb_to_anchor);
 EXPORT_SYMBOL(usb_submit_urb);
 EXPORT_SYMBOL(usb_unlink_urb);
 EXPORT_SYMBOL(usb_kill_urb);
+EXPORT_SYMBOL(usb_kill_associated_urbs);
+EXPORT_SYMBOL(usb_wait_anchor_empty_timeout);
+EXPORT_SYMBOL(usb_free_anchored_urbs);
 
--- a/drivers/usb/usb-skeleton.c	2007-05-04 13:07:30.000000000 +0200
+++ b/drivers/usb/usb-skeleton.c	2007-05-07 15:26:04.000000000 +0200
@@ -54,6 +54,7 @@ struct usb_skel {
 	struct usb_device	*udev;			/* the usb device for this device */
 	struct usb_interface	*interface;		/* the interface for this device */
 	struct semaphore	limit_sem;		/* limiting the number of writes in progress */
+	struct usb_anchor	submitted;		/* in case we need to retract our submissions */
 	unsigned char           *bulk_in_buffer;	/* the buffer to receive data */
 	size_t			bulk_in_size;		/* the size of the receive buffer */
 	__u8			bulk_in_endpointAddr;	/* the address of the bulk in endpoint */
@@ -244,6 +245,7 @@ static ssize_t skel_write(struct file *f
 			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
 			  buf, writesize, skel_write_bulk_callback, dev);
 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	usb_associate_urb_to_anchor(urb, &dev->submitted);
 
 	/* send the data out the bulk port */
 	retval = usb_submit_urb(urb, GFP_KERNEL);
@@ -306,6 +308,7 @@ static int skel_probe(struct usb_interfa
 	kref_init(&dev->kref);
 	sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
 	mutex_init(&dev->io_mutex);
+	init_usb_anchor(&dev->submitted);
 
 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
 	dev->interface = interface;
@@ -391,10 +394,31 @@ static void skel_disconnect(struct usb_i
 	info("USB Skeleton #%d now disconnected", minor);
 }
 
+static int skel_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_skel *dev= usb_get_intfdata(intf);
+	int time;
+
+	mutex_lock(&dev->io_mutex);
+	time = usb_wait_anchor_empty_timeout(&dev->submitted, 1 * HZ);
+	if (!time)
+		usb_kill_associated_urbs(&dev->submitted, NULL);
+	mutex_unlock(&dev->io_mutex);
+
+	return 0;
+}
+
+static int skel_resume (struct usb_interface *intf)
+{
+	return 0;
+}
+
 static struct usb_driver skel_driver = {
 	.name =		"skeleton",
 	.probe =	skel_probe,
 	.disconnect =	skel_disconnect,
+	.suspend =	skel_suspend,
+	.resume =	skel_resume,
 	.id_table =	skel_table,
 	.supports_autosuspend = 1,
 };

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.