USB: fix bug of device descriptor got from superspeed device
[linux-flexiantxendom0.git] / drivers / usb / core / hub.c
index 0968157..ada678e 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/rcupdate.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
@@ -339,7 +340,8 @@ static int get_hub_status(struct usb_device *hdev,
 {
        int i, status = -ETIMEDOUT;
 
-       for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
+       for (i = 0; i < USB_STS_RETRIES &&
+                       (status == -ETIMEDOUT || status == -EPIPE); i++) {
                status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                        USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
                        data, sizeof(*data), USB_STS_TIMEOUT);
@@ -355,7 +357,8 @@ static int get_port_status(struct usb_device *hdev, int port1,
 {
        int i, status = -ETIMEDOUT;
 
-       for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
+       for (i = 0; i < USB_STS_RETRIES &&
+                       (status == -ETIMEDOUT || status == -EPIPE); i++) {
                status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                        USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
                        data, sizeof(*data), USB_STS_TIMEOUT);
@@ -379,15 +382,6 @@ static int hub_port_status(struct usb_hub *hub, int port1,
                *status = le16_to_cpu(hub->status->port.wPortStatus);
                *change = le16_to_cpu(hub->status->port.wPortChange);
 
-               if ((hub->hdev->parent != NULL) &&
-                               hub_is_superspeed(hub->hdev)) {
-                       /* Translate the USB 3 port status */
-                       u16 tmp = *status & USB_SS_PORT_STAT_MASK;
-                       if (*status & USB_SS_PORT_STAT_POWER)
-                               tmp |= USB_PORT_STAT_POWER;
-                       *status = tmp;
-               }
-
                ret = 0;
        }
        mutex_unlock(&hub->status_mutex);
@@ -712,10 +706,26 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
        if (type == HUB_INIT3)
                goto init3;
 
-       /* After a resume, port power should still be on.
+       /* The superspeed hub except for root hub has to use Hub Depth
+        * value as an offset into the route string to locate the bits
+        * it uses to determine the downstream port number. So hub driver
+        * should send a set hub depth request to superspeed hub after
+        * the superspeed hub is set configuration in initialization or
+        * reset procedure.
+        *
+        * After a resume, port power should still be on.
         * For any other type of activation, turn it on.
         */
        if (type != HUB_RESUME) {
+               if (hdev->parent && hub_is_superspeed(hdev)) {
+                       ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+                                       HUB_SET_DEPTH, USB_RT_HUB,
+                                       hdev->level - 1, 0, NULL, 0,
+                                       USB_CTRL_SET_TIMEOUT);
+                       if (ret < 0)
+                               dev_err(hub->intfdev,
+                                               "set hub depth failed\n");
+               }
 
                /* Speed up system boot by using a delayed_work for the
                 * hub's initial power-up delays.  This is pretty awkward
@@ -820,6 +830,12 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                                        USB_PORT_FEAT_C_PORT_LINK_STATE);
                }
 
+               if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
+                               hub_is_superspeed(hub->hdev)) {
+                       need_debounce_delay = true;
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_BH_PORT_RESET);
+               }
                /* We can forget about a "removed" device when there's a
                 * physical disconnect or the connect status changes.
                 */
@@ -988,18 +1004,6 @@ static int hub_configure(struct usb_hub *hub,
                goto fail;
        }
 
-       if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) {
-               ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
-                               HUB_SET_DEPTH, USB_RT_HUB,
-                               hdev->level - 1, 0, NULL, 0,
-                               USB_CTRL_SET_TIMEOUT);
-
-               if (ret < 0) {
-                       message = "can't set hub depth";
-                       goto fail;
-               }
-       }
-
        /* Request the entire hub descriptor.
         * hub->descriptor can handle USB_MAXCHILDREN ports,
         * but the hub can/will return fewer bytes here.
@@ -1508,6 +1512,7 @@ void usb_set_device_state(struct usb_device *udev,
                enum usb_device_state new_state)
 {
        unsigned long flags;
+       int wakeup = -1;
 
        spin_lock_irqsave(&device_state_lock, flags);
        if (udev->state == USB_STATE_NOTATTACHED)
@@ -1522,11 +1527,10 @@ 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_set_wakeup_capable(&udev->dev,
-                                       (udev->actconfig->desc.bmAttributes
-                                        & USB_CONFIG_ATT_WAKEUP));
+                               wakeup = udev->actconfig->desc.bmAttributes
+                                        & USB_CONFIG_ATT_WAKEUP;
                        else
-                               device_set_wakeup_capable(&udev->dev, 0);
+                               wakeup = 0;
                }
                if (udev->state == USB_STATE_SUSPENDED &&
                        new_state != USB_STATE_SUSPENDED)
@@ -1538,6 +1542,8 @@ void usb_set_device_state(struct usb_device *udev,
        } else
                recursively_mark_NOTATTACHED(udev);
        spin_unlock_irqrestore(&device_state_lock, flags);
+       if (wakeup >= 0)
+               device_set_wakeup_capable(&udev->dev, wakeup);
 }
 EXPORT_SYMBOL_GPL(usb_set_device_state);
 
@@ -1639,15 +1645,11 @@ void usb_disconnect(struct usb_device **pdev)
 {
        struct usb_device       *udev = *pdev;
        int                     i;
-
-       if (!udev) {
-               pr_debug ("%s nodev\n", __func__);
-               return;
-       }
+       struct usb_hcd          *hcd = bus_to_hcd(udev->bus);
 
        /* mark the device as inactive, so any further urb submissions for
         * this device (and any of its children) will fail immediately.
-        * this quiesces everyting except pending urbs.
+        * this quiesces everything except pending urbs.
         */
        usb_set_device_state(udev, USB_STATE_NOTATTACHED);
        dev_info(&udev->dev, "USB disconnect, device number %d\n",
@@ -1666,7 +1668,9 @@ void usb_disconnect(struct usb_device **pdev)
         * so that the hardware is now fully quiesced.
         */
        dev_dbg (&udev->dev, "unregistering device\n");
+       mutex_lock(hcd->bandwidth_mutex);
        usb_disable_device(udev, 0);
+       mutex_unlock(hcd->bandwidth_mutex);
        usb_hcd_synchronize_unlinks(udev);
 
        usb_remove_ep_devs(&udev->ep0);
@@ -1933,6 +1937,10 @@ fail:
  */
 int usb_deauthorize_device(struct usb_device *usb_dev)
 {
+       char *product = NULL;
+       char *manufacturer = NULL;
+       char *serial = NULL;
+
        usb_lock_device(usb_dev);
        if (usb_dev->authorized == 0)
                goto out_unauthorized;
@@ -1940,11 +1948,12 @@ int usb_deauthorize_device(struct usb_device *usb_dev)
        usb_dev->authorized = 0;
        usb_set_configuration(usb_dev, -1);
 
-       kfree(usb_dev->product);
+       product = usb_dev->product;
+       manufacturer = usb_dev->manufacturer;
+       serial = usb_dev->serial;
+
        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);
 
        usb_destroy_configuration(usb_dev);
@@ -1952,6 +1961,12 @@ int usb_deauthorize_device(struct usb_device *usb_dev)
 
 out_unauthorized:
        usb_unlock_device(usb_dev);
+       if (product || manufacturer || serial) {
+               synchronize_rcu();
+               kfree(product);
+               kfree(manufacturer);
+               kfree(serial);
+       }
        return 0;
 }
 
@@ -1959,6 +1974,9 @@ out_unauthorized:
 int usb_authorize_device(struct usb_device *usb_dev)
 {
        int result = 0, c;
+       char *product = NULL;
+       char *manufacturer = NULL;
+       char *serial = NULL;
 
        usb_lock_device(usb_dev);
        if (usb_dev->authorized == 1)
@@ -1977,11 +1995,12 @@ int usb_authorize_device(struct usb_device *usb_dev)
                goto error_device_descriptor;
        }
 
-       kfree(usb_dev->product);
+       product = usb_dev->product;
+       manufacturer = usb_dev->manufacturer;
+       serial = usb_dev->serial;
+       
        usb_dev->product = NULL;
-       kfree(usb_dev->manufacturer);
        usb_dev->manufacturer = NULL;
-       kfree(usb_dev->serial);
        usb_dev->serial = NULL;
 
        usb_dev->authorized = 1;
@@ -2009,6 +2028,12 @@ error_device_descriptor:
 error_autoresume:
 out_authorized:
        usb_unlock_device(usb_dev);     // complements locktree
+       if (product || manufacturer || serial) {
+               synchronize_rcu();
+               kfree(product);
+               kfree(manufacturer);
+               kfree(serial);
+       }
        return result;
 }
 
@@ -2032,11 +2057,23 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 
 #define HUB_ROOT_RESET_TIME    50      /* times are in msec */
 #define HUB_SHORT_RESET_TIME   10
+#define HUB_BH_RESET_TIME      50
 #define HUB_LONG_RESET_TIME    200
 #define HUB_RESET_TIMEOUT      500
 
+static int hub_port_reset(struct usb_hub *hub, int port1,
+                       struct usb_device *udev, unsigned int delay, bool warm);
+
+/* Is a USB 3.0 port in the Inactive state? */
+static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
+{
+       return hub_is_superspeed(hub->hdev) &&
+               (portstatus & USB_PORT_STAT_LINK_STATE) ==
+               USB_SS_PORT_LS_SS_INACTIVE;
+}
+
 static int hub_port_wait_reset(struct usb_hub *hub, int port1,
-                               struct usb_device *udev, unsigned int delay)
+                       struct usb_device *udev, unsigned int delay, bool warm)
 {
        int delay_time, ret;
        u16 portstatus;
@@ -2053,28 +2090,71 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                if (ret < 0)
                        return ret;
 
-               /* Device went away? */
-               if (!(portstatus & USB_PORT_STAT_CONNECTION))
-                       return -ENOTCONN;
-
-               /* bomb out completely if the connection bounced */
-               if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                       return -ENOTCONN;
-
-               /* if we`ve finished resetting, then break out of the loop */
-               if (!(portstatus & USB_PORT_STAT_RESET) &&
-                   (portstatus & USB_PORT_STAT_ENABLE)) {
-                       if (hub_is_wusb(hub))
-                               udev->speed = USB_SPEED_WIRELESS;
-                       else if (hub_is_superspeed(hub->hdev))
-                               udev->speed = USB_SPEED_SUPER;
-                       else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
-                               udev->speed = USB_SPEED_HIGH;
-                       else if (portstatus & USB_PORT_STAT_LOW_SPEED)
-                               udev->speed = USB_SPEED_LOW;
-                       else
-                               udev->speed = USB_SPEED_FULL;
-                       return 0;
+               /*
+                * Some buggy devices require a warm reset to be issued even
+                * when the port appears not to be connected.
+                */
+               if (!warm) {
+                       /*
+                        * Some buggy devices can cause an NEC host controller
+                        * to transition to the "Error" state after a hot port
+                        * reset.  This will show up as the port state in
+                        * "Inactive", and the port may also report a
+                        * disconnect.  Forcing a warm port reset seems to make
+                        * the device work.
+                        *
+                        * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
+                        */
+                       if (hub_port_inactive(hub, portstatus)) {
+                               int ret;
+
+                               if ((portchange & USB_PORT_STAT_C_CONNECTION))
+                                       clear_port_feature(hub->hdev, port1,
+                                                       USB_PORT_FEAT_C_CONNECTION);
+                               if (portchange & USB_PORT_STAT_C_LINK_STATE)
+                                       clear_port_feature(hub->hdev, port1,
+                                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+                               if (portchange & USB_PORT_STAT_C_RESET)
+                                       clear_port_feature(hub->hdev, port1,
+                                                       USB_PORT_FEAT_C_RESET);
+                               dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+                                               port1);
+                               ret = hub_port_reset(hub, port1,
+                                               udev, HUB_BH_RESET_TIME,
+                                               true);
+                               if ((portchange & USB_PORT_STAT_C_CONNECTION))
+                                       clear_port_feature(hub->hdev, port1,
+                                                       USB_PORT_FEAT_C_CONNECTION);
+                               return ret;
+                       }
+                       /* Device went away? */
+                       if (!(portstatus & USB_PORT_STAT_CONNECTION))
+                               return -ENOTCONN;
+
+                       /* bomb out completely if the connection bounced */
+                       if ((portchange & USB_PORT_STAT_C_CONNECTION))
+                               return -ENOTCONN;
+
+                       /* if we`ve finished resetting, then break out of
+                        * the loop
+                        */
+                       if (!(portstatus & USB_PORT_STAT_RESET) &&
+                           (portstatus & USB_PORT_STAT_ENABLE)) {
+                               if (hub_is_wusb(hub))
+                                       udev->speed = USB_SPEED_WIRELESS;
+                               else if (hub_is_superspeed(hub->hdev))
+                                       udev->speed = USB_SPEED_SUPER;
+                               else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+                                       udev->speed = USB_SPEED_HIGH;
+                               else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+                                       udev->speed = USB_SPEED_LOW;
+                               else
+                                       udev->speed = USB_SPEED_FULL;
+                               return 0;
+                       }
+               } else {
+                       if (portchange & USB_PORT_STAT_C_BH_RESET)
+                               return 0;
                }
 
                /* switch to the long delay after two short delay failures */
@@ -2082,35 +2162,84 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                        delay = HUB_LONG_RESET_TIME;
 
                dev_dbg (hub->intfdev,
-                       "port %d not reset yet, waiting %dms\n",
-                       port1, delay);
+                       "port %d not %sreset yet, waiting %dms\n",
+                       port1, warm ? "warm " : "", delay);
        }
 
        return -EBUSY;
 }
 
