Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / usb / core / hcd.c
index cd2268a..fb4d058 100644 (file)
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
-#define DEBUG
-#endif
-
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/completion.h>
-#include <linux/uts.h>                 /* for UTS_SYSNAME */
+#include <linux/utsname.h>
 #include <linux/mm.h>
 #include <asm/io.h>
 #include <asm/scatterlist.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <asm/irq.h>
 #include <asm/byteorder.h>
 
 #include <linux/usb.h>
 
 #include "usb.h"
 #include "hcd.h"
+#include "hub.h"
 
 
 // #define USB_BANDWIDTH_MESSAGES
@@ -96,11 +93,14 @@ struct usb_busmap {
 static struct usb_busmap busmap;
 
 /* used when updating list of hcds */
-DECLARE_MUTEX (usb_bus_list_lock);     /* exported only for usbfs */
+DEFINE_MUTEX(usb_bus_list_lock);       /* exported only for usbfs */
 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 spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(hcd_data_lock);
 
 /* wait queue for synchronous unlinks */
 DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
@@ -120,16 +120,16 @@ DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
 static const u8 usb2_rh_dev_descriptor [18] = {
        0x12,       /*  __u8  bLength; */
        0x01,       /*  __u8  bDescriptorType; Device */
-       0x00, 0x02, /*  __u16 bcdUSB; v2.0 */
+       0x00, 0x02, /*  __le16 bcdUSB; v2.0 */
 
        0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
        0x00,       /*  __u8  bDeviceSubClass; */
        0x01,       /*  __u8  bDeviceProtocol; [ usb 2.0 single TT ]*/
-       0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+       0x40,       /*  __u8  bMaxPacketSize0; 64 Bytes */
 
-       0x00, 0x00, /*  __u16 idVendor; */
-       0x00, 0x00, /*  __u16 idProduct; */
-       KERNEL_VER, KERNEL_REL, /*  __u16 bcdDevice */
+       0x00, 0x00, /*  __le16 idVendor; */
+       0x00, 0x00, /*  __le16 idProduct; */
+       KERNEL_VER, KERNEL_REL, /*  __le16 bcdDevice */
 
        0x03,       /*  __u8  iManufacturer; */
        0x02,       /*  __u8  iProduct; */
@@ -143,16 +143,16 @@ static const u8 usb2_rh_dev_descriptor [18] = {
 static const u8 usb11_rh_dev_descriptor [18] = {
        0x12,       /*  __u8  bLength; */
        0x01,       /*  __u8  bDescriptorType; Device */
-       0x10, 0x01, /*  __u16 bcdUSB; v1.1 */
+       0x10, 0x01, /*  __le16 bcdUSB; v1.1 */
 
        0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
        0x00,       /*  __u8  bDeviceSubClass; */
        0x00,       /*  __u8  bDeviceProtocol; [ low/full speeds only ] */
-       0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+       0x40,       /*  __u8  bMaxPacketSize0; 64 Bytes */
 
-       0x00, 0x00, /*  __u16 idVendor; */
-       0x00, 0x00, /*  __u16 idProduct; */
-       KERNEL_VER, KERNEL_REL, /*  __u16 bcdDevice */
+       0x00, 0x00, /*  __le16 idVendor; */
+       0x00, 0x00, /*  __le16 idProduct; */
+       KERNEL_VER, KERNEL_REL, /*  __le16 bcdDevice */
 
        0x03,       /*  __u8  iManufacturer; */
        0x02,       /*  __u8  iProduct; */
@@ -170,7 +170,7 @@ static const u8 fs_rh_config_descriptor [] = {
        /* one configuration */
        0x09,       /*  __u8  bLength; */
        0x02,       /*  __u8  bDescriptorType; Configuration */
-       0x19, 0x00, /*  __u16 wTotalLength; */
+       0x19, 0x00, /*  __le16 wTotalLength; */
        0x01,       /*  __u8  bNumInterfaces; (1) */
        0x01,       /*  __u8  bConfigurationValue; */
        0x00,       /*  __u8  iConfiguration; */
@@ -208,7 +208,7 @@ static const u8 fs_rh_config_descriptor [] = {
        0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
        0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
        0x03,       /*  __u8  ep_bmAttributes; Interrupt */
-       0x02, 0x00, /*  __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+       0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
        0xff        /*  __u8  ep_bInterval; (255ms -- usb 2.0 spec) */
 };
 
@@ -217,7 +217,7 @@ static const u8 hs_rh_config_descriptor [] = {
        /* one configuration */
        0x09,       /*  __u8  bLength; */
        0x02,       /*  __u8  bDescriptorType; Configuration */
-       0x19, 0x00, /*  __u16 wTotalLength; */
+       0x19, 0x00, /*  __le16 wTotalLength; */
        0x01,       /*  __u8  bNumInterfaces; (1) */
        0x01,       /*  __u8  bConfigurationValue; */
        0x00,       /*  __u8  iConfiguration; */
@@ -255,7 +255,7 @@ 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, /*  __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+       0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
        0x0c        /*  __u8  ep_bInterval; (256ms -- usb 2.0 spec) */
 };
 
@@ -273,6 +273,10 @@ static int ascii2utf (char *s, u8 *utf, int utfmax)
                *utf++ = *s++;
                *utf++ = 0;
        }
+       if (utfmax > 0) {
+               *utf = *s;
+               ++retval;
+       }
        return retval;
 }
 
@@ -297,30 +301,40 @@ static int rh_string (
 
        // language ids
        if (id == 0) {
-               *data++ = 4; *data++ = 3;       /* 4 bytes string data */
-               *data++ = 0x09; *data++ = 0x04; /* MSFT-speak for "en-us" */
-               return 4;
+               buf[0] = 4;    buf[1] = 3;      /* 4 bytes string data */
+               buf[2] = 0x09; buf[3] = 0x04;   /* MSFT-speak for "en-us" */
+               len = min (len, 4);
+               memcpy (data, buf, len);
+               return len;
 
        // serial number
        } else if (id == 1) {
-               strcpy (buf, hcd->self.bus_name);
+               strlcpy (buf, hcd->self.bus_name, sizeof buf);
 
        // product description
        } else if (id == 2) {
-                strcpy (buf, hcd->product_desc);
+               strlcpy (buf, hcd->product_desc, sizeof buf);
 
        // id 3 == vendor description
        } else if (id == 3) {
-                sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE,
-                       hcd->description);
+               snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname,
+                       system_utsname.release, hcd->driver->description);
 
        // unsupported IDs --> "protocol stall"
        } else
-           return 0;
+               return -EPIPE;
 
-       data [0] = 2 * (strlen (buf) + 1);
-       data [1] = 3;   /* type == string */
-       return 2 + ascii2utf (buf, data + 2, len - 2);
+       switch (len) {          /* All cases fall through */
+       default:
+               len = 2 + ascii2utf (buf, data + 2, len - 2);
+       case 2:
+               data [1] = 3;   /* type == string */
+       case 1:
+               data [0] = 2 * (strlen (buf) + 1);
+       case 0:
+               ;               /* Compiler wants a statement here */
+       }
+       return len;
 }
 
 
@@ -329,11 +343,14 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 {
        struct usb_ctrlrequest *cmd;
        u16             typeReq, wValue, wIndex, wLength;
-       const u8        *bufp = NULL;
        u8              *ubuf = urb->transfer_buffer;
+       u8              tbuf [sizeof (struct usb_hub_descriptor)];
+       const u8        *bufp = tbuf;
        int             len = 0;
        int             patch_wakeup = 0;
        unsigned long   flags;
+       int             status = 0;
+       int             n;
 
        cmd = (struct usb_ctrlrequest *) urb->setup_packet;
        typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;
@@ -344,32 +361,50 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
        if (wLength > urb->transfer_buffer_length)
                goto error;
 
-       /* set up for success */
-       urb->status = 0;
-       urb->actual_length = wLength;
+       urb->actual_length = 0;
        switch (typeReq) {
 
        /* DEVICE REQUESTS */
 
+       /* The root hub's remote wakeup enable bit is implemented using
+        * driver model wakeup flags.  If this system supports wakeup
+        * through USB, userspace may change the default "allow wakeup"
+        * policy through sysfs or these calls.
+        *
+        * Most root hubs support wakeup from downstream devices, for
+        * runtime power management (disabling USB clocks and reducing
+        * VBUS power usage).  However, not all of them do so; silicon,
+        * board, and BIOS bugs here are not uncommon, so these can't
+        * be treated quite like external hubs.
+        *
+        * Likewise, not all root hubs will pass wakeup events upstream,
+        * to wake up the whole system.  So don't assume root hub and
+        * controller capabilities are identical.
+        */
+
        case DeviceRequest | USB_REQ_GET_STATUS:
-               ubuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP)
+               tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
+                                       << USB_DEVICE_REMOTE_WAKEUP)
                                | (1 << USB_DEVICE_SELF_POWERED);
-               ubuf [1] = 0;
+               tbuf [1] = 0;
+               len = 2;
                break;
        case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
                if (wValue == USB_DEVICE_REMOTE_WAKEUP)
-                       hcd->remote_wakeup = 0;
+                       device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);
                else
                        goto error;
                break;
        case DeviceOutRequest | USB_REQ_SET_FEATURE:
