VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / usb / core / hcd.c
index 04a8b32..cd2268a 100644 (file)
@@ -102,6 +102,9 @@ EXPORT_SYMBOL_GPL (usb_bus_list_lock);
 /* used when updating hcd data */
 static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
 
+/* wait queue for synchronous unlinks */
+DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -326,7 +329,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 {
        struct usb_ctrlrequest *cmd;
        u16             typeReq, wValue, wIndex, wLength;
-       const u8        *bufp = 0;
+       const u8        *bufp = NULL;
        u8              *ubuf = urb->transfer_buffer;
        int             len = 0;
        int             patch_wakeup = 0;
@@ -536,7 +539,7 @@ static void rh_report_status (unsigned long ptr)
                hcd->rh_timer.data = 0;
                urb->actual_length = length;
                urb->status = 0;
-               urb->hcpriv = 0;
+               urb->hcpriv = NULL;
        } else
                mod_timer (&hcd->rh_timer, jiffies + HZ/4);
        spin_unlock (&hcd_data_lock);
@@ -569,7 +572,7 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
 
 /*-------------------------------------------------------------------------*/
 
-void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
+int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
        unsigned long   flags;
 
@@ -578,9 +581,10 @@ void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
        hcd->rh_timer.data = 0;
 
        local_irq_save (flags);
-       urb->hcpriv = 0;
+       urb->hcpriv = NULL;
        usb_hcd_giveback_urb (hcd, urb, NULL);
        local_irq_restore (flags);
+       return 0;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -622,9 +626,9 @@ static struct class usb_host_class = {
        .release        = &usb_host_release,
 };
 
-void usb_host_init(void)
+int usb_host_init(void)
 {
-       class_register(&usb_host_class);
+       return class_register(&usb_host_class);
 }
 
 void usb_host_cleanup(void)
@@ -764,8 +768,9 @@ EXPORT_SYMBOL (usb_deregister_bus);
  *
  * The USB host controller calls this function to register the root hub
  * properly with the USB subsystem.  It sets up the device properly in
- * the device model tree, and then calls usb_new_device() to register the
- * usb device.  It also assigns the root hub's USB address (always 1).
+ * the device tree and stores the root_hub pointer in the bus structure,
+ * then calls usb_new_device() to register the usb device.  It also
+ * assigns the root hub's USB address (always 1).
  */
 int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev)
 {
@@ -777,7 +782,10 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev
        memset (&usb_dev->bus->devmap.devicemap, 0,
                        sizeof usb_dev->bus->devmap.devicemap);
        set_bit (devnum, usb_dev->bus->devmap.devicemap);
-       usb_dev->state = USB_STATE_ADDRESS;
+       usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
+
+       down (&usb_bus_list_lock);
+       usb_dev->bus->root_hub = usb_dev;
 
        usb_dev->epmaxpacketin[0] = usb_dev->epmaxpacketout[0] = 64;
        retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
@@ -787,14 +795,15 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev
                return (retval < 0) ? retval : -EMSGSIZE;
        }
 
-       (void) usb_get_dev (usb_dev);
        down (&usb_dev->serialize);
        retval = usb_new_device (usb_dev);
-       if (retval)
+       up (&usb_dev->serialize);
+       if (retval) {
+               usb_dev->bus->root_hub = NULL;
                dev_err (parent_dev, "can't register root hub for %s, %d\n",
                                usb_dev->dev.bus_id, retval);
-       up (&usb_dev->serialize);
-       usb_put_dev (usb_dev);
+       }
+       up (&usb_bus_list_lock);
        return retval;
 }
 EXPORT_SYMBOL (usb_register_root_hub);
@@ -1024,7 +1033,6 @@ static int hcd_alloc_dev (struct usb_device *udev)
 static void urb_unlink (struct urb *urb)
 {
        unsigned long           flags;
-       struct usb_device       *dev;
 
        /* Release any periodic transfer bandwidth */
        if (urb->bandwidth)
@@ -1035,9 +1043,8 @@ static void urb_unlink (struct urb *urb)
 
        spin_lock_irqsave (&hcd_data_lock, flags);
        list_del_init (&urb->urb_list);
-       dev = urb->dev;
        spin_unlock_irqrestore (&hcd_data_lock, flags);
-       usb_put_dev (dev);
+       usb_put_dev (urb->dev);
 }
 
 
@@ -1074,23 +1081,28 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
        // FIXME:  verify that quiescing hc works right (RH cleans up)
 
        spin_lock_irqsave (&hcd_data_lock, flags);