+static void hub_port_finish_reset(struct usb_hub *hub, int port1,
+                       struct usb_device *udev, int *status, bool warm)
+{
+       switch (*status) {
+       case 0:
+               if (!warm) {
+                       struct usb_hcd *hcd;
+                       /* TRSTRCY = 10 ms; plus some extra */
+                       msleep(10 + 40);
+                       update_devnum(udev, 0);
+                       hcd = bus_to_hcd(udev->bus);
+                       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:
+               clear_port_feature(hub->hdev,
+                               port1, USB_PORT_FEAT_C_RESET);
+               /* FIXME need disconnect() for NOTATTACHED device */
+               if (warm) {
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_BH_PORT_RESET);
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+               } else {
+                       usb_set_device_state(udev, *status
+                                       ? USB_STATE_NOTATTACHED
+                                       : USB_STATE_DEFAULT);
+               }
+               break;
+       }
+}
+
+/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */
 static int hub_port_reset(struct usb_hub *hub, int port1,
-                               struct usb_device *udev, unsigned int delay)
+                       struct usb_device *udev, unsigned int delay, bool warm)
 {
        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.
-        */
-       down_read(&ehci_cf_port_reset_rwsem);
+       if (!warm) {
+               /* Block EHCI CF initialization during the port reset.
+                * Some companion controllers don't like it when they mix.
+                */
+               down_read(&ehci_cf_port_reset_rwsem);
+       } else {
+               if (!hub_is_superspeed(hub->hdev)) {
+                       dev_err(hub->intfdev, "only USB3 hub support "
+                                               "warm reset\n");
+                       return -EINVAL;
+               }
+       }
 
        /* Reset the port */
        for (i = 0; i < PORT_RESET_TRIES; i++) {
-               status = set_port_feature(hub->hdev,
-                               port1, USB_PORT_FEAT_RESET);
-               if (status)
+               status = set_port_feature(hub->hdev, port1, (warm ?
+                                       USB_PORT_FEAT_BH_PORT_RESET :
+                                       USB_PORT_FEAT_RESET));
+               if (status) {
                        dev_err(hub->intfdev,
-                                       "cannot reset port %d (err = %d)\n",
-                                       port1, status);
-               else {
-                       status = hub_port_wait_reset(hub, port1, udev, delay);
+                                       "cannot %sreset port %d (err = %d)\n",
+                                       warm ? "warm " : "", port1, status);
+               } else {
+                       status = hub_port_wait_reset(hub, port1, udev, delay,
+                                                               warm);
                        if (status && status != -ENOTCONN)
                                dev_dbg(hub->intfdev,
                                                "port_wait_reset: err = %d\n",
@@ -2118,34 +2247,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                }
 
                /* return on disconnect or reset */
-               switch (status) {
-               case 0:
-                       /* TRSTRCY = 10 ms; plus some extra */
-                       msleep(10 + 40);
-                       update_devnum(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:
-                       clear_port_feature(hub->hdev,
-                               port1, USB_PORT_FEAT_C_RESET);
-                       /* FIXME need disconnect() for NOTATTACHED device */
-                       usb_set_device_state(udev, status
-                                       ? USB_STATE_NOTATTACHED
-                                       : USB_STATE_DEFAULT);
+               if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+                       hub_port_finish_reset(hub, port1, udev, &status, warm);
                        goto done;
                }
 
                dev_dbg (hub->intfdev,
-                       "port %d not enabled, trying reset again...\n",
-                       port1);
+                       "port %d not enabled, trying %sreset again...\n",
+                       port1, warm ? "warm " : "");
                delay = HUB_LONG_RESET_TIME;
        }
 
@@ -2153,16 +2262,47 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                "Cannot enable port %i.  Maybe the USB cable is bad?\n",
                port1);
 
- done:
-       up_read(&ehci_cf_port_reset_rwsem);
+done:
+       if (!warm)
+               up_read(&ehci_cf_port_reset_rwsem);
+
        return status;
 }
 