-               if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP)
-                       hcd->remote_wakeup = 1;
+               if (device_can_wakeup(&hcd->self.root_hub->dev)
+                               && wValue == USB_DEVICE_REMOTE_WAKEUP)
+                       device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);
                else
                        goto error;
                break;
        case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-               ubuf [0] = 1;
+               tbuf [0] = 1;
+               len = 1;
                        /* FALLTHROUGH */
        case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
                break;
@@ -392,20 +427,22 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
                                bufp = fs_rh_config_descriptor;
                                len = sizeof fs_rh_config_descriptor;
                        }
-                       if (hcd->can_wakeup)
+                       if (device_can_wakeup(&hcd->self.root_hub->dev))
                                patch_wakeup = 1;
                        break;
                case USB_DT_STRING << 8:
-                       urb->actual_length = rh_string (
-                               wValue & 0xff, hcd,
-                               ubuf, wLength);
+                       n = rh_string (wValue & 0xff, hcd, ubuf, wLength);
+                       if (n < 0)
+                               goto error;
+                       urb->actual_length = n;
                        break;
                default:
                        goto error;
                }
                break;
        case DeviceRequest | USB_REQ_GET_INTERFACE:
-               ubuf [0] = 0;
+               tbuf [0] = 0;
+               len = 1;
                        /* FALLTHROUGH */
        case DeviceOutRequest | USB_REQ_SET_INTERFACE:
                break;
@@ -421,8 +458,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 
        case EndpointRequest | USB_REQ_GET_STATUS:
                // ENDPOINT_HALT flag
-               ubuf [0] = 0;
-               ubuf [1] = 0;
+               tbuf [0] = 0;
+               tbuf [1] = 0;
+               len = 2;
                        /* FALLTHROUGH */
        case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
        case EndpointOutRequest | USB_REQ_SET_FEATURE:
@@ -433,27 +471,35 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 
        default:
                /* non-generic request */
-               if (HCD_IS_SUSPENDED (hcd->state))
-                       urb->status = -EAGAIN;
-               else if (!HCD_IS_RUNNING (hcd->state))
-                       urb->status = -ENODEV;
-               else
-                       urb->status = hcd->driver->hub_control (hcd,
-                               typeReq, wValue, wIndex,
-                               ubuf, wLength);
+               switch (typeReq) {
+               case GetHubStatus:
+               case GetPortStatus:
+                       len = 4;
+                       break;
+               case GetHubDescriptor:
+                       len = sizeof (struct usb_hub_descriptor);
+                       break;
+               }
+               status = hcd->driver->hub_control (hcd,
+                       typeReq, wValue, wIndex,
+                       tbuf, wLength);
                break;
 error:
                /* "protocol stall" on error */
-               urb->status = -EPIPE;
-               dev_dbg (hcd->self.controller, "unsupported hub control message (maxchild %d)\n",
-                               urb->dev->maxchild);
+               status = -EPIPE;
        }
