[PATCH 2/11] drivers/watchdog: Eliminate a NULL pointer dereference
[safe/jmp/linux-2.6] / drivers / usb / core / hub.c
index a880516..83e7bbb 100644 (file)
 #include <linux/ioctl.h>
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
+#include <linux/usb/hcd.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/pm_runtime.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
 #include "usb.h"
-#include "hcd.h"
-#include "hub.h"
 
 /* if we are in debug mode, always announce new devices */
 #ifdef DEBUG
@@ -45,7 +45,6 @@ struct usb_hub {
 
        /* buffer for urb ... with extra space in case of babble */
        char                    (*buffer)[8];
-       dma_addr_t              buffer_dma;     /* DMA address for buffer */
        union {
                struct usb_hub_status   hub;
                struct usb_port_status  port;
@@ -61,6 +60,8 @@ struct usb_hub {
                                                        status change */
        unsigned long           busy_bits[1];   /* ports being reset or
                                                        resumed */
+       unsigned long           removed_bits[1]; /* ports with a "removed"
+                                                       device present */
 #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
 #error event_bits[] is too short!
 #endif
@@ -152,11 +153,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev);
 
 static inline char *portspeed(int portstatus)
 {
-       if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
+       if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                return "480 Mb/s";
-       else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
+       else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                return "1.5 Mb/s";
-       else if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))
+       else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
                return "5.0 Gb/s";
        else
                return "12 Mb/s";
@@ -374,12 +375,13 @@ static void kick_khubd(struct usb_hub *hub)
 {
        unsigned long   flags;
 
-       /* Suppress autosuspend until khubd runs */
-       atomic_set(&to_usb_interface(hub->intfdev)->pm_usage_cnt, 1);
-
        spin_lock_irqsave(&hub_event_lock, flags);
        if (!hub->disconnected && list_empty(&hub->event_list)) {
                list_add_tail(&hub->event_list, &hub_event_list);
+
+               /* Suppress autosuspend until khubd runs */
+               usb_autopm_get_interface_no_resume(
+                               to_usb_interface(hub->intfdev));
                wake_up(&khubd_wait);
        }
        spin_unlock_irqrestore(&hub_event_lock, flags);
@@ -444,7 +446,7 @@ resubmit:
 static inline int
 hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt)
 {
-       return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+       return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
                               HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo,
                               tt, NULL, 0, 1000);
 }
@@ -636,8 +638,35 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
        kick_khubd(hub);
 }
 
+/**
+ * usb_remove_device - disable a device's port on its parent hub
+ * @udev: device to be disabled and removed
+ * Context: @udev locked, must be able to sleep.
+ *
+ * After @udev's port has been disabled, khubd is notified and it will
+ * see that the device has been disconnected.  When the device is
+ * physically unplugged and something is plugged in, the events will
+ * be received and processed normally.
+ */
+int usb_remove_device(struct usb_device *udev)
+{
+       struct usb_hub *hub;
+       struct usb_interface *intf;
+
+       if (!udev->parent)      /* Can't remove a root hub */
+               return -EINVAL;
+       hub = hdev_to_hub(udev->parent);
+       intf = to_usb_interface(hub->intfdev);
+
+       usb_autopm_get_interface(intf);
+       set_bit(udev->portnum, hub->removed_bits);
+       hub_port_logical_disconnect(hub, udev->portnum);
+       usb_autopm_put_interface(intf);
+       return 0;
+}
+
 enum hub_activation_type {
-       HUB_INIT, HUB_INIT2, HUB_INIT3,
+       HUB_INIT, HUB_INIT2, HUB_INIT3,         /* INITs must come first */
        HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
 };
 
@@ -682,8 +711,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                                        msecs_to_jiffies(delay));
 
                        /* Suppress autosuspend until init is done */
