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/
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to