-       if (urb->status) {
-               urb->actual_length = 0;
-               dev_dbg (hcd->self.controller, "CTRL: TypeReq=0x%x val=0x%x idx=0x%x len=%d ==> %d\n",
-                       typeReq, wValue, wIndex, wLength, urb->status);
+
+       if (status) {
+               len = 0;
+               if (status != -EPIPE) {
+                       dev_dbg (hcd->self.controller,
+                               "CTRL: TypeReq=0x%x val=0x%x "
+                               "idx=0x%x len=%d ==> %d\n",
+                               typeReq, wValue, wIndex,
+                               wLength, status);
+               }
        }
-       if (bufp) {
+       if (len) {
                if (urb->transfer_buffer_length < len)
                        len = urb->transfer_buffer_length;
                urb->actual_length = len;
@@ -461,13 +507,19 @@ error:
                memcpy (ubuf, bufp, len);
 
                /* report whether RH hardware supports remote wakeup */
-               if (patch_wakeup)
+               if (patch_wakeup &&
+                               len > offsetof (struct usb_config_descriptor,
+                                               bmAttributes))
                        ((struct usb_config_descriptor *)ubuf)->bmAttributes
                                |= USB_CONFIG_ATT_WAKEUP;
        }
 
        /* any errors get returned through the urb completion */
        local_irq_save (flags);
+       spin_lock (&urb->lock);
+       if (urb->status == -EINPROGRESS)
+               urb->status = status;
+       spin_unlock (&urb->lock);
        usb_hcd_giveback_urb (hcd, urb, NULL);
        local_irq_restore (flags);
        return 0;
@@ -476,164 +528,192 @@ error:
 /*-------------------------------------------------------------------------*/
 
 /*
- * Root Hub interrupt transfers are synthesized with a timer.
- * Completions are called in_interrupt() but not in_irq().
+ * Root Hub interrupt transfers are polled using a timer if the
+ * driver requests it; otherwise the driver is responsible for
+ * calling usb_hcd_poll_rh_status() when an event occurs.
+ *
+ * Completions are called in_interrupt(), but they may or may not
+ * be in_irq().
  */
+void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
+{
+       struct urb      *urb;
+       int             length;
+       unsigned long   flags;
+       char            buffer[4];      /* Any root hubs with > 31 ports? */
 
-static void rh_report_status (unsigned long ptr);
+       if (!hcd->uses_new_polling && !hcd->status_urb)
+               return;
 
-static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) 
-{
-       int     len = 1 + (urb->dev->maxchild / 8);
-
-       /* rh_timer protected by hcd_data_lock */
-       if (hcd->rh_timer.data
-                       || urb->status != -EINPROGRESS
-                       || urb->transfer_buffer_length < len
-                       || !HCD_IS_RUNNING (hcd->state)) {
-               dev_dbg (hcd->self.controller,
-                               "not queuing rh status urb, stat %d\n",
-                               urb->status);
-               return -EINVAL;
+       length = hcd->driver->hub_status_data(hcd, buffer);
+       if (length > 0) {
+
+               /* try to complete the status urb */
+               local_irq_save (flags);
+               spin_lock(&hcd_root_hub_lock);
+               urb = hcd->status_urb;
+               if (urb) {
+                       spin_lock(&urb->lock);
+                       if (urb->status == -EINPROGRESS) {
+                               hcd->poll_pending = 0;
+                               hcd->status_urb = NULL;
+                               urb->status = 0;
+                               urb->hcpriv = NULL;
+                               urb->actual_length = length;
+                               memcpy(urb->transfer_buffer, buffer, length);
+                       } else          /* urb has been unlinked */
+                               length = 0;
+                       spin_unlock(&urb->lock);
+               } else
+                       length = 0;
+               spin_unlock(&hcd_root_hub_lock);
+
+               /* local irqs are always blocked in completions */
+               if (length > 0)
+                       usb_hcd_giveback_urb (hcd, urb, NULL);
+               else
+                       hcd->poll_pending = 1;
+               local_irq_restore (flags);
        }
 
-       init_timer (&hcd->rh_timer);
-       hcd->rh_timer.function = rh_report_status;
-       hcd->rh_timer.data = (unsigned long) urb;
-       /* USB 2.0 spec says 256msec; this is close enough */
-       hcd->rh_timer.expires = jiffies + HZ/4;
-       add_timer (&hcd->rh_timer);
-       urb->hcpriv = hcd;      /* nonzero to indicate it's queued */
-       return 0;
+       /* The USB 2.0 spec says 256 ms.  This is close enough and won't
+        * exceed that limit if HZ is 100. */
+       if (hcd->uses_new_polling ? hcd->poll_rh :
+                       (length == 0 && hcd->status_urb != NULL))
+               mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250));
 }
+EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
 
 /* timer callback */
+static void rh_timer_func (unsigned long _hcd)
+{
+       usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
+}
 
-static void rh_report_status (unsigned long ptr)
+/*-------------------------------------------------------------------------*/
+
+static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
 {
-       struct urb      *urb;
-       struct usb_hcd  *hcd;
-       int             length = 0;
+       int             retval;
        unsigned long   flags;
+       int             len = 1 + (urb->dev->maxchild / 8);
+
+       spin_lock_irqsave (&hcd_root_hub_lock, flags);
+       if (urb->status != -EINPROGRESS)        /* already unlinked */
+               retval = urb->status;
+       else if (hcd->status_urb || urb->transfer_buffer_length < len) {
+               dev_dbg (hcd->self.controller, "not queuing rh status urb\n");
+               retval = -EINVAL;
+       } else {
+               hcd->status_urb = urb;
+               urb->hcpriv = hcd;      /* indicate it's queued */
 
-       urb = (struct urb *) ptr;
-       local_irq_save (flags);
-       spin_lock (&urb->lock);
+               if (!hcd->uses_new_polling)
+                       mod_timer (&hcd->rh_timer, jiffies +
+                                       msecs_to_jiffies(250));
 
-       /* do nothing if the urb's been unlinked */
-       if (!urb->dev
-                       || urb->status != -EINPROGRESS
-                       || (hcd = urb->dev->bus->hcpriv) == 0) {
-               spin_unlock (&urb->lock);
-               local_irq_restore (flags);
-               return;
+               /* If a status change has already occurred, report it ASAP */
+               else if (hcd->poll_pending)
+                       mod_timer (&hcd->rh_timer, jiffies);
+               retval = 0;
        }
-
-       if (!HCD_IS_SUSPENDED (hcd->state))
-               length = hcd->driver->hub_status_data (
-                                       hcd, urb->transfer_buffer);
-
-       /* complete the status urb, or retrigger the timer */
-       spin_lock (&hcd_data_lock);
-       if (length > 0) {
-               hcd->rh_timer.data = 0;
-               urb->actual_length = length;
-               urb->status = 0;
-               urb->hcpriv = NULL;
-       } else
-               mod_timer (&hcd->rh_timer, jiffies + HZ/4);
-       spin_unlock (&hcd_data_lock);
-       spin_unlock (&urb->lock);
-
-       /* local irqs are always blocked in completions */
-       if (length > 0)
-               usb_hcd_giveback_urb (hcd, urb, NULL);
-       local_irq_restore (flags);
+       spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
+       return retval;
 }
 
-/*-------------------------------------------------------------------------*/
-
 static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
 {
-       if (usb_pipeint (urb->pipe)) {
-               int             retval;
-               unsigned long   flags;
-
-               spin_lock_irqsave (&hcd_data_lock, flags);
-               retval = rh_status_urb (hcd, urb);
-               spin_unlock_irqrestore (&hcd_data_lock, flags);
-               return retval;
-       }
+       if (usb_pipeint (urb->pipe))
+               return rh_queue_status (hcd, urb);
        if (usb_pipecontrol (urb->pipe))
                return rh_call_control (hcd, urb);
-       else
-               return -EINVAL;
+       return -EINVAL;
 }
 
 /*-------------------------------------------------------------------------*/
 
-int usb_rh_status_dequeue (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.
+ */
+static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
-       unsigned long   flags;
-
-       /* note:  always a synchronous unlink */
-       del_timer_sync (&hcd->rh_timer);
-       hcd->rh_timer.data = 0;
+       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);
+
+               spin_lock_irq(&urb->lock);
+               --urb->reject;
+               spin_unlock_irq(&urb->lock);
+
+       } else {                                /* Status URB */
+               if (!hcd->uses_new_polling)
+                       del_timer_sync (&hcd->rh_timer);
+               local_irq_disable ();
+               spin_lock (&hcd_root_hub_lock);
+               if (urb == hcd->status_urb) {
+                       hcd->status_urb = NULL;
+                       urb->hcpriv = NULL;
+               } else
+                       urb = NULL;             /* wasn't fully queued */
+               spin_unlock (&hcd_root_hub_lock);
+               if (urb)
+                       usb_hcd_giveback_urb (hcd, urb, NULL);
+               local_irq_enable ();
+       }
 
-       local_irq_save (flags);
-       urb->hcpriv = NULL;
-       usb_hcd_giveback_urb (hcd, urb, NULL);
-       local_irq_restore (flags);
        return 0;
 }
 
 /*-------------------------------------------------------------------------*/
 
 /* exported only within usbcore */