-                       atomic_set(&to_usb_interface(hub->intfdev)->
-                                       pm_usage_cnt, 1);
+                       usb_autopm_get_interface_no_resume(
+                                       to_usb_interface(hub->intfdev));
                        return;         /* Continues at init2: below */
                } else {
                        hub_power_on(hub, true);
@@ -715,8 +744,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                                !(portstatus & USB_PORT_STAT_CONNECTION) ||
                                !udev ||
                                udev->state == USB_STATE_NOTATTACHED)) {
-                       clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
-                       portstatus &= ~USB_PORT_STAT_ENABLE;
+                       /*
+                        * USB3 protocol ports will automatically transition
+                        * to Enabled state when detect an USB3.0 device attach.
+                        * Do not disable USB3 protocol ports.
+                        * FIXME: USB3 root hub and external hubs are treated
+                        * differently here.
+                        */
+                       if (hdev->descriptor.bDeviceProtocol != 3 ||
+                           (!hdev->parent &&
+                            !(portstatus & USB_PORT_STAT_SUPER_SPEED))) {
+                               clear_port_feature(hdev, port1,
+                                                  USB_PORT_FEAT_ENABLE);
+                               portstatus &= ~USB_PORT_STAT_ENABLE;
+                       }
                }
 
                /* Clear status-change flags; we'll debounce later */
@@ -731,6 +772,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                                        USB_PORT_FEAT_C_ENABLE);
                }
 
+               /* We can forget about a "removed" device when there's a
+                * physical disconnect or the connect status changes.
+                */
+               if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+                               (portchange & USB_PORT_STAT_C_CONNECTION))
+                       clear_bit(port1, hub->removed_bits);
+
                if (!udev || udev->state == USB_STATE_NOTATTACHED) {
                        /* Tell khubd to disconnect the device or
                         * check for a new connection
@@ -792,6 +840,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 
        /* Scan all ports that need attention */
        kick_khubd(hub);
+
+       /* Allow autosuspend if it was suppressed */
+       if (type <= HUB_INIT3)
+               usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
 }
 
 /* Implement the continuations for the delays above */
@@ -860,6 +912,7 @@ static int hub_post_reset(struct usb_interface *intf)
 static int hub_configure(struct usb_hub *hub,
        struct usb_endpoint_descriptor *endpoint)
 {
+       struct usb_hcd *hcd;
        struct usb_device *hdev = hub->hdev;
        struct device *hub_dev = hub->intfdev;
        u16 hubstatus, hubchange;
@@ -868,8 +921,7 @@ static int hub_configure(struct usb_hub *hub,
        int maxp, ret;
        char *message = "out of memory";
 
-       hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
-                       &hub->buffer_dma);
+       hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
        if (!hub->buffer) {
                ret = -ENOMEM;
                goto fail;
@@ -1061,6 +1113,19 @@ static int hub_configure(struct usb_hub *hub,
                dev_dbg(hub_dev, "%umA bus power budget for each child\n",
                                hub->mA_per_port);
 
+       /* Update the HCD's internal representation of this hub before khubd
+        * starts getting port status changes for devices under the hub.
+        */
+       hcd = bus_to_hcd(hdev->bus);
+       if (hcd->driver->update_hub_device) {
+               ret = hcd->driver->update_hub_device(hcd, hdev,
+                               &hub->tt, GFP_KERNEL);
+               if (ret < 0) {
+                       message = "can't update HCD hub info";
+                       goto fail;
+               }
+       }
+
        ret = hub_hub_status(hub, &hubstatus, &hubchange);
        if (ret < 0) {
                message = "can't get hub status";
@@ -1097,8 +1162,6 @@ static int hub_configure(struct usb_hub *hub,
 
        usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
                hub, endpoint->bInterval);
-       hub->urb->transfer_dma = hub->buffer_dma;
-       hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
        /* maybe cycle the hub leds */
        if (hub->has_indicators && blinkenlights)
@@ -1130,7 +1193,10 @@ static void hub_disconnect(struct usb_interface *intf)
 
        /* Take the hub off the event list and don't let it be added again */
        spin_lock_irq(&hub_event_lock);
-       list_del_init(&hub->event_list);
+       if (!list_empty(&hub->event_list)) {
+               list_del_init(&hub->event_list);
+               usb_autopm_put_interface_no_suspend(intf);
+       }
        hub->disconnected = 1;
        spin_unlock_irq(&hub_event_lock);
 
@@ -1148,8 +1214,7 @@ static void hub_disconnect(struct usb_interface *intf)
        kfree(hub->port_owners);
        kfree(hub->descriptor);
        kfree(hub->status);
-       usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
-                       hub->buffer_dma);
+       kfree(hub->buffer);
 
        kref_put(&hub->kref, hub_release);
 }
@@ -1164,6 +1229,9 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
        desc = intf->cur_altsetting;
        hdev = interface_to_usbdev(intf);
 
+       /* Hubs have proper suspend/resume support */
+       usb_enable_autosuspend(hdev);
+
        if (hdev->level == MAX_TOPO_LEVEL) {
                dev_err(&intf->dev,
                        "Unsupported bus topology: hub nested too deep\n");
@@ -1342,10 +1410,8 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
                if (udev->children[i])
                        recursively_mark_NOTATTACHED(udev->children[i]);
        }
-       if (udev->state == USB_STATE_SUSPENDED) {
-               udev->discon_suspended = 1;
+       if (udev->state == USB_STATE_SUSPENDED)
                udev->active_duration -= jiffies;
-       }
        udev->state = USB_STATE_NOTATTACHED;
 }
 
@@ -1388,11 +1454,11 @@ void usb_set_device_state(struct usb_device *udev,
                                        || new_state == USB_STATE_SUSPENDED)
                                ;       /* No change to wakeup settings */
                        else if (new_state == USB_STATE_CONFIGURED)
-                               device_init_wakeup(&udev->dev,
+                               device_set_wakeup_capable(&udev->dev,
                                        (udev->actconfig->desc.bmAttributes
                                         & USB_CONFIG_ATT_WAKEUP));
                        else
-                               device_init_wakeup(&udev->dev, 0);
+                               device_set_wakeup_capable(&udev->dev, 0);
                }
                if (udev->state == USB_STATE_SUSPENDED &&
                        new_state != USB_STATE_SUSPENDED)
@@ -1469,31 +1535,15 @@ static void update_address(struct usb_device *udev, int devnum)
                udev->devnum = devnum;
 }
 