+/* Check if a port is power on */
+static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
+{
+       int ret = 0;
+
+       if (hub_is_superspeed(hub->hdev)) {
+               if (portstatus & USB_SS_PORT_STAT_POWER)
+                       ret = 1;
+       } else {
+               if (portstatus & USB_PORT_STAT_POWER)
+                       ret = 1;
+       }
+
+       return ret;
+}
+
 #ifdef CONFIG_PM
 
-#define MASK_BITS      (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \
-                               USB_PORT_STAT_SUSPEND)
-#define WANT_BITS      (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION)
+/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
+static int port_is_suspended(struct usb_hub *hub, unsigned portstatus)
+{
+       int ret = 0;
+
+       if (hub_is_superspeed(hub->hdev)) {
+               if ((portstatus & USB_PORT_STAT_LINK_STATE)
+                               == USB_SS_PORT_LS_U3)
+                       ret = 1;
+       } else {
+               if (portstatus & USB_PORT_STAT_SUSPEND)
+                       ret = 1;
+       }
+
+       return ret;
+}
 
 /* Determine whether the device on a port is ready for a normal resume,
  * is ready for a reset-resume, or should be disconnected.
@@ -2172,7 +2312,9 @@ static int check_port_resume_type(struct usb_device *udev,
                int status, unsigned portchange, unsigned portstatus)
 {
        /* Is the device still present? */