-struct usb_bus *usb_bus_get (struct usb_bus *bus)
+struct usb_bus *usb_bus_get(struct usb_bus *bus)
 {
-       struct class_device *tmp;
+       if (bus)
+               kref_get(&bus->kref);
+       return bus;
+}
 
-       if (!bus)
-               return NULL;
+static void usb_host_release(struct kref *kref)
+{
+       struct usb_bus *bus = container_of(kref, struct usb_bus, kref);
 
-       tmp = class_device_get(&bus->class_dev);
-       if (tmp)        
-               return to_usb_bus(tmp);
-       else
-               return NULL;
+       if (bus->release)
+               bus->release(bus);
 }
 
 /* exported only within usbcore */
-void usb_bus_put (struct usb_bus *bus)
+void usb_bus_put(struct usb_bus *bus)
 {
        if (bus)
-               class_device_put(&bus->class_dev);
+               kref_put(&bus->kref, usb_host_release);
 }
 
 /*-------------------------------------------------------------------------*/
 
-static void usb_host_release(struct class_device *class_dev)
-{
-       struct usb_bus *bus = to_usb_bus(class_dev);
-
-       if (bus->release)
-               bus->release(bus);
-}
-
-static struct class usb_host_class = {
-       .name           = "usb_host",
-       .release        = &usb_host_release,
-};
+static struct class *usb_host_class;
 
 int usb_host_init(void)
 {
-       return class_register(&usb_host_class);
+       int retval = 0;
+
+       usb_host_class = class_create(THIS_MODULE, "usb_host");
+       if (IS_ERR(usb_host_class))
+               retval = PTR_ERR(usb_host_class);
+       return retval;
 }
 
 void usb_host_cleanup(void)
 {
-       class_unregister(&usb_host_class);
+       class_destroy(usb_host_class);
 }
 
 /**
@@ -643,7 +723,7 @@ void usb_host_cleanup(void)
  * This code is used to initialize a usb_bus structure, memory for which is
  * separately managed.
  */
-void usb_bus_init (struct usb_bus *bus)
+static void usb_bus_init (struct usb_bus *bus)
 {
        memset (&bus->devmap, 0, sizeof(struct usb_devmap));
 
@@ -657,8 +737,9 @@ void usb_bus_init (struct usb_bus *bus)
        bus->bandwidth_isoc_reqs = 0;
 
        INIT_LIST_HEAD (&bus->bus_list);
+
+       kref_init(&bus->kref);
 }
-EXPORT_SYMBOL (usb_bus_init);
 
 /**
  * usb_alloc_bus - creates a new USB host controller structure
@@ -676,15 +757,13 @@ struct usb_bus *usb_alloc_bus (struct usb_operations *op)
 {
        struct usb_bus *bus;
 
-       bus = kmalloc (sizeof *bus, GFP_KERNEL);
+       bus = kzalloc (sizeof *bus, GFP_KERNEL);
        if (!bus)
                return NULL;
-       memset(bus, 0, sizeof(struct usb_bus));
        usb_bus_init (bus);
        bus->op = op;
        return bus;
 }
-EXPORT_SYMBOL (usb_alloc_bus);
 
 /*-------------------------------------------------------------------------*/
 
@@ -696,41 +775,40 @@ EXPORT_SYMBOL (usb_alloc_bus);
  * Assigns a bus number, and links the controller into usbcore data
  * structures so that it can be seen by scanning the bus list.
  */
-int usb_register_bus(struct usb_bus *bus)
+static int usb_register_bus(struct usb_bus *bus)
 {
        int busnum;
-       int retval;
 
-       down (&usb_bus_list_lock);
+       mutex_lock(&usb_bus_list_lock);
        busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
        if (busnum < USB_MAXBUS) {
                set_bit (busnum, busmap.busmap);
                bus->busnum = busnum;
        } else {
                printk (KERN_ERR "%s: too many buses\n", usbcore_name);
+               mutex_unlock(&usb_bus_list_lock);
                return -E2BIG;
        }
 
-       snprintf(bus->class_dev.class_id, BUS_ID_SIZE, "usb%d", busnum);
-       bus->class_dev.class = &usb_host_class;
-       bus->class_dev.dev = bus->controller;
-       retval = class_device_register(&bus->class_dev);
-       if (retval) {
+       bus->class_dev = class_device_create(usb_host_class, NULL, MKDEV(0,0),
+                                            bus->controller, "usb_host%d", busnum);
+       if (IS_ERR(bus->class_dev)) {
                clear_bit(busnum, busmap.busmap);
-               up(&usb_bus_list_lock);
-               return retval;
+               mutex_unlock(&usb_bus_list_lock);
+               return PTR_ERR(bus->class_dev);
        }
 
+       class_set_devdata(bus->class_dev, bus);
+
        /* Add it to the local list of buses */
        list_add (&bus->bus_list, &usb_bus_list);
-       up (&usb_bus_list_lock);
+       mutex_unlock(&usb_bus_list_lock);
 
-       usbfs_add_bus (bus);
+       usb_notify_add_bus(bus);
 
        dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
        return 0;
 }
-EXPORT_SYMBOL (usb_register_bus);
 
 /**
  * usb_deregister_bus - deregisters the USB host controller
@@ -740,7 +818,7 @@ EXPORT_SYMBOL (usb_register_bus);
  * Recycles the bus number, and unlinks the controller from usbcore data
  * structures so that it won't be seen by scanning the bus list.
  */
-void usb_deregister_bus (struct usb_bus *bus)
+static void usb_deregister_bus (struct usb_bus *bus)
 {
        dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum);
 
@@ -749,31 +827,30 @@ void usb_deregister_bus (struct usb_bus *bus)
         * controller code, as well as having it call this when cleaning
         * itself up
         */
-       down (&usb_bus_list_lock);
+       mutex_lock(&usb_bus_list_lock);
        list_del (&bus->bus_list);
-       up (&usb_bus_list_lock);
+       mutex_unlock(&usb_bus_list_lock);
 
-       usbfs_remove_bus (bus);
+       usb_notify_remove_bus(bus);
 
        clear_bit (bus->busnum, busmap.busmap);
 
-       class_device_unregister(&bus->class_dev);
+       class_device_unregister(bus->class_dev);
 }
