]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/core/hcd.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-2.6-omap-h63xx.git] / drivers / usb / core / hcd.c
index e2e00ba4e1e6dd5b20aff7dda22316cd02a39e97..42ef1d5f6c8ad07c78416bdbaa2c3e9c60e7516e 100644 (file)
@@ -22,7 +22,6 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/kernel.h>
@@ -37,6 +36,8 @@
 #include <linux/mutex.h>
 #include <asm/irq.h>
 #include <asm/byteorder.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
 
 #include <linux/usb.h>
 
@@ -45,8 +46,6 @@
 #include "hub.h"
 
 
-// #define USB_BANDWIDTH_MESSAGES
-
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -100,12 +99,17 @@ EXPORT_SYMBOL_GPL (usb_bus_list_lock);
 /* used for controlling access to virtual root hubs */
 static DEFINE_SPINLOCK(hcd_root_hub_lock);
 
-/* used when updating hcd data */
-static DEFINE_SPINLOCK(hcd_data_lock);
+/* used when updating an endpoint's URB list */
+static DEFINE_SPINLOCK(hcd_urb_list_lock);
 
 /* wait queue for synchronous unlinks */
 DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
 
+static inline int is_root_hub(struct usb_device *udev)
+{
+       return (udev->parent == NULL);
+}
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -256,7 +260,9 @@ static const u8 hs_rh_config_descriptor [] = {
        0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
        0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
        0x03,       /*  __u8  ep_bmAttributes; Interrupt */
-       0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+                   /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
+                    * see hub.c:hub_configure() for details. */
+       (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
        0x0c        /*  __u8  ep_bInterval; (256ms -- usb 2.0 spec) */
 };
 
@@ -318,8 +324,8 @@ static int rh_string (
 
        // id 3 == vendor description
        } else if (id == 3) {
-               snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname,
-                       system_utsname.release, hcd->driver->description);
+               snprintf (buf, sizeof buf, "%s %s %s", init_utsname()->sysname,
+                       init_utsname()->release, hcd->driver->description);
 
        // unsupported IDs --> "protocol stall"
        } else
@@ -345,7 +351,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
        struct usb_ctrlrequest *cmd;
        u16             typeReq, wValue, wIndex, wLength;
        u8              *ubuf = urb->transfer_buffer;
-       u8              tbuf [sizeof (struct usb_hub_descriptor)];
+       u8              tbuf [sizeof (struct usb_hub_descriptor)]
+               __attribute__((aligned(4)));
        const u8        *bufp = tbuf;
        int             len = 0;
        int             patch_wakeup = 0;
@@ -521,7 +528,7 @@ error:
        if (urb->status == -EINPROGRESS)
                urb->status = status;
        spin_unlock (&urb->lock);
-       usb_hcd_giveback_urb (hcd, urb, NULL);
+       usb_hcd_giveback_urb (hcd, urb);
        local_irq_restore (flags);
        return 0;
 }
@@ -543,6 +550,8 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
        unsigned long   flags;
        char            buffer[4];      /* Any root hubs with > 31 ports? */
 
+       if (unlikely(!hcd->rh_registered))
+               return;
        if (!hcd->uses_new_polling && !hcd->status_urb)
                return;
 
@@ -571,17 +580,19 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
 
                /* local irqs are always blocked in completions */
                if (length > 0)
-                       usb_hcd_giveback_urb (hcd, urb, NULL);
+                       usb_hcd_giveback_urb (hcd, urb);
                else
                        hcd->poll_pending = 1;
                local_irq_restore (flags);
        }
 
        /* The USB 2.0 spec says 256 ms.  This is close enough and won't
-        * exceed that limit if HZ is 100. */
+        * exceed that limit if HZ is 100. The math is more clunky than
+        * maybe expected, this is to make sure that all timers for USB devices
+        * fire at the same time to give the CPU a break inbetween */
        if (hcd->uses_new_polling ? hcd->poll_rh :
                        (length == 0 && hcd->status_urb != NULL))
-               mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250));
+               mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
 }
 EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
 
@@ -610,8 +621,8 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
                urb->hcpriv = hcd;      /* indicate it's queued */
 
                if (!hcd->uses_new_polling)
-                       mod_timer (&hcd->rh_timer, jiffies +
-                                       msecs_to_jiffies(250));
+                       mod_timer (&hcd->rh_timer,
+                               (jiffies/(HZ/4) + 1) * (HZ/4));
 
                /* If a status change has already occurred, report it ASAP */
                else if (hcd->poll_pending)
@@ -633,31 +644,20 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
 
 /*-------------------------------------------------------------------------*/
 
-/* Asynchronous unlinks of root-hub control URBs are legal, but they
- * don't do anything.  Status URB unlinks must be made in process context
- * with interrupts enabled.
+/* Unlinks of root-hub control URBs are legal, but they don't do anything
+ * since these URBs always execute synchronously.
  */
 static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
