Hi Alan, list,

here's the patch to introduce anchors for use with "fire-and-forget"
techniques and a modification for the skeleton driver to use them.
This should make implementing pre/post_reset and suspend/resume
much easier.

        Regards
                Oliver
-- 

--- a/include/linux/usb.h       2007-05-04 10:50:18.000000000 +0200
+++ b/include/linux/usb.h       2007-05-08 12:47:27.000000000 +0200
@@ -958,11 +958,26 @@ 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_list: membership in the list of an anchor
+ * @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 +1150,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 +1287,10 @@ 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_anchored_urbs(struct usb_anchor *anchor);
+extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor);
+extern void usb_unanchor_urb(struct urb *urb);
+extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, int 
timeout);
 
 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-16 11:32:15.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,7 @@
 static void urb_destroy(struct kref *kref)
 {
        struct urb *urb = to_urb(kref);
+
        kfree(urb);
 }
 
@@ -34,6 +36,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 +103,55 @@ struct urb * usb_get_urb(struct urb *urb
                kref_get(&urb->kref);
        return urb;
 }
-               
-               
+
+/**
+ * usb_anchor_urb - anchors an URB while it is processed
+ * @urb: pointer to the urb to anchor
+ * @anchor: 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_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&anchor->lock, flags);
+       usb_get_urb(urb);
+       list_add_tail(&urb->anchor_list, &anchor->urb_list);
+       urb->anchor = anchor;
+       spin_unlock_irqrestore(&anchor->lock, flags);
+}
+
+/**
+ * usb_unanchor_urb - unanchors an URB
+ * @urb: pointer to the urb to anchor
+ *
+ * Call this to stop the system keeping track of this URB
+ */
+
+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);
+       usb_put_urb(urb);
+       if (list_empty(&anchor->urb_list))
+               wake_up(&anchor->wait);
+}
+
 /*-------------------------------------------------------------------*/
 
 /**
@@ -477,12 +527,56 @@ void usb_kill_urb(struct urb *urb)
        --urb->reject;
        spin_unlock_irq(&urb->lock);
 }
+/**
+ * usb_kill_anchored_urbs - cancel transfer requests en masse
+ * @anchor: anchor the requests are bound to
+ *
+ * this allows all outstanding URBs to be killed starting
+ * from the back of the queue
+ */
+
+void usb_kill_anchored_urbs(struct usb_anchor *anchor)
+{
+       struct urb *victim;
+
+       spin_lock_irq(&anchor->lock);
+       while (!list_empty(&anchor->urb_list)) {
+               victim = list_entry(anchor->urb_list.prev, struct urb, 
anchor_list);
+               /* we must make sure the URB isn't freed before we kill it*/
+               usb_get_urb(victim);
+               spin_unlock_irq(&anchor->lock);
+               /* this will unanchor the URB */
+               usb_kill_urb(victim);
+               usb_put_urb(victim);
+               spin_lock_irq(&anchor->lock);
+       }
+       spin_unlock_irq(&anchor->lock);
+}
+
+/**
+ * usb_wait_anchor_empty_timeout - wait for an anchor to be unused
+ * @anchor: the anchor you want to become unused
+ * @timeout: how long you are willing to wait in jiffies
+ *
+ * Call this is you want to be sure all an anchor's
+ * URBs have finished
+ */
+
+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;
+}
 
 EXPORT_SYMBOL(usb_init_urb);
 EXPORT_SYMBOL(usb_alloc_urb);
 EXPORT_SYMBOL(usb_free_urb);
 EXPORT_SYMBOL(usb_get_urb);
+EXPORT_SYMBOL(usb_anchor_urb);
 EXPORT_SYMBOL(usb_submit_urb);
 EXPORT_SYMBOL(usb_unlink_urb);
 EXPORT_SYMBOL(usb_kill_urb);
-
+EXPORT_SYMBOL(usb_kill_anchored_urbs);
+EXPORT_SYMBOL(usb_wait_anchor_empty_timeout);
--- a/drivers/usb/usb-skeleton.c        2007-05-04 13:07:30.000000000 +0200
+++ b/drivers/usb/usb-skeleton.c        2007-05-16 11:18:22.000000000 +0200
@@ -54,16 +54,20 @@ 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 */
        __u8                    bulk_out_endpointAddr;  /* the address of the 
bulk out endpoint */
+       int                     errors;                 /* the last request 
tanked */
+       spinlock_t              err_lock;               /* lock for errors */
        struct kref             kref;
        struct mutex            io_mutex;               /* synchronize I/O with 
disconnect */
 };
 #define to_skel_dev(d) container_of(d, struct usb_skel, kref)
 
 static struct usb_driver skel_driver;
