[PATCH] USB: convert usbfs/inode.c to use usb notifiers
[sfrench/cifs-2.6.git] / drivers / usb / core / hub.c
index c3e2024c4347073bc5f36641720058b4d1209c14..6a2ebd89916f596050014f5625ee0bae12367c02 100644 (file)
@@ -436,9 +436,10 @@ static void hub_power_on(struct usb_hub *hub)
 {
        int port1;
        unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
+       u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
        /* if hub supports power switching, enable power on each port */
-       if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
+       if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
                dev_dbg(hub->intfdev, "enabling power on all ports\n");
                for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
                        set_port_feature(hub->hdev, port1,
@@ -449,10 +450,18 @@ static void hub_power_on(struct usb_hub *hub)
        msleep(max(pgood_delay, (unsigned) 100));
 }
 
-static void hub_quiesce(struct usb_hub *hub)
+static inline void __hub_quiesce(struct usb_hub *hub)
 {
-       /* stop khubd and related activity */
+       /* (nonblocking) khubd and related activity won't re-trigger */
        hub->quiescing = 1;
+       hub->activating = 0;
+       hub->resume_root_hub = 0;
+}
+
+static void hub_quiesce(struct usb_hub *hub)
+{
+       /* (blocking) stop khubd and related activity */
+       __hub_quiesce(hub);
        usb_kill_urb(hub->urb);
        if (hub->has_indicators)
                cancel_delayed_work(&hub->leds);
@@ -466,6 +475,7 @@ static void hub_activate(struct usb_hub *hub)
 
        hub->quiescing = 0;
        hub->activating = 1;
+       hub->resume_root_hub = 0;
        status = usb_submit_urb(hub->urb, GFP_NOIO);
        if (status < 0)
                dev_err(hub->intfdev, "activate --> %d\n", status);
@@ -516,6 +526,7 @@ static int hub_configure(struct usb_hub *hub,
        struct usb_device *hdev = hub->hdev;
        struct device *hub_dev = hub->intfdev;
        u16 hubstatus, hubchange;
+       u16 wHubCharacteristics;
        unsigned int pipe;
        int maxp, ret;
        char *message;
@@ -561,9 +572,9 @@ static int hub_configure(struct usb_hub *hub,
        dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
                (hdev->maxchild == 1) ? "" : "s");
 
-       le16_to_cpus(&hub->descriptor->wHubCharacteristics);
+       wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
-       if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) {
+       if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
                int     i;
                char    portstr [USB_MAXCHILDREN + 1];
 
@@ -576,7 +587,7 @@ static int hub_configure(struct usb_hub *hub,
        } else
                dev_dbg(hub_dev, "standalone hub\n");
 
-       switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) {
+       switch (wHubCharacteristics & HUB_CHAR_LPSM) {
                case 0x00:
                        dev_dbg(hub_dev, "ganged power switching\n");
                        break;
@@ -589,7 +600,7 @@ static int hub_configure(struct usb_hub *hub,
                        break;
        }
 
-       switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) {
+       switch (wHubCharacteristics & HUB_CHAR_OCPM) {
                case 0x00:
                        dev_dbg(hub_dev, "global over-current protection\n");
                        break;
@@ -629,7 +640,7 @@ static int hub_configure(struct usb_hub *hub,
        }
 
        /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
-       switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {
+       switch (wHubCharacteristics & HUB_CHAR_TTTT) {
                case HUB_TTTT_8_BITS:
                        if (hdev->descriptor.bDeviceProtocol != 0) {
                                hub->tt.think_time = 666;
@@ -659,7 +670,7 @@ static int hub_configure(struct usb_hub *hub,
        }
 
        /* probe() zeroes hub->indicator[] */
-       if (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND) {
+       if (wHubCharacteristics & HUB_CHAR_PORTIND) {
                hub->has_indicators = 1;
                dev_dbg(hub_dev, "Port indicators are supported\n");
        }
@@ -704,7 +715,7 @@ static int hub_configure(struct usb_hub *hub,
                        (hubstatus & HUB_STATUS_LOCAL_POWER)
                        ? "lost (inactive)" : "good");
 
-       if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) == 0)
+       if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)
                dev_dbg(hub_dev, "%sover-current condition exists\n",
                        (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
 
@@ -1117,14 +1128,14 @@ void usb_disconnect(struct usb_device **pdev)
         */
        usb_disable_device(udev, 0);
 
+       usb_notify_remove_device(udev);
+
        /* Free the device number, remove the /proc/bus/usb entry and
         * the sysfs attributes, and delete the parent's children[]
         * (or root_hub) pointer.
         */
        dev_dbg (&udev->dev, "unregistering device\n");
        release_address(udev);
-       usbfs_remove_device(udev);
-       usbdev_remove(udev);
        usb_remove_sysfs_dev_files(udev);
 
        /* Avoid races with recursively_mark_NOTATTACHED() */
@@ -1322,11 +1333,9 @@ int usb_new_device(struct usb_device *udev)
                 * (Includes HNP test device.)
                 */
                if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
-                       static int __usb_suspend_device (struct usb_device *,
-                                               int port1, pm_message_t state);
-                       err = __usb_suspend_device(udev,
-                                       udev->bus->otg_port,
-                                       PMSG_SUSPEND);
+                       static int __usb_suspend_device(struct usb_device *,
+                                               int port1);
+                       err = __usb_suspend_device(udev, udev->bus->otg_port);
                        if (err < 0)
                                dev_dbg(&udev->dev, "HNP fail, %d\n", err);
                }
@@ -1362,10 +1371,8 @@ int usb_new_device(struct usb_device *udev)
        }
 
        /* USB device state == configured ... usable */
+       usb_notify_add_device(udev);
 
-       /* add a /proc/bus/usb entry */
-       usbdev_add(udev);
-       usbfs_add_device(udev);
        return 0;
 
 fail:
@@ -1516,7 +1523,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
        /* FIXME let caller ask to power down the port:
         *  - some devices won't enumerate without a VBUS power cycle
         *  - SRP saves power that way
-        *  - usb_suspend_device(dev, PMSG_SUSPEND)
+        *  - ... new call, TBD ...
         * That's easy if this hub can switch power per-port, and
         * khubd reactivates the port later (timer, SRP, etc).
         * Powerdown must be optional, because of reset/DFU.
@@ -1598,11 +1605,14 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
  * Other than re-initializing the hub (plug/unplug, except for root hubs),
  * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
  * timer, no SRP, no requests through sysfs.
+ *
+ * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
+ * the root hub for their bus goes into global suspend ... so we don't
+ * (falsely) update the device power state to say it suspended.
  */
-static int __usb_suspend_device (struct usb_device *udev, int port1,
-                                pm_message_t state)
+static int __usb_suspend_device (struct usb_device *udev, int port1)
 {
-       int     status;
+       int     status = 0;
 
        /* caller owns the udev device lock */
        if (port1 < 0)
@@ -1613,95 +1623,39 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
                return 0;
        }
 
-       /* suspend interface drivers; if this is a hub, it
-        * suspends the child devices
-        */
+       /* all interfaces must already be suspended */
        if (udev->actconfig) {
                int     i;
 
                for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
                        struct usb_interface    *intf;
-                       struct usb_driver       *driver;
 
                        intf = udev->actconfig->interface[i];
-                       if (state.event <= intf->dev.power.power_state.event)
-                               continue;
-                       if (!intf->dev.driver)
-                               continue;
-                       driver = to_usb_driver(intf->dev.driver);
-
-                       if (driver->suspend) {
-                               status = driver->suspend(intf, state);
-                               if (intf->dev.power.power_state.event != state.event
-                                               || status)
-                                       dev_err(&intf->dev,
-                                               "suspend %d fail, code %d\n",
-                                               state.event, status);
-                       }
-
-                       /* only drivers with suspend() can ever resume();
-                        * and after power loss, even they won't.
-                        * bus_rescan_devices() can rebind drivers later.
-                        *
-                        * FIXME the PM core self-deadlocks when unbinding
-                        * drivers during suspend/resume ... everything grabs
-                        * dpm_sem (not a spinlock, ugh).  we want to unbind,
-                        * since we know every driver's probe/disconnect works
-                        * even for drivers that can't suspend.
-                        */
-                       if (!driver->suspend || state.event > PM_EVENT_FREEZE) {
-#if 1
-                               dev_warn(&intf->dev, "resume is unsafe!\n");
-#else
-                               down_write(&usb_bus_type.rwsem);
-                               device_release_driver(&intf->dev);
-                               up_write(&usb_bus_type.rwsem);
-#endif
+                       if (is_active(intf)) {
+                               dev_dbg(&intf->dev, "nyet suspended\n");
+                               return -EBUSY;
                        }
                }
        }
 
-       /*
-        * FIXME this needs port power off call paths too, to help force
-        * USB into the "generic" PM model.  At least for devices on
-        * ports that aren't using ganged switching (usually root hubs).
-        *
-        * NOTE: SRP-capable links should adopt more aggressive poweroff
-        * policies (when HNP doesn't apply) once we have mechanisms to
-        * turn power back on!  (Likely not before 2.7...)
+       /* we only change a device's upstream USB link.
+        * root hubs have no upstream USB link.
         */
-       if (state.event > PM_EVENT_FREEZE) {
-               dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");
-       }
-
-       /* "global suspend" of the HC-to-USB interface (root hub), or
-        * "selective suspend" of just one hub-device link.
-        */
-       if (!udev->parent) {
-               struct usb_bus  *bus = udev->bus;
-               if (bus && bus->op->hub_suspend) {
-                       status = bus->op->hub_suspend (bus);
-                       if (status == 0) {
-                               dev_dbg(&udev->dev, "usb suspend\n");
-                               usb_set_device_state(udev,
-                                               USB_STATE_SUSPENDED);
-                       }
-               } else
-                       status = -EOPNOTSUPP;
-       } else
+       if (udev->parent)
                status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
                                udev);
 
        if (status == 0)
-               udev->dev.power.power_state = state;
+               udev->dev.power.power_state = PMSG_SUSPEND;
        return status;
 }
 
-/**
+#endif
+
+/*
  * usb_suspend_device - suspend a usb device
  * @udev: device that's no longer in active use
- * @state: PMSG_SUSPEND to suspend
- * Context: must be able to sleep; device not locked
+ * Context: must be able to sleep; device not locked; pm locks held
  *
  * Suspends a USB device that isn't in active use, conserving power.
  * Devices may wake out of a suspend, if anything important happens,
@@ -1709,37 +1663,50 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
  * suspend by the host, using usb_resume_device().  It's also routine
  * to disconnect devices while they are suspended.
  *
+ * This only affects the USB hardware for a device; its interfaces
+ * (and, for hubs, child devices) must already have been suspended.
+ *
  * Suspending OTG devices may trigger HNP, if that's been enabled
  * between a pair of dual-role devices.  That will change roles, such
  * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
  *
  * Returns 0 on success, else negative errno.
  */
-int usb_suspend_device(struct usb_device *udev, pm_message_t state)
+int usb_suspend_device(struct usb_device *udev)
 {
+#ifdef CONFIG_USB_SUSPEND
        int     port1, status;
 
        port1 = locktree(udev);
        if (port1 < 0)
                return port1;
 
-       status = __usb_suspend_device(udev, port1, state);
+       status = __usb_suspend_device(udev, port1);
        usb_unlock_device(udev);
        return status;
+#else
+       /* NOTE:  udev->state unchanged, it's not lying ... */
+       udev->dev.power.power_state = PMSG_SUSPEND;
+       return 0;
+#endif
 }
 
+
 /*
+ * If the USB "suspend" state is in use (rather than "global suspend"),
+ * many devices will be individually taken out of suspend state using
+ * special" resume" signaling.  These routines kick in shortly after
  * hardware resume signaling is finished, either because of selective
  * resume (by host) or remote wakeup (by device) ... now see what changed
  * in the tree that's rooted at this device.
  */
-static int finish_port_resume(struct usb_device *udev)
+static int finish_device_resume(struct usb_device *udev)
 {
        int     status;
        u16     devstatus;
 
        /* caller owns the udev device lock */
-       dev_dbg(&udev->dev, "usb resume\n");
+       dev_dbg(&udev->dev, "finish resume\n");
 
        /* usb ch9 identifies four variants of SUSPENDED, based on what
         * state the device resumes to.  Linux currently won't see the
@@ -1749,7 +1716,6 @@ static int finish_port_resume(struct usb_device *udev)
        usb_set_device_state(udev, udev->actconfig
                        ? USB_STATE_CONFIGURED
                        : USB_STATE_ADDRESS);
-       udev->dev.power.power_state = PMSG_ON;
 
        /* 10.5.4.5 says be sure devices in the tree are still there.
         * For now let's assume the device didn't go crazy on resume,
@@ -1762,9 +1728,11 @@ static int finish_port_resume(struct usb_device *udev)
                        status);
        else if (udev->actconfig) {
                unsigned        i;
+               int             (*resume)(struct device *);
 
                le16_to_cpus(&devstatus);
-               if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
+               if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)
+                               && udev->parent) {
                        status = usb_control_msg(udev,
                                        usb_sndctrlpipe(udev, 0),
                                        USB_REQ_CLEAR_FEATURE,
@@ -1780,33 +1748,11 @@ static int finish_port_resume(struct usb_device *udev)
                }
 
                /* resume interface drivers; if this is a hub, it
-                * resumes the child devices
+                * may have a child resume event to deal with soon
                 */
-               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
-                       struct usb_interface    *intf;
-                       struct usb_driver       *driver;
-
-                       intf = udev->actconfig->interface[i];
-                       if (intf->dev.power.power_state.event == PM_EVENT_ON)
-                               continue;
-                       if (!intf->dev.driver) {
-                               /* FIXME maybe force to alt 0 */
-                               continue;
-                       }
-                       driver = to_usb_driver(intf->dev.driver);
-
-                       /* bus_rescan_devices() may rebind drivers */
-                       if (!driver->resume)
-                               continue;
-
-                       /* can we do better than just logging errors? */
-                       status = driver->resume(intf);
-                       if (intf->dev.power.power_state.event != PM_EVENT_ON
-                                       || status)
-                               dev_dbg(&intf->dev,
-                                       "resume fail, state %d code %d\n",
-                                       intf->dev.power.power_state.event, status);
-               }
+               resume = udev->dev.bus->resume;
+               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)
+                       (void) resume(&udev->actconfig->interface[i]->dev);
                status = 0;
 
        } else if (udev->devnum <= 0) {
@@ -1816,6 +1762,8 @@ static int finish_port_resume(struct usb_device *udev)
        return status;
 }
 
+#ifdef CONFIG_USB_SUSPEND
+
 static int
 hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 {
@@ -1861,7 +1809,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
                        /* TRSMRCY = 10 msec */
                        msleep(10);
                        if (udev)
-                               status = finish_port_resume(udev);
+                               status = finish_device_resume(udev);
                }
        }
        if (status < 0)
@@ -1870,12 +1818,12 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
        return status;
 }
 