-EXPORT_SYMBOL (usb_deregister_bus);
 
 /**
- * usb_register_root_hub - called by HCD to register its root hub 
- * @usb_dev: the usb root hub device to be registered.
- * @parent_dev: the parent device of this root hub.
+ * register_root_hub - called by usb_add_hcd() to register a root hub
+ * @hcd: host controller for this root hub
  *
- * 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 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).
+ * This function registers the root hub with the USB subsystem.  It sets up
+ * the device properly in the device tree and 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)
+static int register_root_hub(struct usb_hcd *hcd)
 {
+       struct device *parent_dev = hcd->self.controller;
+       struct usb_device *usb_dev = hcd->self.root_hub;
        const int devnum = 1;
        int retval;
 
@@ -784,29 +861,46 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev
        set_bit (devnum, usb_dev->bus->devmap.devicemap);
        usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
 
-       down (&usb_bus_list_lock);
-       usb_dev->bus->root_hub = usb_dev;
+       mutex_lock(&usb_bus_list_lock);
 
-       usb_dev->epmaxpacketin[0] = usb_dev->epmaxpacketout[0] = 64;
+       usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
        retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
        if (retval != sizeof usb_dev->descriptor) {
+               mutex_unlock(&usb_bus_list_lock);
                dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
                                usb_dev->dev.bus_id, retval);
                return (retval < 0) ? retval : -EMSGSIZE;
        }
 
-       down (&usb_dev->serialize);
        retval = usb_new_device (usb_dev);
-       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_bus_list_lock);
+       mutex_unlock(&usb_bus_list_lock);
+
+       if (retval == 0) {
+               spin_lock_irq (&hcd_root_hub_lock);
+               hcd->rh_registered = 1;
+               spin_unlock_irq (&hcd_root_hub_lock);
+
+               /* Did the HC die before the root hub was registered? */
+               if (hcd->state == HC_STATE_HALT)
+                       usb_hc_died (hcd);      /* This time clean up */
+       }
+
        return retval;
 }
-EXPORT_SYMBOL (usb_register_root_hub);
+
+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)
+               hcd->driver->hub_irq_enable (hcd);
+}
 
 
 /*-------------------------------------------------------------------------*/
@@ -846,9 +940,9 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
        case USB_SPEED_HIGH:    /* ISOC or INTR */
                // FIXME adjust for input vs output
                if (isoc)
-                       tmp = HS_USECS (bytecount);
+                       tmp = HS_NSECS_ISO (bytecount);
                else
-                       tmp = HS_USECS_ISO (bytecount);
+                       tmp = HS_NSECS (bytecount);
                return tmp;
        default:
                pr_debug ("%s: bogus device speed!\n", usbcore_name);
@@ -996,40 +1090,6 @@ EXPORT_SYMBOL (usb_release_bandwidth);
 
 /*-------------------------------------------------------------------------*/
 
-/* called from khubd, or root hub init threads for hcd-private init */
-static int hcd_alloc_dev (struct usb_device *udev)
-{
-       struct hcd_dev          *dev;
-       struct usb_hcd          *hcd;
-       unsigned long           flags;
-
-       if (!udev || udev->hcpriv)
-               return -EINVAL;
-       if (!udev->bus || !udev->bus->hcpriv)
-               return -ENODEV;
-       hcd = udev->bus->hcpriv;
-       if (hcd->state == USB_STATE_QUIESCING)
-               return -ENOLINK;
-
-       dev = (struct hcd_dev *) kmalloc (sizeof *dev, GFP_KERNEL);
-       if (dev == NULL)
-               return -ENOMEM;
-       memset (dev, 0, sizeof *dev);
-
-       INIT_LIST_HEAD (&dev->dev_list);
-       INIT_LIST_HEAD (&dev->urb_list);
-
-       spin_lock_irqsave (&hcd_data_lock, flags);
-       list_add (&dev->dev_list, &hcd->dev_list);
-       // refcount is implicit
-       udev->hcpriv = dev;
-       spin_unlock_irqrestore (&hcd_data_lock, flags);
-
-       return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
 static void urb_unlink (struct urb *urb)
 {
        unsigned long           flags;
@@ -1044,7 +1104,6 @@ static void urb_unlink (struct urb *urb)
        spin_lock_irqsave (&hcd_data_lock, flags);
        list_del_init (&urb->urb_list);
        spin_unlock_irqrestore (&hcd_data_lock, flags);
-       usb_put_dev (urb->dev);
 }
 
 
@@ -1053,24 +1112,17 @@ static void urb_unlink (struct urb *urb)
  * expects usb_submit_urb() to have sanity checked and conditioned all
  * inputs in the urb
  */
-static int hcd_submit_urb (struct urb *urb, int mem_flags)
+static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
 {
        int                     status;
        struct usb_hcd          *hcd = urb->dev->bus->hcpriv;
-       struct hcd_dev          *dev = urb->dev->hcpriv;
+       struct usb_host_endpoint *ep;
        unsigned long           flags;
 
-       if (!hcd || !dev)
+       if (!hcd)
                return -ENODEV;
 
-       /*
-        * FIXME:  make urb timeouts be generic, keeping the HCD cores
-        * as simple as possible.
-        */
-
-       // NOTE:  a generic device/urb monitoring hook would go here.
-       // hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)
-       // It would catch submission paths for all urbs.
+       usbmon_urb_submit(&hcd->self, urb);
 
        /*
         * Atomically queue the urb,  first to our records, then to the HCD.
@@ -1081,18 +1133,36 @@ 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 (unlikely (urb->reject))
+       ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
+                       [usb_pipeendpoint(urb->pipe)];
+       if (unlikely (!ep))
+               status = -ENOENT;
+       else 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);
+       else switch (hcd->state) {
+       case HC_STATE_RUNNING:
+       case HC_STATE_RESUMING:
+doit:
+               list_add_tail (&urb->urb_list, &ep->urb_list);
                status = 0;
-       } else
+               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);
        if (status) {
                INIT_LIST_HEAD (&urb->urb_list);
+               usbmon_urb_submit_error(&hcd->self, urb, status);
                return status;
        }
 
@@ -1109,8 +1179,6 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
                 * valid and usb_buffer_{sync,unmap}() not be needed, since
                 * they could clobber root hub response data.
                 */
-               urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
-                                       | URB_NO_SETUP_DMA_MAP);
                status = rh_urb_enqueue (hcd, urb);
                goto done;
        }
@@ -1137,7 +1205,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
                                            : DMA_TO_DEVICE);
        }
 
-       status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
+       status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);
 done:
        if (unlikely (status)) {
                urb_unlink (urb);
@@ -1145,6 +1213,7 @@ done:
                if (urb->reject)
                        wake_up (&usb_kill_urb_queue);
                usb_put_urb (urb);
+               usbmon_urb_submit_error(&hcd->self, urb, status);
        }
        return status;
 }
@@ -1155,7 +1224,7 @@ done:
 static int hcd_get_frame_number (struct usb_device *udev)
 {
        struct usb_hcd  *hcd = (struct usb_hcd *)udev->bus->hcpriv;
-       if (!HCD_IS_RUNNING (hcd->state))
+       if (!HC_IS_RUNNING (hcd->state))
                return -ESHUTDOWN;
        return hcd->driver->get_frame_number (hcd);
 }