+static void skel_draw_down(struct usb_skel *dev);
 
 static void skel_delete(struct kref *kref)
 {
@@ -102,18 +106,22 @@ static int skel_open(struct inode *inode
 
        /* increment our usage count for the device */
        kref_get(&dev->kref);
-       /* now we can drop the lock */
+       /* to guard against an ongoing reset */
+       mutex_lock(&dev->io_mutex);
+       /* now we can drop the common lock */
        mutex_unlock(&skel_open_lock);
 
        /* prevent the device from being autosuspended */
        retval = usb_autopm_get_interface(interface);
        if (retval) {
+               mutex_unlock(&dev->io_mutex);
                kref_put(&dev->kref, skel_delete);
                goto exit;
        }
 
        /* save our object in the file's private structure */
        file->private_data = dev;
+       mutex_unlock(&dev->io_mutex);
 
 exit:
        return retval;
@@ -138,6 +146,29 @@ static int skel_release(struct inode *in
        return 0;
 }
 
+static int skel_flush(struct file *file, fl_owner_t id)
+{
+       struct usb_skel *dev;
+       unsigned long flags;
+       int res;
+
+       dev = (struct usb_skel *)file->private_data;
+       if (dev == NULL)
+               return -ENODEV;
+
+       /* wait for io to stop */
+       skel_draw_down(dev);
+
+       /* read out errors, leave subsequent opens a clean slate */
+       spin_lock_irqsave(&dev->err_lock, flags);
+       res = dev->errors ? -EIO : 0;
+       if (res)
+               dev->errors = 0;
+       spin_unlock_irqrestore(&dev->err_lock, flags);
+
+       return res;
+}
+
 static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t 
*ppos)
 {
        struct usb_skel *dev;
@@ -179,14 +210,17 @@ static void skel_write_bulk_callback(str
        dev = (struct usb_skel *)urb->context;
 
        /* sync/async unlink faults aren't errors */
-       if (urb->status &&
-           !(urb->status == -ENOENT ||
-             urb->status == -ECONNRESET ||
-             urb->status == -ESHUTDOWN)) {
-               err("%s - nonzero write bulk status received: %d",
-                   __FUNCTION__, urb->status);
+       if (urb->status) {
+               if (!(urb->status == -ENOENT ||
+                     urb->status == -ECONNRESET ||
+                     urb->status == -ESHUTDOWN)) {
+                       err("%s - nonzero write bulk status received: %d",
+                       __FUNCTION__, urb->status);
+               }
+               spin_lock(&dev->err_lock);
+               dev->errors = urb->status;
+               spin_unlock(&dev->err_lock);
        }
-
        /* free up our allocated buffer */
        usb_buffer_free(urb->dev, urb->transfer_buffer_length,
                        urb->transfer_buffer, urb->transfer_dma);
@@ -213,6 +247,15 @@ static ssize_t skel_write(struct file *f
                goto exit;
        }
 
+       spin_lock_irq(&dev->err_lock);
+       if (dev->errors < 0) {
+               dev->errors = 0;
+               retval = -EIO;
+       }
+       spin_unlock_irq(&dev->err_lock);
+       if (retval < 0)
+               goto error;
+
        /* create a urb, and a buffer for it, and copy the data to the urb */
        urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!urb) {
@@ -244,13 +287,14 @@ 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_anchor_urb(urb, &dev->submitted);
 
        /* send the data out the bulk port */
        retval = usb_submit_urb(urb, GFP_KERNEL);
        mutex_unlock(&dev->io_mutex);
        if (retval) {
                err("%s - failed submitting write urb, error %d", __FUNCTION__, 
retval);
-               goto error;
+               goto error_unanchor;
        }
 
        /* release our reference to this urb, the USB core will eventually free 
it entirely */
@@ -259,6 +303,8 @@ static ssize_t skel_write(struct file *f
 
        return writesize;
 
+error_unanchor:
+       usb_unanchor_urb(urb);
 error:
        if (urb) {
                usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
@@ -276,6 +322,7 @@ static const struct file_operations skel
        .write =        skel_write,
        .open =         skel_open,
        .release =      skel_release,
+       .flush =        skel_flush,
 };
 
 /*
@@ -306,6 +353,8 @@ static int skel_probe(struct usb_interfa
        kref_init(&dev->kref);
        sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
        mutex_init(&dev->io_mutex);
+       spin_lock_init(&dev->err_lock);
+       init_usb_anchor(&dev->submitted);
 
        dev->udev = usb_get_dev(interface_to_usbdev(interface));
        dev->interface = interface;
@@ -383,7 +432,7 @@ static void skel_disconnect(struct usb_i
        dev->interface = NULL;
        mutex_unlock(&dev->io_mutex);
 
-
+       usb_kill_anchored_urbs(&dev->submitted);
 
        /* decrement our usage count */
        kref_put(&dev->kref, skel_delete);
@@ -391,10 +440,60 @@ static void skel_disconnect(struct usb_i
        info("USB Skeleton #%d now disconnected", minor);
 }
 
+static void __skel_draw_down(struct usb_skel *dev)
+{
+       int time;
+
+       time = usb_wait_anchor_empty_timeout(&dev->submitted, 1 * HZ);
+       if (!time)
+               usb_kill_anchored_urbs(&dev->submitted);
+}
+
+static void skel_draw_down(struct usb_skel *dev)
+{
+       mutex_lock(&dev->io_mutex);
+       __skel_draw_down(dev);
+       mutex_unlock(&dev->io_mutex);
+}
+
+static int skel_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct usb_skel *dev = usb_get_intfdata(intf);
+
+       __skel_draw_down(dev);
+       return 0;
+}
+
+static void skel_pre_reset(struct usb_interface *intf)
+{
+       struct usb_skel *dev = usb_get_intfdata(intf);
+
+       mutex_lock(&dev->io_mutex);
+       __skel_draw_down(dev);
+}
+
+static int skel_resume (struct usb_interface *intf)
+{
+       return 0;
+}
+
+static void skel_post_reset(struct usb_interface *intf)
+{
+       struct usb_skel *dev = usb_get_intfdata(intf);
+
+       /* we are sure no URBs are active - no locking needed */
+       dev->errors = -EPIPE;
+       mutex_unlock(&dev->io_mutex);
+}
+
 static struct usb_driver skel_driver = {
        .name =         "skeleton",
        .probe =        skel_probe,
        .disconnect =   skel_disconnect,
+       .suspend =      skel_suspend,
+       .resume =       skel_resume,
+       .pre_reset =    skel_pre_reset,
+       .post_reset =   skel_post_reset,
        .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