-       if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
-               if (in_interrupt())
-                       return 0;               /* nothing to do */
-
-               spin_lock_irq(&urb->lock);      /* from usb_kill_urb */
-               ++urb->reject;
-               spin_unlock_irq(&urb->lock);
-
-               wait_event(usb_kill_urb_queue,
-                               atomic_read(&urb->use_count) == 0);
+       unsigned long   flags;
 
-               spin_lock_irq(&urb->lock);
-               --urb->reject;
-               spin_unlock_irq(&urb->lock);
+       if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
+               ;       /* Do nothing */
 
        } else {                                /* Status URB */
                if (!hcd->uses_new_polling)
-                       del_timer_sync (&hcd->rh_timer);
-               local_irq_disable ();
+                       del_timer (&hcd->rh_timer);
+               local_irq_save (flags);
                spin_lock (&hcd_root_hub_lock);
                if (urb == hcd->status_urb) {
                        hcd->status_urb = NULL;
@@ -666,8 +666,8 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                        urb = NULL;             /* wasn't fully queued */
                spin_unlock (&hcd_root_hub_lock);
                if (urb)
-                       usb_hcd_giveback_urb (hcd, urb, NULL);
-               local_irq_enable ();
+                       usb_hcd_giveback_urb (hcd, urb);
+               local_irq_restore (flags);
        }
 
        return 0;
@@ -675,31 +675,6 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 
 /*-------------------------------------------------------------------------*/
 
-/* exported only within usbcore */
-struct usb_bus *usb_bus_get(struct usb_bus *bus)
-{
-       if (bus)
-               kref_get(&bus->kref);
-       return bus;
-}
-
-static void usb_host_release(struct kref *kref)
-{
-       struct usb_bus *bus = container_of(kref, struct usb_bus, kref);
-
-       if (bus->release)
-               bus->release(bus);
-}
-
-/* exported only within usbcore */
-void usb_bus_put(struct usb_bus *bus)
-{
-       if (bus)
-               kref_put(&bus->kref, usb_host_release);
-}
-
-/*-------------------------------------------------------------------------*/
-
 static struct class *usb_host_class;
 
 int usb_host_init(void)
@@ -731,39 +706,12 @@ static void usb_bus_init (struct usb_bus *bus)
        bus->devnum_next = 1;
 
        bus->root_hub = NULL;
-       bus->hcpriv = NULL;
        bus->busnum = -1;
        bus->bandwidth_allocated = 0;
        bus->bandwidth_int_reqs  = 0;
        bus->bandwidth_isoc_reqs = 0;
 
        INIT_LIST_HEAD (&bus->bus_list);
-
-       kref_init(&bus->kref);
-}
-
-/**
- * usb_alloc_bus - creates a new USB host controller structure
- * @op: pointer to a struct usb_operations that this bus structure should use
- * Context: !in_interrupt()
- *
- * Creates a USB host controller bus structure with the specified 
- * usb_operations and initializes all the necessary internal objects.
- *
- * If no memory is available, NULL is returned.
- *
- * The caller should call usb_put_bus() when it is finished with the structure.
- */
-struct usb_bus *usb_alloc_bus (struct usb_operations *op)
-{
-       struct usb_bus *bus;
-
-       bus = kzalloc (sizeof *bus, GFP_KERNEL);
-       if (!bus)
-               return NULL;
-       usb_bus_init (bus);
-       bus->op = op;
-       return bus;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -898,8 +846,7 @@ void usb_enable_root_hub_irq (struct usb_bus *bus)
        struct usb_hcd *hcd;
 
        hcd = container_of (bus, struct usb_hcd, self);
-       if (hcd->driver->hub_irq_enable && !hcd->poll_rh &&
-                       hcd->state != HC_STATE_HALT)
+       if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT)
                hcd->driver->hub_irq_enable (hcd);
 }
 
@@ -952,136 +899,6 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
 }
 EXPORT_SYMBOL (usb_calc_bus_time);
 