-       if (status || (portstatus & MASK_BITS) != WANT_BITS) {
+       if (status || port_is_suspended(hub, portstatus) ||
+                       !port_is_power_on(hub, portstatus) ||
+                       !(portstatus & USB_PORT_STAT_CONNECTION)) {
                if (status >= 0)
                        status = -ENODEV;
        }
@@ -2259,8 +2401,6 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
        int             port1 = udev->portnum;
        int             status;
 
-       // dev_dbg(hub->intfdev, "suspend port %d\n", port1);
-
        /* enable remote wakeup when appropriate; this lets the device
         * wake up the upstream hub (including maybe the root hub).
         *
@@ -2277,13 +2417,23 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                        dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
                                        status);
                        /* bail if autosuspend is requested */
-                       if (msg.event & PM_EVENT_AUTO)
+                       if (PMSG_IS_AUTO(msg))
                                return status;
                }
        }
 
+       /* disable USB2 hardware LPM */
+       if (udev->usb2_hw_lpm_enabled == 1)
+               usb_set_usb2_hardware_lpm(udev, 0);
+
        /* see 7.1.7.6 */
-       status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
+       if (hub_is_superspeed(hub->hdev))
+               status = set_port_feature(hub->hdev,
+                               port1 | (USB_SS_PORT_LS_U3 << 3),
+                               USB_PORT_FEAT_LINK_STATE);
+       else
+               status = set_port_feature(hub->hdev, port1,
+                                               USB_PORT_FEAT_SUSPEND);
        if (status) {
                dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
                                port1, status);