-       if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {
+       if (unlikely (urb->reject))
+               status = -EPERM;
+       else if (HCD_IS_RUNNING (hcd->state) &&
+                       hcd->state != USB_STATE_QUIESCING) {
                usb_get_dev (urb->dev);
                list_add_tail (&urb->urb_list, &dev->urb_list);
                status = 0;
-       } else {
-               INIT_LIST_HEAD (&urb->urb_list);
+       } else
                status = -ESHUTDOWN;
-       }
        spin_unlock_irqrestore (&hcd_data_lock, flags);
-       if (status)
+       if (status) {
+               INIT_LIST_HEAD (&urb->urb_list);
                return status;
+       }
 
        /* increment urb's reference count as part of giving it to the HCD
         * (which now controls it).  HCD guarantees that it either returns
         * an error or calls giveback(), but not both.
         */
        urb = usb_get_urb (urb);
+       atomic_inc (&urb->use_count);
+
        if (urb->dev == hcd->self.root_hub) {
                /* NOTE:  requirement on hub callers (usbfs and the hub
                 * driver, for now) that URBs' urb->transfer_buffer be
@@ -1127,9 +1139,12 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
 
        status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
 done:
-       if (status) {
-               usb_put_urb (urb);
+       if (unlikely (status)) {
                urb_unlink (urb);
+               atomic_dec (&urb->use_count);
+               if (urb->reject)
+                       wake_up (&usb_kill_urb_queue);
+               usb_put_urb (urb);
        }
        return status;
 }
@@ -1152,60 +1167,39 @@ static int hcd_get_frame_number (struct usb_device *udev)
  * soon as practical.  we've already set up the urb's return status,
  * but we can't know if the callback completed already.
  */
-static void
+static int
 unlink1 (struct usb_hcd *hcd, struct urb *urb)
 {
+       int             value;
+
        if (urb == (struct urb *) hcd->rh_timer.data)
-               usb_rh_status_dequeue (hcd, urb);
+               value = usb_rh_status_dequeue (hcd, urb);
        else {
-               int             value;
 
-               /* failures "should" be harmless */
+               /* The only reason an HCD might fail this call is if
+                * it has not yet fully queued the urb to begin with.
+                * Such failures should be harmless. */
                value = hcd->driver->urb_dequeue (hcd, urb);
-               if (value != 0)
-                       dev_dbg (hcd->self.controller,
-                               "dequeue %p --> %d\n",
-                               urb, value);
        }
-}
-
-struct completion_splice {             // modified urb context:
-       /* did we complete? */
-       struct completion       done;
 
-       /* original urb data */
-       usb_complete_t          complete;
-       void                    *context;
-};
-
-static void unlink_complete (struct urb *urb, struct pt_regs *regs)
-{
-       struct completion_splice        *splice;
-
-       splice = (struct completion_splice *) urb->context;
-
-       /* issue original completion call */
-       urb->complete = splice->complete;
-       urb->context = splice->context;
-       urb->complete (urb, regs);
-
-       /* then let the synchronous unlink call complete */
-       complete (&splice->done);
+       if (value != 0)
+               dev_dbg (hcd->self.controller, "dequeue %p --> %d\n",
+                               urb, value);
+       return value;
 }
 
 /*
- * called in any context; note ASYNC_UNLINK restrictions
+ * called in any context
  *
  * caller guarantees urb won't be recycled till both unlink()
  * and the urb's completion function return
  */
-static int hcd_unlink_urb (struct urb *urb)
+static int hcd_unlink_urb (struct urb *urb, int status)
 {
        struct hcd_dev                  *dev;
-       struct usb_hcd                  *hcd = 0;
-       struct device                   *sys = 0;
+       struct usb_hcd                  *hcd = NULL;
+       struct device                   *sys = NULL;
        unsigned long                   flags;
-       struct completion_splice        splice;
        struct list_head                *tmp;
        int                             retval;
 
@@ -1257,8 +1251,6 @@ static int hcd_unlink_urb (struct urb *urb)
 
        /* Any status except -EINPROGRESS means something already started to
         * unlink this URB from the hardware.  So there's no more work to do.
-        *
-        * FIXME use better explicit urb state
         */
        if (urb->status != -EINPROGRESS) {
                retval = -EBUSY;
@@ -1276,62 +1268,19 @@ static int hcd_unlink_urb (struct urb *urb)
                hcd->saw_irq = 1;
        }
 
-       /* maybe set up to block until the urb's completion fires.  the
-        * lower level hcd code is always async, locking on urb->status
-        * updates; an intercepted completion unblocks us.
-        */
-       if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
-               if (in_interrupt ()) {
-                       dev_dbg (hcd->self.controller, 
-                               "non-async unlink in_interrupt");
-                       retval = -EWOULDBLOCK;
-                       goto done;
-               }
-               /* synchronous unlink: block till we see the completion */
-               init_completion (&splice.done);
-               splice.complete = urb->complete;
-               splice.context = urb->context;
-               urb->complete = unlink_complete;
-               urb->context = &splice;
-               urb->status = -ENOENT;
-       } else {
-               /* asynchronous unlink */
-               urb->status = -ECONNRESET;
-       }
+       urb->status = status;
+
        spin_unlock (&hcd_data_lock);
        spin_unlock_irqrestore (&urb->lock, flags);
 
-       // FIXME remove splicing, so this becomes unlink1 (hcd, urb);
-       if (urb == (struct urb *) hcd->rh_timer.data) {
-               usb_rh_status_dequeue (hcd, urb);
-               retval = 0;
-       } else {
-               retval = hcd->driver->urb_dequeue (hcd, urb);
-
-               /* hcds shouldn't really fail these calls, but... */
-               if (retval) {
-                       dev_dbg (sys, "dequeue %p --> %d\n", urb, retval);
-                       if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
-                               spin_lock_irqsave (&urb->lock, flags);
-                               urb->complete = splice.complete;
-                               urb->context = splice.context;
-                               spin_unlock_irqrestore (&urb->lock, flags);
-                       }
-                       goto bye;
-               }
-       }
-
-       /* block till giveback, if needed */
-       if (urb->transfer_flags & URB_ASYNC_UNLINK)
-               return -EINPROGRESS;
-
-       wait_for_completion (&splice.done);
-       return 0;
+       retval = unlink1 (hcd, urb);
+       if (retval == 0)
+               retval = -EINPROGRESS;
+       return retval;
 
 done:
        spin_unlock (&hcd_data_lock);
        spin_unlock_irqrestore (&urb->lock, flags);