-/*
- * usb_check_bandwidth():
- *
- * old_alloc is from host_controller->bandwidth_allocated in microseconds;
- * bustime is from calc_bus_time(), but converted to microseconds.
- *
- * returns <bustime in us> if successful,
- * or -ENOSPC if bandwidth request fails.
- *
- * FIXME:
- * This initial implementation does not use Endpoint.bInterval
- * in managing bandwidth allocation.
- * It probably needs to be expanded to use Endpoint.bInterval.
- * This can be done as a later enhancement (correction).
- *
- * This will also probably require some kind of
- * frame allocation tracking...meaning, for example,
- * that if multiple drivers request interrupts every 10 USB frames,
- * they don't all have to be allocated at
- * frame numbers N, N+10, N+20, etc.  Some of them could be at
- * N+11, N+21, N+31, etc., and others at
- * N+12, N+22, N+32, etc.
- *
- * Similarly for isochronous transfers...
- *
- * Individual HCDs can schedule more directly ... this logic
- * is not correct for high speed transfers.
- */
-int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
-{
-       unsigned int    pipe = urb->pipe;
-       long            bustime;
-       int             is_in = usb_pipein (pipe);
-       int             is_iso = usb_pipeisoc (pipe);
-       int             old_alloc = dev->bus->bandwidth_allocated;
-       int             new_alloc;
-
-
-       bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso,
-                       usb_maxpacket (dev, pipe, !is_in)));
-       if (is_iso)
-               bustime /= urb->number_of_packets;
-
-       new_alloc = old_alloc + (int) bustime;
-       if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) {
-#ifdef DEBUG
-               char    *mode = 
-#ifdef CONFIG_USB_BANDWIDTH
-                       "";
-#else
-                       "would have ";
-#endif
-               dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n",
-                       mode, old_alloc, bustime, new_alloc);
-#endif
-#ifdef CONFIG_USB_BANDWIDTH
-               bustime = -ENOSPC;      /* report error */
-#endif
-       }
-
-       return bustime;
-}
-EXPORT_SYMBOL (usb_check_bandwidth);
-
-
-/**
- * usb_claim_bandwidth - records bandwidth for a periodic transfer
- * @dev: source/target of request
- * @urb: request (urb->dev == dev)
- * @bustime: bandwidth consumed, in (average) microseconds per frame
- * @isoc: true iff the request is isochronous
- *
- * Bus bandwidth reservations are recorded purely for diagnostic purposes.
- * HCDs are expected not to overcommit periodic bandwidth, and to record such
- * reservations whenever endpoints are added to the periodic schedule.
- *
- * FIXME averaging per-frame is suboptimal.  Better to sum over the HCD's
- * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable
- * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how
- * large its periodic schedule is.
- */
-void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
-{
-       dev->bus->bandwidth_allocated += bustime;
-       if (isoc)
-               dev->bus->bandwidth_isoc_reqs++;
-       else
-               dev->bus->bandwidth_int_reqs++;
-       urb->bandwidth = bustime;
-
-#ifdef USB_BANDWIDTH_MESSAGES
-       dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n",
-               bustime,
-               isoc ? "ISOC" : "INTR",
-               dev->bus->bandwidth_allocated,
-               dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
-#endif
-}
-EXPORT_SYMBOL (usb_claim_bandwidth);
-
-
-/**
- * usb_release_bandwidth - reverses effect of usb_claim_bandwidth()
- * @dev: source/target of request
- * @urb: request (urb->dev == dev)
- * @isoc: true iff the request is isochronous
- *
- * This records that previously allocated bandwidth has been released.
- * Bandwidth is released when endpoints are removed from the host controller's
- * periodic schedule.
- */
-void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)
-{
-       dev->bus->bandwidth_allocated -= urb->bandwidth;
-       if (isoc)
-               dev->bus->bandwidth_isoc_reqs--;
-       else
-               dev->bus->bandwidth_int_reqs--;
-
-#ifdef USB_BANDWIDTH_MESSAGES
-       dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n",
-               urb->bandwidth,
-               isoc ? "ISOC" : "INTR",
-               dev->bus->bandwidth_allocated,
-               dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
-#endif
-       urb->bandwidth = 0;
-}
-EXPORT_SYMBOL (usb_release_bandwidth);
-
 
 /*-------------------------------------------------------------------------*/
 
@@ -1091,32 +908,41 @@ EXPORT_SYMBOL (usb_release_bandwidth);
 
 /*-------------------------------------------------------------------------*/
 
-static void urb_unlink (struct urb *urb)
+static void urb_unlink(struct usb_hcd *hcd, struct urb *urb)
 {
        unsigned long           flags;
 
-       /* Release any periodic transfer bandwidth */
-       if (urb->bandwidth)
-               usb_release_bandwidth (urb->dev, urb,
-                       usb_pipeisoc (urb->pipe));
-
        /* clear all state linking urb to this dev (and hcd) */
-
-       spin_lock_irqsave (&hcd_data_lock, flags);
+       spin_lock_irqsave(&hcd_urb_list_lock, flags);
        list_del_init (&urb->urb_list);
-       spin_unlock_irqrestore (&hcd_data_lock, flags);
-}
+       spin_unlock_irqrestore(&hcd_urb_list_lock, flags);
 