-static int hub_resume (struct usb_interface *intf);
+#endif
 
-/**
+/*
  * usb_resume_device - re-activate a suspended usb device
  * @udev: device to re-activate
- * Context: must be able to sleep; device not locked
+ * Context: must be able to sleep; device not locked; pm locks held
  *
  * This will re-activate the suspended device, increasing power usage
  * while letting drivers communicate again with its endpoints.
@@ -1893,35 +1841,22 @@ int usb_resume_device(struct usb_device *udev)
        if (port1 < 0)
                return port1;
 
-       /* "global resume" of the HC-to-USB interface (root hub), or
-        * selective resume of one hub-to-device port
-        */
-       if (!udev->parent) {
-               struct usb_bus  *bus = udev->bus;
-               if (bus && bus->op->hub_resume) {
-                       status = bus->op->hub_resume (bus);
+#ifdef CONFIG_USB_SUSPEND
+       /* selective resume of one downstream hub-to-device port */
+       if (udev->parent) {
+               if (udev->state == USB_STATE_SUSPENDED) {
+                       // NOTE swsusp may bork us, device state being wrong...
+                       // NOTE this fails if parent is also suspended...
+                       status = hub_port_resume(hdev_to_hub(udev->parent),
+                                       port1, udev);
                } else
-                       status = -EOPNOTSUPP;
-               if (status == 0) {
-                       dev_dbg(&udev->dev, "usb resume\n");
-                       /* TRSMRCY = 10 msec */
-                       msleep(10);
-                       usb_set_device_state (udev, USB_STATE_CONFIGURED);
-                       udev->dev.power.power_state = PMSG_ON;
-                       status = hub_resume (udev
-                                       ->actconfig->interface[0]);
-               }
-       } else if (udev->state == USB_STATE_SUSPENDED) {
-               // NOTE this fails if parent is also suspended...
-               status = hub_port_resume(hdev_to_hub(udev->parent),
-                               port1, udev);
-       } else {
-               status = 0;
-       }
-       if (status < 0) {
+                       status = 0;
+       } else
+#endif
+               status = finish_device_resume(udev);
+       if (status < 0)
                dev_dbg(&udev->dev, "can't resume, status %d\n",
                        status);
-       }
 
        usb_unlock_device(udev);
 
@@ -1938,6 +1873,8 @@ static int remote_wakeup(struct usb_device *udev)
 {
        int     status = 0;
 
+#ifdef CONFIG_USB_SUSPEND
+
        /* don't repeat RESUME sequence if this device
         * was already woken up by some other task
         */
@@ -1946,38 +1883,52 @@ static int remote_wakeup(struct usb_device *udev)
                dev_dbg(&udev->dev, "RESUME (wakeup)\n");
                /* TRSMRCY = 10 msec */
                msleep(10);
-               status = finish_port_resume(udev);
+               status = finish_device_resume(udev);
        }
        up(&udev->serialize);
+#endif
        return status;
 }
 