-#ifdef CONFIG_USB_SUSPEND
-
-static void usb_stop_pm(struct usb_device *udev)
+static void hub_free_dev(struct usb_device *udev)
 {
-       /* Synchronize with the ksuspend thread to prevent any more
-        * autosuspend requests from being submitted, and decrement
-        * the parent's count of unsuspended children.
-        */
-       usb_pm_lock(udev);
-       if (udev->parent && !udev->discon_suspended)
-               usb_autosuspend_device(udev->parent);
-       usb_pm_unlock(udev);
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
 
-       /* Stop any autosuspend or autoresume requests already submitted */
-       cancel_delayed_work_sync(&udev->autosuspend);
-       cancel_work_sync(&udev->autoresume);
+       /* Root hubs aren't real devices, so don't free HCD resources */
+       if (hcd->driver->free_dev && udev->parent)
+               hcd->driver->free_dev(hcd, udev);
 }
 
-#else
-
-static inline void usb_stop_pm(struct usb_device *udev)
-{ }
-
-#endif
-
 /**
  * usb_disconnect - disconnect a device (usbcore-internal)
  * @pdev: pointer to device being disconnected
@@ -1562,7 +1612,7 @@ void usb_disconnect(struct usb_device **pdev)
        *pdev = NULL;
        spin_unlock_irq(&device_state_lock);
 
-       usb_stop_pm(udev);
+       hub_free_dev(udev);
 
        put_device(&udev->dev);
 }
@@ -1598,12 +1648,12 @@ static inline void announce_device(struct usb_device *udev) { }
 #endif
 
 /**
- * usb_configure_device_otg - FIXME (usbcore-internal)
+ * usb_enumerate_device_otg - FIXME (usbcore-internal)
  * @udev: newly addressed device (in ADDRESS state)
  *
- * Do configuration for On-The-Go devices
+ * Finish enumeration for On-The-Go devices
  */