@@ -2294,10 +2444,15 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                                USB_DEVICE_REMOTE_WAKEUP, 0,
                                NULL, 0,
                                USB_CTRL_SET_TIMEOUT);
+
+               /* System sleep transitions should never fail */
+               if (!PMSG_IS_AUTO(msg))
+                       status = 0;
        } else {
                /* device has up to 10 msec to fully suspend */
-               dev_dbg(&udev->dev, "usb %ssuspend\n",
-                               (msg.event & PM_EVENT_AUTO ? "auto-" : ""));
+               dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
+                               (PMSG_IS_AUTO(msg) ? "auto-" : ""),
+                               udev->do_remote_wakeup);
                usb_set_device_state(udev, USB_STATE_SUSPENDED);
                msleep(10);
        }
@@ -2427,7 +2582,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
        /* Skip the initial Clear-Suspend step for a remote wakeup */
        status = hub_port_status(hub, port1, &portstatus, &portchange);
-       if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND))
+       if (status == 0 && !port_is_suspended(hub, portstatus))
                goto SuspendCleared;
 
        // dev_dbg(hub->intfdev, "resume port %d\n", port1);
@@ -2435,15 +2590,20 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        set_bit(port1, hub->busy_bits);
 
        /* see 7.1.7.7; affects power usage, but not budgeting */