+       if (hcd->self.uses_dma && !is_root_hub(urb->dev)) {
+               if (usb_pipecontrol (urb->pipe)
+                       && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+                       dma_unmap_single (hcd->self.controller, urb->setup_dma,
+                                       sizeof (struct usb_ctrlrequest),
+                                       DMA_TO_DEVICE);
+               if (urb->transfer_buffer_length != 0
+                       && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+                       dma_unmap_single (hcd->self.controller,
+                                       urb->transfer_dma,
+                                       urb->transfer_buffer_length,
+                                       usb_pipein (urb->pipe)
+                                           ? DMA_FROM_DEVICE
+                                           : DMA_TO_DEVICE);
+       }
+}
 
 /* may be called in any context with a valid urb->dev usecount
  * caller surrenders "ownership" of urb
  * expects usb_submit_urb() to have sanity checked and conditioned all
  * inputs in the urb
  */
-static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
+int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
 {
        int                     status;
-       struct usb_hcd          *hcd = urb->dev->bus->hcpriv;
+       struct usb_hcd          *hcd = bus_to_hcd(urb->dev->bus);
        struct usb_host_endpoint *ep;
        unsigned long           flags;
 
@@ -1133,7 +959,7 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
 
        // FIXME:  verify that quiescing hc works right (RH cleans up)
 
-       spin_lock_irqsave (&hcd_data_lock, flags);
+       spin_lock_irqsave(&hcd_urb_list_lock, flags);
        ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
                        [usb_pipeendpoint(urb->pipe)];
        if (unlikely (!ep))
@@ -1143,24 +969,14 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
        else switch (hcd->state) {
        case HC_STATE_RUNNING:
        case HC_STATE_RESUMING:
-doit:
                list_add_tail (&urb->urb_list, &ep->urb_list);
                status = 0;
                break;
-       case HC_STATE_SUSPENDED:
-               /* HC upstream links (register access, wakeup signaling) can work
-                * even when the downstream links (and DMA etc) are quiesced; let
-                * usbcore talk to the root hub.
-                */
-               if (hcd->self.controller->power.power_state.event == PM_EVENT_ON
-                               && urb->dev->parent == NULL)
-                       goto doit;
-               /* FALL THROUGH */
        default:
                status = -ESHUTDOWN;
                break;
        }
-       spin_unlock_irqrestore (&hcd_data_lock, flags);
+       spin_unlock_irqrestore(&hcd_urb_list_lock, flags);
        if (status) {
                INIT_LIST_HEAD (&urb->urb_list);
                usbmon_urb_submit_error(&hcd->self, urb, status);
@@ -1174,7 +990,7 @@ doit:
        urb = usb_get_urb (urb);
        atomic_inc (&urb->use_count);
 
-       if (urb->dev == hcd->self.root_hub) {
+       if (is_root_hub(urb->dev)) {
                /* NOTE:  requirement on hub callers (usbfs and the hub
                 * driver, for now) that URBs' urb->transfer_buffer be
                 * valid and usb_buffer_{sync,unmap}() not be needed, since
@@ -1187,7 +1003,7 @@ doit:
        /* lower level hcd code should use *_dma exclusively,
         * unless it uses pio or talks to another transport.
         */
-       if (hcd->self.controller->dma_mask) {
+       if (hcd->self.uses_dma) {
                if (usb_pipecontrol (urb->pipe)
                        && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
                        urb->setup_dma = dma_map_single (
@@ -1209,29 +1025,18 @@ doit:
        status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);
 done:
        if (unlikely (status)) {
-               urb_unlink (urb);
+               urb_unlink(hcd, urb);
                atomic_dec (&urb->use_count);
                if (urb->reject)
                        wake_up (&usb_kill_urb_queue);
-               usb_put_urb (urb);
                usbmon_urb_submit_error(&hcd->self, urb, status);
+               usb_put_urb (urb);
        }
        return status;
 }
 
 /*-------------------------------------------------------------------------*/
 
-/* called in any context */
-static int hcd_get_frame_number (struct usb_device *udev)
-{
-       struct usb_hcd  *hcd = (struct usb_hcd *)udev->bus->hcpriv;
-       if (!HC_IS_RUNNING (hcd->state))
-               return -ESHUTDOWN;
-       return hcd->driver->get_frame_number (hcd);
-}
-
-/*-------------------------------------------------------------------------*/
-
 /* this makes the hcd giveback() the urb more quickly, by kicking it
  * off hardware queues (which may take a while) and returning it as
  * soon as practical.  we've already set up the urb's return status,
@@ -1242,7 +1047,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
 {
        int             value;
 
-       if (urb->dev == hcd->self.root_hub)
+       if (is_root_hub(urb->dev))
                value = usb_rh_urb_dequeue (hcd, urb);
        else {
 
@@ -1264,7 +1069,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
  * 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, int status)
+int usb_hcd_unlink_urb (struct urb *urb, int status)
 {
        struct usb_host_endpoint        *ep;
        struct usb_hcd                  *hcd = NULL;
@@ -1290,14 +1095,14 @@ static int hcd_unlink_urb (struct urb *urb, int status)
         * that it was submitted.  But as a rule it can't know whether or
         * not it's already been unlinked ... so we respect the reversed
         * lock sequence needed for the usb_hcd_giveback_urb() code paths
-        * (urb lock, then hcd_data_lock) in case some other CPU is now
+        * (urb lock, then hcd_urb_list_lock) in case some other CPU is now
         * unlinking it.
         */
        spin_lock_irqsave (&urb->lock, flags);
-       spin_lock (&hcd_data_lock);
+       spin_lock(&hcd_urb_list_lock);
 
        sys = &urb->dev->dev;
-       hcd = urb->dev->bus->hcpriv;
+       hcd = bus_to_hcd(urb->dev->bus);
        if (hcd == NULL) {
                retval = -ENODEV;
                goto done;
@@ -1326,17 +1131,16 @@ static int hcd_unlink_urb (struct urb *urb, int status)
         * finish unlinking the initial failed usb_set_address()
         * or device descriptor fetch.
         */
-       if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags)
-           && hcd->self.root_hub != urb->dev) {
+       if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) &&
+                       !is_root_hub(urb->dev)) {
                dev_warn (hcd->self.controller, "Unlink after no-IRQ?  "
-                       "Controller is probably using the wrong IRQ."
-                       "\n");
+                       "Controller is probably using the wrong IRQ.\n");
                set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
        }
 
        urb->status = status;
 
-       spin_unlock (&hcd_data_lock);
+       spin_unlock(&hcd_urb_list_lock);
        spin_unlock_irqrestore (&urb->lock, flags);
 
        retval = unlink1 (hcd, urb);
@@ -1345,7 +1149,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
        return retval;
 
 done:
-       spin_unlock (&hcd_data_lock);
+       spin_unlock(&hcd_urb_list_lock);
        spin_unlock_irqrestore (&urb->lock, flags);
        if (retval != -EIDRM && sys && sys->driver)
                dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
@@ -1354,46 +1158,63 @@ done:
 
 /*-------------------------------------------------------------------------*/
 
+/**
+ * usb_hcd_giveback_urb - return URB from HCD to device driver
+ * @hcd: host controller returning the URB
+ * @urb: urb being returned to the USB device driver.
+ * Context: in_interrupt()
+ *
+ * This hands the URB from HCD to its USB device driver, using its
+ * completion function.  The HCD has freed all per-urb resources
+ * (and is done using urb->hcpriv).  It also released all HCD locks;
+ * the device driver won't cause problems if it frees, modifies,
+ * or resubmits this URB.
+ */
+void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
+{
+       urb_unlink(hcd, urb);
+       usbmon_urb_complete (&hcd->self, urb);
+       usb_unanchor_urb(urb);
+
+       /* pass ownership to the completion handler */
+       urb->complete (urb);
+       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);
+
+/*-------------------------------------------------------------------------*/
+
 /* disables the endpoint: cancels any pending urbs, then synchronizes with
- * the hcd to make sure all endpoint state is gone from hardware. use for
+ * the hcd to make sure all endpoint state is gone from hardware, and then
+ * waits until the endpoint's queue is completely drained. use for
  * set_configuration, set_interface, driver removal, physical disconnect.
  *
  * example:  a qh stored in ep->hcpriv, holding state related to endpoint
  * type, maxpacket size, toggle, halt status, and scheduling.
  */
-static void
-hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
+void usb_hcd_endpoint_disable (struct usb_device *udev,
+               struct usb_host_endpoint *ep)
 {
        struct usb_hcd          *hcd;
        struct urb              *urb;
 
-       hcd = udev->bus->hcpriv;
-
-       WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
-                       udev->state != USB_STATE_NOTATTACHED);
-
+       hcd = bus_to_hcd(udev->bus);
        local_irq_disable ();
 
-       /* FIXME move most of this into message.c as part of its
-        * endpoint disable logic
-        */
-
        /* ep is already gone from udev->ep_{in,out}[]; no more submits */
 rescan:
-       spin_lock (&hcd_data_lock);
+       spin_lock(&hcd_urb_list_lock);
        list_for_each_entry (urb, &ep->urb_list, urb_list) {
                int     tmp;
 
-               /* another cpu may be in hcd, spinning on hcd_data_lock
-                * to giveback() this urb.  the races here should be
-                * small, but a full fix needs a new "can't submit"
-                * urb state.
-                * FIXME urb->reject should allow that...
-                */
+               /* the urb may already have been unlinked */
                if (urb->status != -EINPROGRESS)
                        continue;
                usb_get_urb (urb);
-               spin_unlock (&hcd_data_lock);
+               spin_unlock(&hcd_urb_list_lock);
 
                spin_lock (&urb->lock);
                tmp = urb->status;
@@ -1422,7 +1243,7 @@ rescan:
                /* list contents may have changed */
                goto rescan;
        }
-       spin_unlock (&hcd_data_lock);
+       spin_unlock(&hcd_urb_list_lock);
        local_irq_enable ();
 
        /* synchronize with the hardware, so old configuration state
@@ -1431,95 +1252,116 @@ rescan:
        might_sleep ();
        if (hcd->driver->endpoint_disable)
                hcd->driver->endpoint_disable (hcd, ep);
+
+       /* Wait until the endpoint queue is completely empty.  Most HCDs
+        * will have done this already in their endpoint_disable method,
+        * but some might not.  And there could be root-hub control URBs
+        * still pending since they aren't affected by the HCDs'
+        * endpoint_disable methods.
+        */
+       while (!list_empty (&ep->urb_list)) {
+               spin_lock_irq(&hcd_urb_list_lock);
+
+               /* The list may have changed while we acquired the spinlock */
+               urb = NULL;
+               if (!list_empty (&ep->urb_list)) {
+                       urb = list_entry (ep->urb_list.prev, struct urb,
+                                       urb_list);
+                       usb_get_urb (urb);
+               }
+               spin_unlock_irq(&hcd_urb_list_lock);
+
+               if (urb) {
+                       usb_kill_urb (urb);
+                       usb_put_urb (urb);
+               }
+       }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called in any context */
+int usb_hcd_get_frame_number (struct usb_device *udev)
+{
+       struct usb_hcd  *hcd = bus_to_hcd(udev->bus);
+
+       if (!HC_IS_RUNNING (hcd->state))
+               return -ESHUTDOWN;
+       return hcd->driver->get_frame_number (hcd);
 }
 
 /*-------------------------------------------------------------------------*/
 
 #ifdef CONFIG_PM
 
-int hcd_bus_suspend (struct usb_bus *bus)
+int hcd_bus_suspend(struct usb_device *rhdev)
 {
-       struct usb_hcd          *hcd;
-       int                     status;
+       struct usb_hcd  *hcd = container_of(rhdev->bus, struct usb_hcd, self);
+       int             status;
+       int             old_state = hcd->state;
 
-       hcd = container_of (bus, struct usb_hcd, self);
-       if (!hcd->driver->bus_suspend)
-               return -ENOENT;
-       hcd->state = HC_STATE_QUIESCING;
-       status = hcd->driver->bus_suspend (hcd);
-       if (status == 0)
+       dev_dbg(&rhdev->dev, "bus %s%s\n",
+                       rhdev->auto_pm ? "auto-" : "", "suspend");
+       if (!hcd->driver->bus_suspend) {
+               status = -ENOENT;
+       } else {
+               hcd->state = HC_STATE_QUIESCING;
+               status = hcd->driver->bus_suspend(hcd);
+       }
+       if (status == 0) {
+               usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
                hcd->state = HC_STATE_SUSPENDED;
-       else
-               dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
+       } else {
+               hcd->state = old_state;
+               dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
                                "suspend", status);
+       }
        return status;
 }
 
-int hcd_bus_resume (struct usb_bus *bus)
+int hcd_bus_resume(struct usb_device *rhdev)
 {
-       struct usb_hcd          *hcd;
-       int                     status;
+       struct usb_hcd  *hcd = container_of(rhdev->bus, struct usb_hcd, self);
+       int             status;
+       int             old_state = hcd->state;
 
-       hcd = container_of (bus, struct usb_hcd, self);
+       dev_dbg(&rhdev->dev, "usb %s%s\n",
+                       rhdev->auto_pm ? "auto-" : "", "resume");
        if (!hcd->driver->bus_resume)
                return -ENOENT;
        if (hcd->state == HC_STATE_RUNNING)
                return 0;
+
        hcd->state = HC_STATE_RESUMING;
-       status = hcd->driver->bus_resume (hcd);
-       if (status == 0)
+       status = hcd->driver->bus_resume(hcd);
+       if (status == 0) {
+               /* TRSMRCY = 10 msec */
+               msleep(10);
+               usb_set_device_state(rhdev, rhdev->actconfig
+                               ? USB_STATE_CONFIGURED
+                               : USB_STATE_ADDRESS);
                hcd->state = HC_STATE_RUNNING;
-       else {
-               dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
+       } else {
+               hcd->state = old_state;
+               dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
                                "resume", status);
-               usb_hc_died(hcd);
+               if (status != -ESHUTDOWN)
+                       usb_hc_died(hcd);
        }
        return status;
 }
 
-/*
- * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports
- * @hcd: host controller for this root hub
- *
- * This call arranges that usb_hcd_resume_root_hub() is safe to call later;
- * that the HCD's root hub polling is deactivated; and that the root's hub
- * driver is suspended.  HCDs may call this to autosuspend when their root
- * hub's downstream ports are all inactive:  unpowered, disconnected,
- * disabled, or suspended.
- *
- * The HCD will autoresume on device connect change detection (using SRP
- * or a D+/D- pullup).  The HCD also autoresumes on remote wakeup signaling
- * from any ports that are suspended (if that is enabled).  In most cases,
- * overcurrent signaling (on powered ports) will also start autoresume.
- *
- * Always called with IRQs blocked.
- */
-void usb_hcd_suspend_root_hub (struct usb_hcd *hcd)
+/* Workqueue routine for root-hub remote wakeup */
+static void hcd_resume_work(struct work_struct *work)
 {
-       struct urb      *urb;
-
-       spin_lock (&hcd_root_hub_lock);
-       usb_suspend_root_hub (hcd->self.root_hub);
-
-       /* force status urb to complete/unlink while suspended */
-       if (hcd->status_urb) {
-               urb = hcd->status_urb;
-               urb->status = -ECONNRESET;
-               urb->hcpriv = NULL;
-               urb->actual_length = 0;
+       struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
+       struct usb_device *udev = hcd->self.root_hub;
 
-               del_timer (&hcd->rh_timer);
-               hcd->poll_pending = 0;
-               hcd->status_urb = NULL;
-       } else
-               urb = NULL;
-       spin_unlock (&hcd_root_hub_lock);
-       hcd->state = HC_STATE_SUSPENDED;
-
-       if (urb)
-               usb_hcd_giveback_urb (hcd, urb, NULL);
+       usb_lock_device(udev);
+       usb_mark_last_busy(udev);
+       usb_external_resume_device(udev);
+       usb_unlock_device(udev);
 }
-EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
 
 /**
  * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
@@ -1527,8 +1369,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
  *
  * The USB host controller calls this function when its root hub is
  * suspended (with the remote wakeup feature enabled) and a remote
- * wakeup request is received.  It queues a request for khubd to
- * resume the root hub (that is, manage its downstream ports again).
+ * wakeup request is received.  The routine submits a workqueue request
+ * to resume the root hub (that is, manage its downstream ports again).
  */
 void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
 {
@@ -1536,7 +1378,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
 
        spin_lock_irqsave (&hcd_root_hub_lock, flags);
        if (hcd->rh_registered)
-               usb_resume_root_hub (hcd->self.root_hub);
+               queue_work(ksuspend_usb_wq, &hcd->wakeup_work);
        spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
@@ -1584,69 +1426,6 @@ EXPORT_SYMBOL (usb_bus_start_enum);
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
- */
-static struct usb_operations usb_hcd_operations = {
-       .get_frame_number =     hcd_get_frame_number,
-       .submit_urb =           hcd_submit_urb,
-       .unlink_urb =           hcd_unlink_urb,
-       .buffer_alloc =         hcd_buffer_alloc,
-       .buffer_free =          hcd_buffer_free,
-       .disable =              hcd_endpoint_disable,
-};
-
-/*-------------------------------------------------------------------------*/
-
-/**
- * usb_hcd_giveback_urb - return URB from HCD to device driver
- * @hcd: host controller returning the URB
- * @urb: urb being returned to the USB device driver.
- * @regs: pt_regs, passed down to the URB completion handler
- * Context: in_interrupt()
- *
- * This hands the URB from HCD to its USB device driver, using its
- * completion function.  The HCD has freed all per-urb resources
- * (and is done using urb->hcpriv).  It also released all HCD locks;
- * the device driver won't cause problems if it frees, modifies,
- * or resubmits this URB.
- */
-void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
-{
-       int at_root_hub;
-
-       at_root_hub = (urb->dev == hcd->self.root_hub);
-       urb_unlink (urb);
-
-       /* lower level hcd code should use *_dma exclusively */
-       if (hcd->self.controller->dma_mask && !at_root_hub) {
-               if (usb_pipecontrol (urb->pipe)
-                       && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
-                       dma_unmap_single (hcd->self.controller, urb->setup_dma,
-                                       sizeof (struct usb_ctrlrequest),
-                                       DMA_TO_DEVICE);
-               if (urb->transfer_buffer_length != 0
-                       && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
-                       dma_unmap_single (hcd->self.controller, 
-                                       urb->transfer_dma,
-                                       urb->transfer_buffer_length,
-                                       usb_pipein (urb->pipe)
-                                           ? DMA_FROM_DEVICE
-                                           : DMA_TO_DEVICE);
-       }
-
-       usbmon_urb_complete (&hcd->self, urb);
-       /* 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);
-
-/*-------------------------------------------------------------------------*/
-
 /**
  * usb_hcd_irq - hook IRQs to HCD framework (bus glue)
  * @irq: the IRQ being raised
@@ -1656,7 +1435,7 @@ EXPORT_SYMBOL (usb_hcd_giveback_urb);
  * If the controller isn't HALTed, calls the driver's irq handler.
  * Checks whether the controller is now dead.
  */
-irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+irqreturn_t usb_hcd_irq (int irq, void *__hcd)
 {
        struct usb_hcd          *hcd = __hcd;
        int                     start = hcd->state;
@@ -1664,7 +1443,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
        if (unlikely(start == HC_STATE_HALT ||
            !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
                return IRQ_NONE;
-       if (hcd->driver->irq (hcd, r) == IRQ_NONE)
+       if (hcd->driver->irq (hcd) == IRQ_NONE)
                return IRQ_NONE;
 
        set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
@@ -1705,14 +1484,6 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
 
 /*-------------------------------------------------------------------------*/
 
-static void hcd_release (struct usb_bus *bus)
-{
-       struct usb_hcd *hcd;
-
-       hcd = container_of(bus, struct usb_hcd, self);
-       kfree(hcd);
-}
-
 /**
  * usb_create_hcd - create and initialize an HCD structure
  * @driver: HC driver that will use this hcd
@@ -1737,17 +1508,19 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
                return NULL;
        }
        dev_set_drvdata(dev, hcd);
+       kref_init(&hcd->kref);
 
        usb_bus_init(&hcd->self);
-       hcd->self.op = &usb_hcd_operations;
-       hcd->self.hcpriv = hcd;
-       hcd->self.release = &hcd_release;
        hcd->self.controller = dev;
        hcd->self.bus_name = bus_name;
+       hcd->self.uses_dma = (dev->dma_mask != NULL);
 
        init_timer(&hcd->rh_timer);
        hcd->rh_timer.function = rh_timer_func;
        hcd->rh_timer.data = (unsigned long) hcd;
+#ifdef CONFIG_PM
+       INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
+#endif
 
        hcd->driver = driver;
        hcd->product_desc = (driver->product_desc) ? driver->product_desc :
@@ -1757,10 +1530,25 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 }
 EXPORT_SYMBOL (usb_create_hcd);
 
+static void hcd_release (struct kref *kref)
+{
+       struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
+
+       kfree(hcd);
+}
+
+struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
+{
+       if (hcd)
+               kref_get (&hcd->kref);
+       return hcd;
+}
+EXPORT_SYMBOL (usb_get_hcd);
+
 void usb_put_hcd (struct usb_hcd *hcd)
 {
-       dev_set_drvdata(hcd->self.controller, NULL);
-       usb_bus_put(&hcd->self);
+       if (hcd)
+               kref_put (&hcd->kref, hcd_release);
 }
 EXPORT_SYMBOL (usb_put_hcd);
 
@@ -1826,24 +1614,16 @@ int usb_add_hcd(struct usb_hcd *hcd,
 
        /* enable irqs just before we start the controller */
        if (hcd->driver->irq) {
-               char    buf[8], *bufp = buf;
-
-#ifdef __sparc__
-               bufp = __irq_itoa(irqnum);
-#else
-               sprintf(buf, "%d", irqnum);
-#endif
-
                snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
                                hcd->driver->description, hcd->self.busnum);
                if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
                                hcd->irq_descr, hcd)) != 0) {
                        dev_err(hcd->self.controller,
-                                       "request interrupt %s failed\n", bufp);
+                                       "request interrupt %d failed\n", irqnum);
                        goto err_request_irq;
                }
                hcd->irq = irqnum;
-               dev_info(hcd->self.controller, "irq %s, %s 0x%08llx\n", bufp,
+               dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum,
                                (hcd->driver->flags & HCD_MEMORY) ?
                                        "io mem" : "io base",
                                        (unsigned long long)hcd->rsrc_start);
@@ -1907,16 +1687,20 @@ void usb_remove_hcd(struct usb_hcd *hcd)
        hcd->rh_registered = 0;
        spin_unlock_irq (&hcd_root_hub_lock);
 
+#ifdef CONFIG_PM
+       cancel_work_sync(&hcd->wakeup_work);
+#endif
+
        mutex_lock(&usb_bus_list_lock);
        usb_disconnect(&hcd->self.root_hub);
        mutex_unlock(&usb_bus_list_lock);
 
-       hcd->poll_rh = 0;
-       del_timer_sync(&hcd->rh_timer);
-
        hcd->driver->stop(hcd);
        hcd->state = HC_STATE_HALT;
 
+       hcd->poll_rh = 0;
+       del_timer_sync(&hcd->rh_timer);
+
        if (hcd->irq >= 0)
                free_irq(hcd->irq, hcd);
        usb_deregister_bus(&hcd->self);
@@ -1924,6 +1708,16 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 }
 EXPORT_SYMBOL (usb_remove_hcd);
 
+void
+usb_hcd_platform_shutdown(struct platform_device* dev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+       if (hcd->driver->shutdown)
+               hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_platform_shutdown);
+
 /*-------------------------------------------------------------------------*/
 
 #if defined(CONFIG_USB_MON)