-static int usb_configure_device_otg(struct usb_device *udev)
+static int usb_enumerate_device_otg(struct usb_device *udev)
 {
        int err = 0;
 
@@ -1616,7 +1666,7 @@ static int usb_configure_device_otg(struct usb_device *udev)
        if (!udev->bus->is_b_host
                        && udev->config
                        && udev->parent == udev->bus->root_hub) {
-               struct usb_otg_descriptor       *desc = 0;
+               struct usb_otg_descriptor       *desc = NULL;
                struct usb_bus                  *bus = udev->bus;
 
                /* descriptor may appear anywhere in config */
@@ -1674,7 +1724,7 @@ fail:
 
 
 /**
- * usb_configure_device - Detect and probe device intfs/otg (usbcore-internal)
+ * usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal)
  * @udev: newly addressed device (in ADDRESS state)
  *
  * This is only called by usb_new_device() and usb_authorize_device()
@@ -1685,7 +1735,7 @@ fail:
  * the string descriptors, as they will be errored out by the device
  * until it has been authorized.
  */
-static int usb_configure_device(struct usb_device *udev)
+static int usb_enumerate_device(struct usb_device *udev)
 {
        int err;
 
@@ -1709,7 +1759,7 @@ static int usb_configure_device(struct usb_device *udev)
                                                      udev->descriptor.iManufacturer);
                udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
        }
-       err = usb_configure_device_otg(udev);
+       err = usb_enumerate_device_otg(udev);
 fail:
        return err;
 }
@@ -1719,8 +1769,8 @@ fail:
  * usb_new_device - perform initial device setup (usbcore-internal)
  * @udev: newly addressed device (in ADDRESS state)
  *
- * This is called with devices which have been enumerated, but not yet
- * configured.  The device descriptor is available, but not descriptors
+ * This is called with devices which have been detected but not fully
+ * enumerated.  The device descriptor is available, but not descriptors
  * for any device configuration.  The caller must have locked either
  * the parent hub (if udev is a normal device) or else the
  * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