-       status = clear_port_feature(hub->hdev,
-                       port1, USB_PORT_FEAT_SUSPEND);
+       if (hub_is_superspeed(hub->hdev))
+               status = set_port_feature(hub->hdev,
+                               port1 | (USB_SS_PORT_LS_U0 << 3),
+                               USB_PORT_FEAT_LINK_STATE);
+       else
+               status = clear_port_feature(hub->hdev,
+                               port1, USB_PORT_FEAT_SUSPEND);
        if (status) {
                dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
                                port1, status);
        } else {
                /* drive resume for at least 20 msec */
                dev_dbg(&udev->dev, "usb %sresume\n",
-                               (msg.event & PM_EVENT_AUTO ? "auto-" : ""));
+                               (PMSG_IS_AUTO(msg) ? "auto-" : ""));
                msleep(25);
 
                /* Virtual root hubs can trigger on GET_PORT_STATUS to
@@ -2458,9 +2618,15 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
  SuspendCleared:
        if (status == 0) {
-               if (portchange & USB_PORT_STAT_C_SUSPEND)
-                       clear_port_feature(hub->hdev, port1,
-                                       USB_PORT_FEAT_C_SUSPEND);
+               if (hub_is_superspeed(hub->hdev)) {
+                       if (portchange & USB_PORT_STAT_C_LINK_STATE)
+                               clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+               } else {
+                       if (portchange & USB_PORT_STAT_C_SUSPEND)
+                               clear_port_feature(hub->hdev, port1,
+                                               USB_PORT_FEAT_C_SUSPEND);
+               }
        }
 
        clear_bit(port1, hub->busy_bits);
@@ -2472,7 +2638,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        if (status < 0) {
                dev_dbg(&udev->dev, "can't resume, status %d\n", status);
                hub_port_logical_disconnect(hub, port1);
+       } else  {
+               /* Try to enable USB2 hardware LPM */
+               if (udev->usb2_hw_lpm_capable == 1)
+                       usb_set_usb2_hardware_lpm(udev, 1);
        }
+
        return status;
 }
 
@@ -2532,16 +2703,15 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
        struct usb_device       *hdev = hub->hdev;
        unsigned                port1;
 
-       /* fail if children aren't already suspended */
+       /* Warn if children aren't already suspended */
        for (port1 = 1; port1 <= hdev->maxchild; port1++) {
                struct usb_device       *udev;
 
                udev = hdev->children [port1-1];
                if (udev && udev->can_submit) {
-                       if (!(msg.event & PM_EVENT_AUTO))
-                               dev_dbg(&intf->dev, "port %d nyet suspended\n",
-                                               port1);
-                       return -EBUSY;
+                       dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
+                       if (PMSG_IS_AUTO(msg))
+                               return -EBUSY;
                }
        }
 
@@ -2713,7 +2883,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        int                     i, j, retval;
        unsigned                delay = HUB_SHORT_RESET_TIME;
        enum usb_device_speed   oldspeed = udev->speed;
-       char                    *speed, *type;
+       const char              *speed;
        int                     devnum = udev->devnum;
 
        /* root hub ports have a slightly longer reset period
@@ -2732,17 +2902,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 
        mutex_lock(&usb_address0_mutex);
 
-       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 {
-               /* Reset the device; full speed may morph to high speed */
-               /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
-               retval = hub_port_reset(hub, port1, udev, delay);
-               if (retval < 0)         /* error or disconnect */
-                       goto fail;
-               /* success, speed is known */
-       }
+       /* Reset the device; full speed may morph to high speed */
+       /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
+       retval = hub_port_reset(hub, port1, udev, delay, false);
+       if (retval < 0)         /* error or disconnect */
+               goto fail;
+       /* success, speed is known */
+
        retval = -ENODEV;
 
        if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