@@ -1172,8 +1241,8 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
 {
        int             value;
 
-       if (urb == (struct urb *) hcd->rh_timer.data)
-               value = usb_rh_status_dequeue (hcd, urb);
+       if (urb->dev == hcd->self.root_hub)
+               value = usb_rh_urb_dequeue (hcd, urb);
        else {
 
                /* The only reason an HCD might fail this call is if
@@ -1196,7 +1265,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
  */
 static int hcd_unlink_urb (struct urb *urb, int status)
 {
-       struct hcd_dev                  *dev;
+       struct usb_host_endpoint        *ep;
        struct usb_hcd                  *hcd = NULL;
        struct device                   *sys = NULL;
        unsigned long                   flags;
@@ -1205,6 +1274,12 @@ static int hcd_unlink_urb (struct urb *urb, int status)
 
        if (!urb)
                return -EINVAL;
+       if (!urb->dev || !urb->dev->bus)
+               return -ENODEV;
+       ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
+                       [usb_pipeendpoint(urb->pipe)];
+       if (!ep)
+               return -ENODEV;
 
        /*
         * we contend for urb->status with the hcd core,
@@ -1220,27 +1295,15 @@ static int hcd_unlink_urb (struct urb *urb, int status)
        spin_lock_irqsave (&urb->lock, flags);
        spin_lock (&hcd_data_lock);
 
-       if (!urb->dev || !urb->dev->bus) {
-               retval = -ENODEV;
-               goto done;
-       }
-
-       dev = urb->dev->hcpriv;
        sys = &urb->dev->dev;
        hcd = urb->dev->bus->hcpriv;
-       if (!dev || !hcd) {
+       if (hcd == NULL) {
                retval = -ENODEV;
                goto done;
        }
 
-       /* running ~= hc unlink handshake works (irq, timer, etc)
-        * halted ~= no unlink handshake is needed
-        * suspended, resuming == should never happen
-        */
-       WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);
-
        /* insist the urb is still queued */
-       list_for_each(tmp, &dev->urb_list) {
+       list_for_each(tmp, &ep->urb_list) {
                if (tmp == &urb->urb_list)
                        break;
        }
@@ -1257,15 +1320,17 @@ static int hcd_unlink_urb (struct urb *urb, int status)
                goto done;
        }
 
-       /* PCI IRQ setup can easily be broken so that USB controllers
+       /* IRQ setup can easily be broken so that USB controllers
         * never get completion IRQs ... maybe even the ones we need to
-        * finish unlinking the initial failed usb_set_address().
+        * finish unlinking the initial failed usb_set_address()
+        * or device descriptor fetch.
         */
-       if (!hcd->saw_irq) {
+       if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags)
+           && hcd->self.root_hub != urb->dev) {
                dev_warn (hcd->self.controller, "Unlink after no-IRQ?  "
-                       "Different ACPI or APIC settings may help."
+                       "Controller is probably using the wrong IRQ."
                        "\n");
-               hcd->saw_irq = 1;
+               set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
        }
 
        urb->status = status;
@@ -1292,49 +1357,37 @@ done:
  * the hcd to make sure all endpoint state is gone from hardware. use for
  * set_configuration, set_interface, driver removal, physical disconnect.
  *
- * example:  a qh stored in hcd_dev.ep[], holding state related to endpoint
+ * 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, int endpoint)
+static void
+hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
 {
-       struct hcd_dev  *dev;
-       struct usb_hcd  *hcd;
-       struct urb      *urb;
-       unsigned        epnum = endpoint & USB_ENDPOINT_NUMBER_MASK;
+       struct usb_hcd          *hcd;
+       struct urb              *urb;
 
-       dev = udev->hcpriv;
        hcd = udev->bus->hcpriv;
 
-       WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);
+       WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
+                       udev->state != USB_STATE_NOTATTACHED);
 
        local_irq_disable ();
 
-rescan:
-       /* (re)block new requests, as best we can */
-       if (endpoint & USB_DIR_IN) {
-               usb_endpoint_halt (udev, epnum, 0);
-               udev->epmaxpacketin [epnum] = 0;
-       } else {
-               usb_endpoint_halt (udev, epnum, 1);
-               udev->epmaxpacketout [epnum] = 0;
-       }
+       /* FIXME move most of this into message.c as part of its
+        * endpoint disable logic
+        */
 
-       /* then kill any current requests */
+       /* ep is already gone from udev->ep_{in,out}[]; no more submits */
+rescan:
        spin_lock (&hcd_data_lock);
-       list_for_each_entry (urb, &dev->urb_list, urb_list) {
-               int     tmp = urb->pipe;
-
-               /* ignore urbs for other endpoints */
-               if (usb_pipeendpoint (tmp) != epnum)
-                       continue;
-               /* NOTE assumption that only ep0 is a control endpoint */
-               if (epnum != 0 && ((tmp ^ endpoint) & USB_DIR_IN))
-                       continue;
+       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...
                 */
                if (urb->status != -EINPROGRESS)
                        continue;
@@ -1376,97 +1429,171 @@ rescan:
         */
        might_sleep ();
        if (hcd->driver->endpoint_disable)
-               hcd->driver->endpoint_disable (hcd, dev, endpoint);
+               hcd->driver->endpoint_disable (hcd, ep);
 }
 
 /*-------------------------------------------------------------------------*/
 
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM
 
-static int hcd_hub_suspend (struct usb_bus *bus)
+int hcd_bus_suspend (struct usb_bus *bus)
 {
        struct usb_hcd          *hcd;
+       int                     status;
 
        hcd = container_of (bus, struct usb_hcd, self);
-       if (hcd->driver->hub_suspend)
-               return hcd->driver->hub_suspend (hcd);
-       return 0;
+       if (!hcd->driver->bus_suspend)
+               return -ENOENT;
+       hcd->state = HC_STATE_QUIESCING;
+       status = hcd->driver->bus_suspend (hcd);
+       if (status == 0)
+               hcd->state = HC_STATE_SUSPENDED;
+       else
+               dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
+                               "suspend", status);
+       return status;
 }
 
-static int hcd_hub_resume (struct usb_bus *bus)
+int hcd_bus_resume (struct usb_bus *bus)
 {
        struct usb_hcd          *hcd;
+       int                     status;
 
        hcd = container_of (bus, struct usb_hcd, self);
-       if (hcd->driver->hub_resume)
-               return hcd->driver->hub_resume (hcd);
-       return 0;
+       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)
+               hcd->state = HC_STATE_RUNNING;
+       else {
+               dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
+                               "resume", status);
+               usb_hc_died(hcd);
+       }
+       return status;
 }
 
-#endif
+/*
+ * 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)
+{
+       struct urb      *urb;
 
-/*-------------------------------------------------------------------------*/
+       spin_lock (&hcd_root_hub_lock);
+       usb_suspend_root_hub (hcd->self.root_hub);
 