@@ -1739,12 +1789,20 @@ int usb_new_device(struct usb_device *udev)
 {
        int err;
 
-       /* Increment the parent's count of unsuspended children */
-       if (udev->parent)
-               usb_autoresume_device(udev->parent);
+       if (udev->parent) {
+               /* Initialize non-root-hub device wakeup to disabled;
+                * device (un)configuration controls wakeup capable
+                * sysfs power/wakeup controls wakeup enabled/disabled
+                */
+               device_init_wakeup(&udev->dev, 0);
+       }
+
+       /* Tell the runtime-PM framework the device is active */
+       pm_runtime_set_active(&udev->dev);
+       pm_runtime_enable(&udev->dev);
 
-       usb_detect_quirks(udev);                /* Determine quirks */
-       err = usb_configure_device(udev);       /* detect & probe dev/intfs */
+       usb_detect_quirks(udev);
+       err = usb_enumerate_device(udev);       /* Read descriptors */
        if (err < 0)
                goto fail;
        dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
@@ -1757,6 +1815,7 @@ int usb_new_device(struct usb_device *udev)
        /* Tell the world! */
        announce_device(udev);
 
+       device_enable_async_suspend(&udev->dev);
        /* Register the device.  The device driver is responsible
         * for configuring the device and invoking the add-device
         * notifier chain (used by usbfs and possibly others).
@@ -1772,7 +1831,8 @@ int usb_new_device(struct usb_device *udev)
 
 fail:
        usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-       usb_stop_pm(udev);
+       pm_runtime_disable(&udev->dev);
+       pm_runtime_set_suspended(&udev->dev);
        return err;
 }
 
@@ -1789,21 +1849,23 @@ fail:
  */
 int usb_deauthorize_device(struct usb_device *usb_dev)
 {
-       unsigned cnt;
        usb_lock_device(usb_dev);
        if (usb_dev->authorized == 0)
                goto out_unauthorized;
+
        usb_dev->authorized = 0;
        usb_set_configuration(usb_dev, -1);
+
+       kfree(usb_dev->product);
        usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+       kfree(usb_dev->manufacturer);
        usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+       kfree(usb_dev->serial);
        usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-       kfree(usb_dev->config);
-       usb_dev->config = NULL;
-       for (cnt = 0; cnt < usb_dev->descriptor.bNumConfigurations; cnt++)
-               kfree(usb_dev->rawdescriptors[cnt]);
+
+       usb_destroy_configuration(usb_dev);
        usb_dev->descriptor.bNumConfigurations = 0;
-       kfree(usb_dev->rawdescriptors);
+
 out_unauthorized:
        usb_unlock_device(usb_dev);
        return 0;
@@ -1813,15 +1875,11 @@ out_unauthorized:
 int usb_authorize_device(struct usb_device *usb_dev)
 {
        int result = 0, c;
+
        usb_lock_device(usb_dev);
        if (usb_dev->authorized == 1)
                goto out_authorized;
-       kfree(usb_dev->product);
-       usb_dev->product = NULL;
-       kfree(usb_dev->manufacturer);
-       usb_dev->manufacturer = NULL;
-       kfree(usb_dev->serial);
-       usb_dev->serial = NULL;
+
        result = usb_autoresume_device(usb_dev);
        if (result < 0) {
                dev_err(&usb_dev->dev,
@@ -1834,10 +1892,18 @@ int usb_authorize_device(struct usb_device *usb_dev)
                        "authorization: %d\n", result);
                goto error_device_descriptor;
        }
+
+       kfree(usb_dev->product);
+       usb_dev->product = NULL;
+       kfree(usb_dev->manufacturer);
+       usb_dev->manufacturer = NULL;
+       kfree(usb_dev->serial);
+       usb_dev->serial = NULL;
+
        usb_dev->authorized = 1;
-       result = usb_configure_device(usb_dev);
+       result = usb_enumerate_device(usb_dev);
        if (result < 0)
-               goto error_configure;
+               goto error_enumerate;
        /* Choose and set the configuration.  This registers the interfaces
         * with the driver core and lets interface drivers bind to them.
         */
@@ -1852,8 +1918,10 @@ int usb_authorize_device(struct usb_device *usb_dev)
                }
        }
        dev_info(&usb_dev->dev, "authorized to connect\n");
-error_configure:
+
+error_enumerate:
 error_device_descriptor:
+       usb_autosuspend_device(usb_dev);
 error_autoresume:
 out_authorized:
        usb_unlock_device(usb_dev);     // complements locktree
@@ -1913,7 +1981,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                if (!(portstatus & USB_PORT_STAT_RESET) &&
                    (portstatus & USB_PORT_STAT_ENABLE)) {
                        if (hub_is_wusb(hub))
-                               udev->speed = USB_SPEED_VARIABLE;
+                               udev->speed = USB_SPEED_WIRELESS;
                        else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                                udev->speed = USB_SPEED_HIGH;
                        else if (portstatus & USB_PORT_STAT_LOW_SPEED)
@@ -1939,7 +2007,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                struct usb_device *udev, unsigned int delay)
 {
        int i, status;
+       struct usb_hcd *hcd;
 
+       hcd = bus_to_hcd(udev->bus);
        /* Block EHCI CF initialization during the port reset.
         * Some companion controllers don't like it when they mix.
         */
@@ -1967,6 +2037,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                        /* TRSTRCY = 10 ms; plus some extra */
                        msleep(10 + 40);
                        update_address(udev, 0);
+                       if (hcd->driver->reset_device) {
+                               status = hcd->driver->reset_device(hcd, udev);
+                               if (status < 0) {
+                                       dev_err(&udev->dev, "Cannot reset "
+                                                       "HCD device state\n");
+                                       break;
+                               }
+                       }
                        /* FALL THROUGH */
                case -ENOTCONN:
                case -ENODEV:
@@ -2109,9 +2187,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                                USB_DEVICE_REMOTE_WAKEUP, 0,
                                NULL, 0,
                                USB_CTRL_SET_TIMEOUT);
-               if (status)
+               if (status) {
                        dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
                                        status);
+                       /* bail if autosuspend is requested */
+                       if (msg.event & PM_EVENT_AUTO)
+                               return status;
+               }
        }
 
        /* see 7.1.7.6 */
@@ -2120,7 +2202,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
                                port1, status);
                /* paranoia:  "should not happen" */
-               (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+               if (udev->do_remote_wakeup)
+                       (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                                USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
                                USB_DEVICE_REMOTE_WAKEUP, 0,
                                NULL, 0,
@@ -2307,14 +2390,17 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 }
 
 /* caller has locked udev */
-static int remote_wakeup(struct usb_device *udev)
+int usb_remote_wakeup(struct usb_device *udev)
 {
        int     status = 0;
 
        if (udev->state == USB_STATE_SUSPENDED) {
                dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
-               usb_mark_last_busy(udev);
-               status = usb_external_resume_device(udev, PMSG_REMOTE_RESUME);
+               status = usb_autoresume_device(udev);
+               if (status == 0) {
+                       /* Let the drivers do their thing, then... */
+                       usb_autosuspend_device(udev);
+               }
        }
        return status;
 }
@@ -2351,11 +2437,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        return status;
 }
 