-static int hub_suspend(struct usb_interface *intf, pm_message_t state)
+static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
 {
        struct usb_hub          *hub = usb_get_intfdata (intf);
        struct usb_device       *hdev = hub->hdev;
        unsigned                port1;
-       int                     status;
 
-       /* stop khubd and related activity */
-       hub_quiesce(hub);
-
-       /* then suspend every port */
+       /* fail if children aren't already suspended */
        for (port1 = 1; port1 <= hdev->maxchild; port1++) {
                struct usb_device       *udev;
 
                udev = hdev->children [port1-1];
-               if (!udev)
-                       continue;
-               down(&udev->serialize);
-               status = __usb_suspend_device(udev, port1, state);
-               up(&udev->serialize);
-               if (status < 0)
-                       dev_dbg(&intf->dev, "suspend port %d --> %d\n",
-                               port1, status);
+               if (udev && (udev->dev.power.power_state.event
+                                       == PM_EVENT_ON
+#ifdef CONFIG_USB_SUSPEND
+                               || udev->state != USB_STATE_SUSPENDED
+#endif
+                               )) {
+                       dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
+                       return -EBUSY;
+               }
        }
 
-       intf->dev.power.power_state = state;
+       /* "global suspend" of the downstream HC-to-USB interface */
+       if (!hdev->parent) {
+               struct usb_bus  *bus = hdev->bus;
+               if (bus) {
+                       int     status = hcd_bus_suspend (bus);
+
+                       if (status != 0) {
+                               dev_dbg(&hdev->dev, "'global' suspend %d\n",
+                                       status);
+                               return status;
+                       }
+               } else
+                       return -EOPNOTSUPP;
+       }
+
+       /* stop khubd and related activity */
+       hub_quiesce(hub);
        return 0;
 }
 