@@ -2777,25 +2943,16 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        default:
                goto fail;
        }
-       type = "";
-       switch (udev->speed) {
-       case USB_SPEED_LOW:     speed = "low";  break;
-       case USB_SPEED_FULL:    speed = "full"; break;
-       case USB_SPEED_HIGH:    speed = "high"; break;
-       case USB_SPEED_SUPER:
-                               speed = "super";
-                               break;
-       case USB_SPEED_WIRELESS:
-                               speed = "variable";
-                               type = "Wireless ";
-                               break;
-       default:                speed = "?";    break;
-       }
+
+       if (udev->speed == USB_SPEED_WIRELESS)
+               speed = "variable speed Wireless";
+       else
+               speed = usb_speed_string(udev->speed);
+
        if (udev->speed != USB_SPEED_SUPER)
                dev_info(&udev->dev,
-                               "%s %s speed %sUSB device number %d using %s\n",
-                               (udev->config) ? "reset" : "new", speed, type,
+                               "%s %s USB device number %d using %s\n",
+                               (udev->config) ? "reset" : "new", speed,
                                devnum, udev->bus->controller->driver->name);
 
        /* Set up TT records, if needed  */
@@ -2868,7 +3025,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                                        buf->bMaxPacketSize0;
                        kfree(buf);
 
-                       retval = hub_port_reset(hub, port1, udev, delay);
+                       retval = hub_port_reset(hub, port1, udev, delay, false);
                        if (retval < 0)         /* error or disconnect */
                                goto fail;
                        if (oldspeed != udev->speed) {
@@ -2937,12 +3094,28 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        if (retval)
                goto fail;
 
+       /*
+        * Some superspeed devices have finished the link training process
+        * and attached to a superspeed hub port, but the device descriptor
+        * got from those devices show they aren't superspeed devices. Warm
+        * reset the port attached by the devices can fix them.
+        */
+       if ((udev->speed == USB_SPEED_SUPER) &&
+                       (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
+               dev_err(&udev->dev, "got a wrong device descriptor, "
+                               "warm reset device\n");
+               hub_port_reset(hub, port1, udev,
+                               HUB_BH_RESET_TIME, true);
+               retval = -EINVAL;
+               goto fail;
+       }
+
        if (udev->descriptor.bMaxPacketSize0 == 0xff ||
                        udev->speed == USB_SPEED_SUPER)
                i = 512;
        else
                i = udev->descriptor.bMaxPacketSize0;
-       if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
+       if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
                if (udev->speed == USB_SPEED_LOW ||
                                !(i == 8 || i == 16 || i == 32 || i == 64)) {
                        dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
@@ -2966,6 +3139,15 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                goto fail;
        }
 
+       if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
+               retval = usb_get_bos_descriptor(udev);
+               if (!retval) {
+                       if (udev->bos->ext_cap && (USB_LPM_SUPPORT &
+                               le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
+                                       udev->lpm_capable = 1;
+               }
+       }
+
        retval = 0;
        /* notify HCD that we have a device connected and addressed */
        if (hcd->driver->update_device)
@@ -3139,7 +3321,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 
                /* maybe switch power back on (e.g. root hub was reset) */
                if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
-                               && !(portstatus & USB_PORT_STAT_POWER))
+                               && !port_is_power_on(hub, portstatus))
                        set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
                if (portstatus & USB_PORT_STAT_ENABLE)
@@ -3482,6 +3664,17 @@ static void hub_events(void)
                                                USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
                        }
 
+                       /* Warm reset a USB3 protocol port if it's in
+                        * SS.Inactive state.
+                        */
+                       if (hub_is_superspeed(hub->hdev) &&
+                               (portstatus & USB_PORT_STAT_LINK_STATE)
+                                       == USB_SS_PORT_LS_SS_INACTIVE) {
+                               dev_dbg(hub_dev, "warm reset port %d\n", i);
+                               hub_port_reset(hub, i, NULL,
+                                               HUB_BH_RESET_TIME, true);
+                       }
+
                        if (connect_change)
                                hub_port_connect_change(hub, i,
                                                portstatus, portchange);