-static inline int remote_wakeup(struct usb_device *udev)
-{
-       return 0;
-}
-
 #endif
 
 static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
@@ -2422,11 +2503,6 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
 
 #else  /* CONFIG_PM */
 
-static inline int remote_wakeup(struct usb_device *udev)
-{
-       return 0;
-}
-
 #define hub_suspend            NULL
 #define hub_resume             NULL
 #define hub_reset_resume       NULL
@@ -2571,14 +2647,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 
        mutex_lock(&usb_address0_mutex);
 
-       if ((hcd->driver->flags & HCD_USB3) && udev->config) {
-               /* FIXME this will need special handling by the xHCI driver. */
-               dev_dbg(&udev->dev,
-                               "xHCI reset of configured device "
-                               "not supported yet.\n");
-               retval = -EINVAL;
-               goto fail;
-       } else if (!udev->config && oldspeed == USB_SPEED_SUPER) {
+       if (!udev->config && oldspeed == USB_SPEED_SUPER) {
                /* Don't reset USB 3.0 devices during an initial setup */
                usb_set_device_state(udev, USB_STATE_DEFAULT);
        } else {
@@ -2604,7 +2673,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         */
        switch (udev->speed) {
        case USB_SPEED_SUPER:
-       case USB_SPEED_VARIABLE:        /* fixed at 512 */
+       case USB_SPEED_WIRELESS:        /* fixed at 512 */
                udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
                break;
        case USB_SPEED_HIGH:            /* fixed at 64 */
@@ -2632,7 +2701,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        case USB_SPEED_SUPER:
                                speed = "super";
                                break;
-       case USB_SPEED_VARIABLE:
+       case USB_SPEED_WIRELESS:
                                speed = "variable";
                                type = "Wireless ";
                                break;
@@ -2932,7 +3001,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        /* For a suspended device, treat this as a
                         * remote wakeup event.
                         */
-                       status = remote_wakeup(udev);
+                       status = usb_remote_wakeup(udev);
 #endif
 
                } else {
@@ -2951,6 +3020,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                usb_disconnect(&hdev->children[port1-1]);
        clear_bit(port1, hub->change_bits);
 
+       /* We can forget about a "removed" device when there's a physical
+        * disconnect or the connect status changes.
+        */
+       if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+                       (portchange & USB_PORT_STAT_C_CONNECTION))
+               clear_bit(port1, hub->removed_bits);
+
        if (portchange & (USB_PORT_STAT_C_CONNECTION |
                                USB_PORT_STAT_C_ENABLE)) {
                status = hub_port_debounce(hub, port1);
@@ -2964,12 +3040,15 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
        }
 
-       /* Return now if debouncing failed or nothing is connected */
-       if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+       /* Return now if debouncing failed or nothing is connected or
+        * the device was "removed".
+        */
+       if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+                       test_bit(port1, hub->removed_bits)) {
 
                /* maybe switch power back on (e.g. root hub was reset) */
                if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
-                               && !(portstatus & (1 << USB_PORT_FEAT_POWER)))
+                               && !(portstatus & USB_PORT_STAT_POWER))
                        set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
                if (portstatus & USB_PORT_STAT_ENABLE)
@@ -3007,7 +3086,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                if (!(hcd->driver->flags & HCD_USB3))
                        udev->speed = USB_SPEED_UNKNOWN;
                else if ((hdev->parent == NULL) &&
-                               (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED)))
+                               (portstatus & USB_PORT_STAT_SUPER_SPEED))
                        udev->speed = USB_SPEED_SUPER;
                else
                        udev->speed = USB_SPEED_UNKNOWN;
@@ -3108,6 +3187,7 @@ loop_disable:
 loop:
                usb_ep0_reinit(udev);
                release_address(udev);
+               hub_free_dev(udev);
                usb_put_dev(udev);
                if ((status == -ENOTCONN) || (status == -ENOTSUPP))
                        break;
