USB: Add URB_FREE_BUFFER flag and the logic behind it
[pandora-kernel.git] / drivers / usb / core / urb.c
index 9801d08..52ec44b 100644 (file)
@@ -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)
 static void urb_destroy(struct kref *kref)
 {
        struct urb *urb = to_urb(kref);
+
+       if (urb->transfer_flags & URB_FREE_BUFFER)
+               kfree(urb->transfer_buffer);
+
        kfree(urb);
 }
 
@@ -34,6 +39,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 +106,60 @@ 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);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_urb);
+
+/**
+ * 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);
+       if (unlikely(anchor != urb->anchor)) {
+               /* we've lost the race to another thread */
+               spin_unlock_irqrestore(&anchor->lock, flags);
+               return;
+       }
+       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);
+}
+EXPORT_SYMBOL_GPL(usb_unanchor_urb);
+
 /*-------------------------------------------------------------------*/
 
 /**
@@ -235,16 +293,15 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 
        urb->status = -EINPROGRESS;
        urb->actual_length = 0;
-       urb->bandwidth = 0;
 
        /* Lots of sanity checks, so HCDs can rely on clean data
         * and don't need to duplicate tests
         */
        pipe = urb->pipe;
-       temp = usb_pipetype (pipe);
-       is_out = usb_pipeout (pipe);
+       temp = usb_pipetype(pipe);
+       is_out = usb_pipeout(pipe);
 
-       if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED)
+       if (!usb_pipecontrol(pipe) && dev->state < USB_STATE_CONFIGURED)
                return -ENODEV;
 
        /* FIXME there should be a sharable lock protecting us against
@@ -253,11 +310,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
         * checks get made.)
         */
 
-       max = usb_maxpacket (dev, pipe, is_out);
+       max = usb_maxpacket(dev, pipe, is_out);
        if (max <= 0) {
                dev_dbg(&dev->dev,
                        "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
-                       usb_pipeendpoint (pipe), is_out ? "out" : "in",
+                       usb_pipeendpoint(pipe), is_out ? "out" : "in",
                        __FUNCTION__, max);
                return -EMSGSIZE;
        }
@@ -279,11 +336,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                if (urb->number_of_packets <= 0)                    
                        return -EINVAL;
                for (n = 0; n < urb->number_of_packets; n++) {
-                       len = urb->iso_frame_desc [n].length;
+                       len = urb->iso_frame_desc[n].length;
                        if (len < 0 || len > max) 
                                return -EMSGSIZE;
-                       urb->iso_frame_desc [n].status = -EXDEV;
-                       urb->iso_frame_desc [n].actual_length = 0;
+                       urb->iso_frame_desc[n].status = -EXDEV;
+                       urb->iso_frame_desc[n].actual_length = 0;
                }
        }
 
@@ -322,7 +379,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 
        /* fail if submitter gave bogus flags */
        if (urb->transfer_flags != orig_flags) {
-               err ("BOGUS urb flags, %x --> %x",
+               err("BOGUS urb flags, %x --> %x",
                        orig_flags, urb->transfer_flags);
                return -EINVAL;
        }
@@ -373,7 +430,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                urb->interval = temp;
        }
 
-       return usb_hcd_submit_urb (urb, mem_flags);
+       return usb_hcd_submit_urb(urb, mem_flags);
 }
 
 /*-------------------------------------------------------------------*/
@@ -479,6 +536,48 @@ void usb_kill_urb(struct urb *urb)
        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);
+}
+EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);
+
+/**
+ * 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 milliseconds
+ *
+ * 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,
+                                 unsigned int timeout)
+{
+       return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
+                                 msecs_to_jiffies(timeout));
+}
+EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
 EXPORT_SYMBOL(usb_init_urb);
 EXPORT_SYMBOL(usb_alloc_urb);
 EXPORT_SYMBOL(usb_free_urb);
@@ -486,4 +585,3 @@ EXPORT_SYMBOL(usb_get_urb);
 EXPORT_SYMBOL(usb_submit_urb);
 EXPORT_SYMBOL(usb_unlink_urb);
 EXPORT_SYMBOL(usb_kill_urb);
-