@@ -1985,11 +1936,35 @@ static int hub_resume(struct usb_interface *intf)
 {
        struct usb_device       *hdev = interface_to_usbdev(intf);
        struct usb_hub          *hub = usb_get_intfdata (intf);
-       unsigned                port1;
        int                     status;
 
-       if (intf->dev.power.power_state.event == PM_EVENT_ON)
-               return 0;
+       /* "global resume" of the downstream HC-to-USB interface */
+       if (!hdev->parent) {
+               struct usb_bus  *bus = hdev->bus;
+               if (bus) {
+                       status = hcd_bus_resume (bus);
+                       if (status) {
+                               dev_dbg(&intf->dev, "'global' resume %d\n",
+                                       status);
+                               return status;
+                       }
+               } else
+                       return -EOPNOTSUPP;
+               if (status == 0) {
+                       /* TRSMRCY = 10 msec */
+                       msleep(10);
+               }
+       }
+
+       hub_activate(hub);
+
+       /* REVISIT:  this recursion probably shouldn't exist.  Remove
+        * this code sometime, after retesting with different root and
+        * external hubs.
+        */
+#ifdef CONFIG_USB_SUSPEND
+       {
+       unsigned                port1;
 
        for (port1 = 1; port1 <= hdev->maxchild; port1++) {
                struct usb_device       *udev;
@@ -2015,7 +1990,7 @@ static int hub_resume(struct usb_interface *intf)
                if (portstat & USB_PORT_STAT_SUSPEND)
                        status = hub_port_resume(hub, port1, udev);
                else {
-                       status = finish_port_resume(udev);
+                       status = finish_device_resume(udev);
                        if (status < 0) {
                                dev_dbg(&intf->dev, "resume port %d --> %d\n",
                                        port1, status);
@@ -2024,43 +1999,31 @@ static int hub_resume(struct usb_interface *intf)
                }
                up(&udev->serialize);
        }
-       intf->dev.power.power_state = PMSG_ON;
-
-       hub->resume_root_hub = 0;
-       hub_activate(hub);
+       }
+#endif
        return 0;
 }
 
-void usb_resume_root_hub(struct usb_device *hdev)
+void usb_suspend_root_hub(struct usb_device *hdev)
 {
        struct usb_hub *hub = hdev_to_hub(hdev);
 
-       hub->resume_root_hub = 1;
-       kick_khubd(hub);
+       /* This also makes any led blinker stop retriggering.  We're called
+        * from irq, so the blinker might still be scheduled.  Caller promises
+        * that the root hub status URB will be canceled.
+        */
+       __hub_quiesce(hub);
+       mark_quiesced(to_usb_interface(hub->intfdev));
 }
 
-#else  /* !CONFIG_USB_SUSPEND */
-
-int usb_suspend_device(struct usb_device *udev, pm_message_t state)
+void usb_resume_root_hub(struct usb_device *hdev)
 {
-       return 0;
-}
+       struct usb_hub *hub = hdev_to_hub(hdev);
 
-int usb_resume_device(struct usb_device *udev)
-{
-       return 0;
+       hub->resume_root_hub = 1;
+       kick_khubd(hub);
 }
 
-#define        hub_suspend             NULL
-#define        hub_resume              NULL
-#define        remote_wakeup(x)        0
-
-#endif /* CONFIG_USB_SUSPEND */
-
-EXPORT_SYMBOL(usb_suspend_device);
-EXPORT_SYMBOL(usb_resume_device);
-
-
 
 /* USB 2.0 spec, 7.1.7.3 / fig 7-29:
  *
@@ -2469,6 +2432,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 {
        struct usb_device *hdev = hub->hdev;
        struct device *hub_dev = hub->intfdev;
+       u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
        int status, i;
  
        dev_dbg (hub_dev,
@@ -2506,8 +2470,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
        if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
 
                /* maybe switch power back on (e.g. root hub was reset) */
-               if ((hub->descriptor->wHubCharacteristics
-                                       & HUB_CHAR_LPSM) < 2
+               if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
                                && !(portstatus & (1 << USB_PORT_FEAT_POWER)))
                        set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
  
@@ -2686,21 +2649,30 @@ static void hub_events(void)
                intf = to_usb_interface(hub->intfdev);
                hub_dev = &intf->dev;
 
-               dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
+               i = hub->resume_root_hub;
+
+               dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x%s\n",
                                hdev->state, hub->descriptor
                                        ? hub->descriptor->bNbrPorts
                                        : 0,
                                /* NOTE: expects max 15 ports... */
                                (u16) hub->change_bits[0],
-                               (u16) hub->event_bits[0]);
+                               (u16) hub->event_bits[0],
+                               i ? ", resume root" : "");
 
                usb_get_intf(intf);
-               i = hub->resume_root_hub;
                spin_unlock_irq(&hub_event_lock);
 
-               /* Is this is a root hub wanting to be resumed? */
-               if (i)
-                       usb_resume_device(hdev);
+               /* Is this is a root hub wanting to reactivate the downstream
+                * ports?  If so, be sure the interface resumes even if its
+                * stub "device" node was never suspended.
+                */
+               if (i) {
+                       extern void dpm_runtime_resume(struct device *);
+
+                       dpm_runtime_resume(&hdev->dev);
+                       dpm_runtime_resume(&intf->dev);
+               }
 
                /* Lock the device, then check to see if we were
                 * disconnected while waiting for the lock to succeed. */