@@ -3175,7 +3255,7 @@ static void hub_events(void)
                 * disconnected while waiting for the lock to succeed. */
                usb_lock_device(hdev);
                if (unlikely(hub->disconnected))
-                       goto loop;
+                       goto loop_disconnected;
 
                /* If the hub has died, clean up after it */
                if (hdev->state == USB_STATE_NOTATTACHED) {
@@ -3264,8 +3344,11 @@ static void hub_events(void)
                                        USB_PORT_FEAT_C_SUSPEND);
                                udev = hdev->children[i-1];
                                if (udev) {
+                                       /* TRSMRCY = 10 msec */
+                                       msleep(10);
+
                                        usb_lock_device(udev);
-                                       ret = remote_wakeup(hdev->
+                                       ret = usb_remote_wakeup(hdev->
                                                        children[i-1]);
                                        usb_unlock_device(udev);
                                        if (ret < 0)
@@ -3324,11 +3407,15 @@ static void hub_events(void)
                        }
                }
 
-loop_autopm:
-               /* Allow autosuspend if we're not going to run again */
-               if (list_empty(&hub->event_list))
-                       usb_autopm_enable(intf);
-loop:
+ loop_autopm:
+               /* Balance the usb_autopm_get_interface() above */
+               usb_autopm_put_interface_no_suspend(intf);
+ loop:
+               /* Balance the usb_autopm_get_interface_no_resume() in
+                * kick_khubd() and allow autosuspend.
+                */
+               usb_autopm_put_interface(intf);
+ loop_disconnected:
                usb_unlock_device(hdev);
                kref_put(&hub->kref, hub_release);
 
@@ -3355,7 +3442,7 @@ static int hub_thread(void *__unused)
        return 0;
 }
 
-static struct usb_device_id hub_id_table [] = {
+static const struct usb_device_id hub_id_table[] = {
     { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
       .bDeviceClass = USB_CLASS_HUB},
     { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
@@ -3520,6 +3607,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
 {
        struct usb_device               *parent_hdev = udev->parent;
        struct usb_hub                  *parent_hub;
+       struct usb_hcd                  *hcd = bus_to_hcd(udev->bus);
        struct usb_device_descriptor    descriptor = udev->descriptor;
        int                             i, ret = 0;
        int                             port1 = udev->portnum;
@@ -3563,6 +3651,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        /* Restore the device's previous configuration */
        if (!udev->actconfig)
                goto done;
+
+       mutex_lock(&hcd->bandwidth_mutex);
+       ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
+       if (ret < 0) {
+               dev_warn(&udev->dev,
+                               "Busted HC?  Not enough HCD resources for "
+                               "old configuration.\n");
+               mutex_unlock(&hcd->bandwidth_mutex);
+               goto re_enumerate;
+       }
        ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                        USB_REQ_SET_CONFIGURATION, 0,
                        udev->actconfig->desc.bConfigurationValue, 0,
@@ -3571,8 +3669,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                dev_err(&udev->dev,
                        "can't restore configuration #%d (error=%d)\n",
                        udev->actconfig->desc.bConfigurationValue, ret);
+               mutex_unlock(&hcd->bandwidth_mutex);
                goto re_enumerate;
        }
+       mutex_unlock(&hcd->bandwidth_mutex);
        usb_set_device_state(udev, USB_STATE_CONFIGURED);
 
        /* Put interfaces back into the same altsettings as before.
@@ -3582,7 +3682,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
         * endpoint state.
         */
        for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
-               struct usb_interface *intf = udev->actconfig->interface[i];
+               struct usb_host_config *config = udev->actconfig;
+               struct usb_interface *intf = config->interface[i];
                struct usb_interface_descriptor *desc;
 
                desc = &intf->cur_altsetting->desc;
@@ -3591,8 +3692,14 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                        usb_enable_interface(udev, intf, true);
                        ret = 0;
                } else {
+                       /* Let the bandwidth allocation function know that this
+                        * device has been reset, and it will have to use
+                        * alternate setting 0 as the current alternate setting.
+                        */
+                       intf->resetting_device = 1;
                        ret = usb_set_interface(udev, desc->bInterfaceNumber,
                                        desc->bAlternateSetting);
+                       intf->resetting_device = 0;
                }
                if (ret < 0) {
                        dev_err(&udev->dev, "failed to restore interface %d "