-/* 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.
- */
+       /* 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;
 
-static int hcd_free_dev (struct usb_device *udev)
+               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);
+}
+EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
+
+/**
+ * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
+ * @hcd: host controller for this 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).
+ */
+void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
 {
-       struct hcd_dev          *dev;
-       struct usb_hcd          *hcd;
-       unsigned long           flags;
+       unsigned long flags;
 
-       if (!udev || !udev->hcpriv)
-               return -EINVAL;
+       spin_lock_irqsave (&hcd_root_hub_lock, flags);
+       if (hcd->rh_registered)
+               usb_resume_root_hub (hcd->self.root_hub);
+       spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
 
-       if (!udev->bus || !udev->bus->hcpriv)
-               return -ENODEV;
+#endif
 
-       // should udev->devnum == -1 ??
+/*-------------------------------------------------------------------------*/
 
-       dev = udev->hcpriv;
-       hcd = udev->bus->hcpriv;
+#ifdef CONFIG_USB_OTG
 
-       /* device driver problem with refcounts? */
-       if (!list_empty (&dev->urb_list)) {
-               dev_dbg (hcd->self.controller, "free busy dev, %s devnum %d (bug!)\n",
-                       hcd->self.bus_name, udev->devnum);
-               return -EINVAL;
-       }
+/**
+ * usb_bus_start_enum - start immediate enumeration (for OTG)
+ * @bus: the bus (must use hcd framework)
+ * @port_num: 1-based number of port; usually bus->otg_port
+ * Context: in_interrupt()
+ *
+ * Starts enumeration, with an immediate reset followed later by
+ * khubd identifying and possibly configuring the device.
+ * This is needed by OTG controller drivers, where it helps meet
+ * HNP protocol timing requirements for starting a port reset.
+ */
+int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)
+{
+       struct usb_hcd          *hcd;
+       int                     status = -EOPNOTSUPP;
 
-       spin_lock_irqsave (&hcd_data_lock, flags);
-       list_del (&dev->dev_list);
-       udev->hcpriv = NULL;
-       spin_unlock_irqrestore (&hcd_data_lock, flags);
+       /* NOTE: since HNP can't start by grabbing the bus's address0_sem,
+        * boards with root hubs hooked up to internal devices (instead of
+        * just the OTG port) may need more attention to resetting...
+        */
+       hcd = container_of (bus, struct usb_hcd, self);
+       if (port_num && hcd->driver->start_port_reset)
+               status = hcd->driver->start_port_reset(hcd, port_num);
 
-       kfree (dev);
-       return 0;
+       /* run khubd shortly after (first) root port reset finishes;
+        * it may issue others, until at least 50 msecs have passed.
+        */
+       if (status == 0)
+               mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(10));
+       return status;
 }
+EXPORT_SYMBOL (usb_bus_start_enum);
+
+#endif
+
+/*-------------------------------------------------------------------------*/
 
 /*
  * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
- *
- * When registering a USB bus through the HCD framework code, use this
- * usb_operations vector.  The PCI glue layer does so automatically; only
- * bus glue for non-PCI system busses will need to use this.
  */
-struct usb_operations usb_hcd_operations = {
-       .allocate =             hcd_alloc_dev,
+static struct usb_operations usb_hcd_operations = {
        .get_frame_number =     hcd_get_frame_number,
        .submit_urb =           hcd_submit_urb,
        .unlink_urb =           hcd_unlink_urb,
-       .deallocate =           hcd_free_dev,
        .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);
 
 /*-------------------------------------------------------------------------*/
 
@@ -1485,14 +1612,13 @@ EXPORT_SYMBOL (usb_hcd_operations);
  */
 void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
 {
-       urb_unlink (urb);
+       int at_root_hub;
 
-       // NOTE:  a generic device/urb monitoring hook would go here.
-       // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
-       // It would catch exit/unlink paths for all urbs.
+       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) {
+       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,
@@ -1508,6 +1634,7 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs
                                            : DMA_TO_DEVICE);
        }
 
+       usbmon_urb_complete (&hcd->self, urb);
        /* pass ownership to the completion handler */
        urb->complete (urb, regs);
        atomic_dec (&urb->use_count);
