Am Freitag, 4. Mai 2007 06:47 schrieb Greg KH:
> On Thu, May 03, 2007 at 11:05:54AM -0400, Alan Stern wrote:
> > On Thu, 3 May 2007, Oliver Neukum wrote:
> > 
> > > Am Donnerstag, 3. Mai 2007 16:03 schrieb Alan Stern:
> > > > On Thu, 3 May 2007, Oliver Neukum wrote:
> > > > 
> > > > > > > How about exporting usb_disable_interface() and 
> > > > > > > usb_enable_interface()?
> > > > > > > It doesn't solve the problem for endpoint 0, but this case is 
> > > > > > > rare.
> > > > > > 
> > > > > > We could; it would save drivers the trouble of keeping track of 
> > > > > > their own 
> > > > > > URBs.  Do you have a particular driver in mind for this?
> > > > > 
> > > > > Everything derived from newer versions of the skeleton driver.
> > > > 
> > > > Would we be better off modifying the skeleton driver to keep track of 
> > > > its 
> > > > own URBs?
> > > 
> > > I am unsure. The "fire-and-forget" technique is certainly simple for
> > > driver writers. Perhaps we should make it easier to keep track of URBs.
> > > A list_head in the URB?
> > 
> > People have discussed adding one in the past.  Greg might have some 
> > opinions.
> 
> My opinion is that we need to ensure that we can still do the
> "fire-and-forget" type driver :)

And this can be done. What do you think:

--- a/include/linux/usb.h       2007-05-04 10:50:18.000000000 +0200
+++ b/include/linux/usb.h       2007-05-04 14:10:49.000000000 +0200
@@ -958,11 +958,23 @@ struct usb_iso_packet_descriptor {
 
 struct urb;
 
+struct usb_anchor {
+       struct list_head urb_list;
+       spinlock_t lock;
+};
+
+static inline void init_usb_anchor(struct usb_anchor *anchor)
+{
+       INIT_LIST_HEAD(&anchor->urb_list);
+       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 +1147,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 +1284,8 @@ 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_associate_urb_to_anchor(struct urb *u, struct usb_anchor *a);
+extern void usb_kill_associated_urbs(struct usb_anchor *old, struct usb_anchor 
*new);
 
 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-04 12:16:28.000000000 +0200
@@ -1408,6 +1408,15 @@ void usb_hcd_giveback_urb (struct usb_hc
        }
 
        usbmon_urb_complete (&hcd->self, urb);
+       if (urb->anchor) {
+               struct usb_anchor *anchor = urb->anchor;
+
+               spin_lock(&anchor->lock);
+               list_del(&urb->anchor_list);
+               urb->anchor = NULL;
+               spin_unlock(&anchor->lock);
+       }
+
        /* 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-04 14:28:29.000000000 +0200
@@ -11,6 +11,14 @@
 static void urb_destroy(struct kref *kref)
 {
        struct urb *urb = to_urb(kref);
+       unsigned long flags;
+
+       if (urb->anchor) {
+               spin_lock_irqsave(&urb->anchor->lock, flags);
+               list_del(&urb->anchor_list);
+               spin_unlock_irqrestore(&urb->anchor->lock, flags);
+       }
+       
        kfree(urb);
 }
 
@@ -34,6 +42,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,6 +109,25 @@ 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);
+}
                
                
 /*-------------------------------------------------------------------*/
@@ -477,12 +505,45 @@ 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);
+}
 
 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);
 
--- a/drivers/usb/usb-skeleton.c        2007-05-04 13:07:30.000000000 +0200
+++ b/drivers/usb/usb-skeleton.c        2007-05-04 14:11:50.000000000 +0200
@@ -54,10 +54,13 @@ 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 */
+       struct usb_anchor       redo;                   /* to be redone in case 
of suspend or reset */
        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 */
+       unsigned char           revoking;               /* this is set while 
submissions are revoked */
        struct kref             kref;
        struct mutex            io_mutex;               /* synchronize I/O with 
disconnect */
 };
@@ -185,11 +188,17 @@ static void skel_write_bulk_callback(str
              urb->status == -ESHUTDOWN)) {
                err("%s - nonzero write bulk status received: %d",
                    __FUNCTION__, urb->status);
+               /* the device is to be suspend,
+                  this urb must be redone,
+                  the buffer cannot be freed */
+               if (urb->status == -ENOENT && dev->revoking)
+                       goto spare_buffer;
        }
 
        /* free up our allocated buffer */
        usb_buffer_free(urb->dev, urb->transfer_buffer_length,
                        urb->transfer_buffer, urb->transfer_dma);
+spare_buffer:
        up(&dev->limit_sem);
 }
 
@@ -244,6 +253,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 +316,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);
+       init_usb_anchor(&dev->submitted);
+       init_usb_anchor(&dev->redo);
 
        dev->udev = usb_get_dev(interface_to_usbdev(interface));
        dev->interface = interface;
@@ -391,10 +403,57 @@ 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);
+
+       mutex_lock(&dev->io_mutex);
+       dev->revoking = 1;
+       usb_kill_associated_urbs(&dev->submitted, &dev->redo);
+       mutex_unlock(&dev->io_mutex);
+
+       return 0;
+}
+
+static int skel_resume (struct usb_interface *intf)
+{
+       struct usb_skel *dev= usb_get_intfdata(intf);
+       struct urb *lazarus, *n;
+       int r;
+
+       mutex_lock(&dev->io_mutex);
+       dev->revoking = 0;
+       list_for_each_entry_safe(lazarus, n, &dev->redo.urb_list, anchor_list) {
+               list_del(&lazarus->anchor_list);
+               usb_associate_urb_to_anchor(lazarus, &dev->submitted);
+               r = usb_submit_urb(lazarus, GFP_NOIO);
+               if (r < 0) {
+                       /* if URB submission fails, it, its buffer and those
+                          after it and their buffers must be freed */
+                       err("Could not redo URBs during resume");
+                       usb_buffer_free(lazarus->dev, 
lazarus->transfer_buffer_length,
+                       lazarus->transfer_buffer, lazarus->transfer_dma);
+                       usb_free_urb(lazarus);
+                       list_for_each_entry_safe(lazarus, n, 
&dev->redo.urb_list, anchor_list) {
+                               usb_buffer_free(lazarus->dev, 
lazarus->transfer_buffer_length,
+                               lazarus->transfer_buffer, 
lazarus->transfer_dma);
+                               usb_free_urb(lazarus);  
+                       }
+                       mutex_unlock(&dev->io_mutex);
+                       return r;
+               }
+               usb_free_urb(lazarus);
+       }
+       mutex_unlock(&dev->io_mutex);
+       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