-bye:
        if (retval != -EIDRM && sys && sys->driver)
                dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
        return retval;
@@ -1432,6 +1381,32 @@ rescan:
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_USB_SUSPEND
+
+static int hcd_hub_suspend (struct usb_bus *bus)
+{
+       struct usb_hcd          *hcd;
+
+       hcd = container_of (bus, struct usb_hcd, self);
+       if (hcd->driver->hub_suspend)
+               return hcd->driver->hub_suspend (hcd);
+       return 0;
+}
+
+static int hcd_hub_resume (struct usb_bus *bus)
+{
+       struct usb_hcd          *hcd;
+
+       hcd = container_of (bus, struct usb_hcd, self);
+       if (hcd->driver->hub_resume)
+               return hcd->driver->hub_resume (hcd);
+       return 0;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 /* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup.
  * we're guaranteed that the device is fully quiesced.  also, that each
  * endpoint has been hcd_endpoint_disabled.
@@ -1486,6 +1461,10 @@ struct usb_operations usb_hcd_operations = {
        .buffer_alloc =         hcd_buffer_alloc,
        .buffer_free =          hcd_buffer_free,
        .disable =              hcd_endpoint_disable,
+#ifdef CONFIG_USB_SUSPEND
+       .hub_suspend =          hcd_hub_suspend,
+       .hub_resume =           hcd_hub_resume,
+#endif
 };
 EXPORT_SYMBOL (usb_hcd_operations);
 
@@ -1531,6 +1510,9 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs
 
        /* pass ownership to the completion handler */
        urb->complete (urb, regs);
+       atomic_dec (&urb->use_count);
+       if (unlikely (urb->reject))
+               wake_up (&usb_kill_urb_queue);
        usb_put_urb (urb);
 }
 EXPORT_SYMBOL (usb_hcd_giveback_urb);
@@ -1574,11 +1556,13 @@ static void hcd_panic (void *_hcd)
        unsigned                i;
 
        /* hc's root hub is removed later removed in hcd->stop() */
-       hub->state = USB_STATE_NOTATTACHED;
+       down (&hub->serialize);
+       usb_set_device_state(hub, USB_STATE_NOTATTACHED);
        for (i = 0; i < hub->maxchild; i++) {
                if (hub->children [i])
                        usb_disconnect (&hub->children [i]);
        }
+       up (&hub->serialize);
 }
 
 /**