@@ -1522,49 +1649,32 @@ EXPORT_SYMBOL (usb_hcd_giveback_urb);
 /**
  * usb_hcd_irq - hook IRQs to HCD framework (bus glue)
  * @irq: the IRQ being raised
- * @__hcd: pointer to the HCD whose IRQ is beinng signaled
+ * @__hcd: pointer to the HCD whose IRQ is being signaled
  * @r: saved hardware registers
  *
- * When registering a USB bus through the HCD framework code, use this
- * to handle interrupts.  The PCI glue layer does so automatically; only
- * bus glue for non-PCI system busses will need to use this.
+ * 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)
 {
        struct usb_hcd          *hcd = __hcd;
        int                     start = hcd->state;
 
-       if (unlikely (hcd->state == USB_STATE_HALT))    /* irq sharing? */
+       if (unlikely(start == HC_STATE_HALT ||
+           !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
                return IRQ_NONE;
-
-       hcd->saw_irq = 1;
        if (hcd->driver->irq (hcd, r) == IRQ_NONE)
                return IRQ_NONE;
 
-       if (hcd->state != start && hcd->state == USB_STATE_HALT)
+       set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+       if (unlikely(hcd->state == HC_STATE_HALT))
                usb_hc_died (hcd);
        return IRQ_HANDLED;
 }
-EXPORT_SYMBOL (usb_hcd_irq);
 
 /*-------------------------------------------------------------------------*/
 
-static void hcd_panic (void *_hcd)
-{
-       struct usb_hcd          *hcd = _hcd;
-       struct usb_device       *hub = hcd->self.root_hub;
-       unsigned                i;
-
-       /* hc's root hub is removed later removed in hcd->stop() */
-       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);
-}
-
 /**
  * usb_hc_died - report abnormal shutdown of a host controller (bus glue)
  * @hcd: pointer to the HCD representing the controller
@@ -1575,11 +1685,272 @@ static void hcd_panic (void *_hcd)
  */
 void usb_hc_died (struct usb_hcd *hcd)
 {
+       unsigned long flags;
+
        dev_err (hcd->self.controller, "HC died; cleaning up\n");
 
-       /* clean up old urbs and devices; needs a task context */
-       INIT_WORK (&hcd->work, hcd_panic, hcd);
-       (void) schedule_work (&hcd->work);
+       spin_lock_irqsave (&hcd_root_hub_lock, flags);
+       if (hcd->rh_registered) {
+               hcd->poll_rh = 0;
+
+               /* make khubd clean up old urbs and devices */
+               usb_set_device_state (hcd->self.root_hub,
+                               USB_STATE_NOTATTACHED);
+               usb_kick_khubd (hcd->self.root_hub);
+       }
+       spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
+}
+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
+ * @dev: device for this HC, stored in hcd->self.controller
+ * @bus_name: value to store in hcd->self.bus_name
+ * Context: !in_interrupt()
+ *
+ * Allocate a struct usb_hcd, with extra space at the end for the
+ * HC driver's private data.  Initialize the generic members of the
+ * hcd structure.
+ *
+ * If memory is unavailable, returns NULL.
+ */
+struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
+               struct device *dev, char *bus_name)
+{
+       struct usb_hcd *hcd;
+
+       hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
+       if (!hcd) {
+               dev_dbg (dev, "hcd alloc failed\n");
+               return NULL;
+       }
+       dev_set_drvdata(dev, hcd);
+
+       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;
+
+       init_timer(&hcd->rh_timer);
+       hcd->rh_timer.function = rh_timer_func;
+       hcd->rh_timer.data = (unsigned long) hcd;
+
+       hcd->driver = driver;
+       hcd->product_desc = (driver->product_desc) ? driver->product_desc :
+                       "USB Host Controller";
+
+       return hcd;
+}
+EXPORT_SYMBOL (usb_create_hcd);
+
+void usb_put_hcd (struct usb_hcd *hcd)
+{
+       dev_set_drvdata(hcd->self.controller, NULL);
+       usb_bus_put(&hcd->self);
+}
+EXPORT_SYMBOL (usb_put_hcd);
+
+/**
+ * usb_add_hcd - finish generic HCD structure initialization and register
+ * @hcd: the usb_hcd structure to initialize
+ * @irqnum: Interrupt line to allocate
+ * @irqflags: Interrupt type flags
+ *
+ * Finish the remaining parts of generic HCD initialization: allocate the
+ * buffers of consistent memory, register the bus, request the IRQ line,
+ * and call the driver's reset() and start() routines.
+ */
+int usb_add_hcd(struct usb_hcd *hcd,
+               unsigned int irqnum, unsigned long irqflags)
+{
+       int retval;
+       struct usb_device *rhdev;
+
+       dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
+
+       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+       /* HC is in reset state, but accessible.  Now do the one-time init,
+        * bottom up so that hcds can customize the root hubs before khubd
+        * starts talking to them.  (Note, bus id is assigned early too.)
+        */
+       if ((retval = hcd_buffer_create(hcd)) != 0) {
+               dev_dbg(hcd->self.controller, "pool alloc failed\n");
+               return retval;
+       }
+
+       if ((retval = usb_register_bus(&hcd->self)) < 0)
+               goto err_register_bus;
+
+       if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
+               dev_err(hcd->self.controller, "unable to allocate root hub\n");
+               retval = -ENOMEM;
+               goto err_allocate_root_hub;
+       }
+       rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
+                       USB_SPEED_FULL;
+       hcd->self.root_hub = rhdev;
+
+       /* wakeup flag init defaults to "everything works" for root hubs,
+        * but drivers can override it in reset() if needed, along with
+        * recording the overall controller's system wakeup capability.
+        */
+       device_init_wakeup(&rhdev->dev, 1);
+
+       /* "reset" is misnamed; its role is now one-time init. the controller
+        * should already have been reset (and boot firmware kicked off etc).
+        */
+       if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
+               dev_err(hcd->self.controller, "can't setup\n");
+               goto err_hcd_driver_setup;
+       }
+
+       /* NOTE: root hub and controller capabilities may not be the same */
+       if (device_can_wakeup(hcd->self.controller)
+                       && device_can_wakeup(&hcd->self.root_hub->dev))
+               dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
+
+       /* enable irqs just before we start the controller */
+       if (hcd->driver->irq) {
+               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 %d failed\n", irqnum);
+                       goto err_request_irq;
+               }
+               hcd->irq = irqnum;
+               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);
+       } else {
+               hcd->irq = -1;
+               if (hcd->rsrc_start)
+                       dev_info(hcd->self.controller, "%s 0x%08llx\n",
+                                       (hcd->driver->flags & HCD_MEMORY) ?
+                                       "io mem" : "io base",
+                                       (unsigned long long)hcd->rsrc_start);
+       }
+
+       if ((retval = hcd->driver->start(hcd)) < 0) {
+               dev_err(hcd->self.controller, "startup error %d\n", retval);
+               goto err_hcd_driver_start;
+       }
+
+       /* starting here, usbcore will pay attention to this root hub */
+       rhdev->bus_mA = min(500u, hcd->power_budget);
+       if ((retval = register_root_hub(hcd)) != 0)
+               goto err_register_root_hub;
+
+       if (hcd->uses_new_polling && hcd->poll_rh)
+               usb_hcd_poll_rh_status(hcd);
+       return retval;
+
+err_register_root_hub:
+       hcd->driver->stop(hcd);
+err_hcd_driver_start:
+       if (hcd->irq >= 0)
+               free_irq(irqnum, hcd);
+err_request_irq:
+err_hcd_driver_setup:
+       hcd->self.root_hub = NULL;
+       usb_put_dev(rhdev);
+err_allocate_root_hub:
+       usb_deregister_bus(&hcd->self);
+err_register_bus:
+       hcd_buffer_destroy(hcd);
+       return retval;
+} 
+EXPORT_SYMBOL (usb_add_hcd);
+
+/**
+ * usb_remove_hcd - shutdown processing for generic HCDs
+ * @hcd: the usb_hcd structure to remove
+ * Context: !in_interrupt()
+ *
+ * Disconnects the root hub, then reverses the effects of usb_add_hcd(),
+ * invoking the HCD's stop() method.
+ */
+void usb_remove_hcd(struct usb_hcd *hcd)
+{
+       dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
+
+       if (HC_IS_RUNNING (hcd->state))
+               hcd->state = HC_STATE_QUIESCING;
+
+       dev_dbg(hcd->self.controller, "roothub graceful disconnect\n");
+       spin_lock_irq (&hcd_root_hub_lock);
+       hcd->rh_registered = 0;
+       spin_unlock_irq (&hcd_root_hub_lock);
+
+       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;
+
+       if (hcd->irq >= 0)
+               free_irq(hcd->irq, hcd);
+       usb_deregister_bus(&hcd->self);
+       hcd_buffer_destroy(hcd);
+}
+EXPORT_SYMBOL (usb_remove_hcd);
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_USB_MON)
+
+struct usb_mon_operations *mon_ops;
+
+/*
+ * The registration is unlocked.
+ * We do it this way because we do not want to lock in hot paths.
+ *
+ * Notice that the code is minimally error-proof. Because usbmon needs
+ * symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
+ */
+int usb_mon_register (struct usb_mon_operations *ops)
+{
+
+       if (mon_ops)
+               return -EBUSY;
+
+       mon_ops = ops;
+       mb();
+       return 0;
+}
+EXPORT_SYMBOL_GPL (usb_mon_register);
+
+void usb_mon_deregister (void)
+{
+
+       if (mon_ops == NULL) {
+               printk(KERN_ERR "USB: monitor was not registered\n");
+               return;
+       }
+       mon_ops = NULL;
+       mb();
 }
-EXPORT_SYMBOL (usb_hc_died);
+EXPORT_SYMBOL_GPL (usb_mon_deregister);
 
+#endif /* CONFIG_USB_MON */