Merge branch 3.13-rc4 into usb-next
[cascardo/linux.git] / drivers / usb / core / hub.c
index bd9dc35..162e94d 100644 (file)
@@ -2235,17 +2235,13 @@ static int usb_enumerate_device(struct usb_device *udev)
                        return err;
                }
        }
-       if (udev->wusb == 1 && udev->authorized == 0) {
-               udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-               udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-               udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-       } else {
-               /* read the standard strings and cache them if present */
-               udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
-               udev->manufacturer = usb_cache_string(udev,
-                                                     udev->descriptor.iManufacturer);
-               udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
-       }
+
+       /* read the standard strings and cache them if present */
+       udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
+       udev->manufacturer = usb_cache_string(udev,
+                                             udev->descriptor.iManufacturer);
+       udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
+
        err = usb_enumerate_device_otg(udev);
        if (err < 0)
                return err;
@@ -2427,16 +2423,6 @@ int usb_deauthorize_device(struct usb_device *usb_dev)
        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);
-
-       usb_destroy_configuration(usb_dev);
-       usb_dev->descriptor.bNumConfigurations = 0;
-
 out_unauthorized:
        usb_unlock_device(usb_dev);
        return 0;
@@ -2464,17 +2450,7 @@ int usb_authorize_device(struct usb_device *usb_dev)
                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_enumerate_device(usb_dev);
-       if (result < 0)
-               goto error_enumerate;
        /* Choose and set the configuration.  This registers the interfaces
         * with the driver core and lets interface drivers bind to them.
         */
@@ -2490,7 +2466,6 @@ int usb_authorize_device(struct usb_device *usb_dev)
        }
        dev_info(&usb_dev->dev, "authorized to connect\n");
 
-error_enumerate:
 error_device_descriptor:
        usb_autosuspend_device(usb_dev);
 error_autoresume:
@@ -2523,6 +2498,21 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 #define HUB_LONG_RESET_TIME    200
 #define HUB_RESET_TIMEOUT      800
 
+/*
+ * "New scheme" enumeration causes an extra state transition to be
+ * exposed to an xhci host and causes USB3 devices to receive control
+ * commands in the default state.  This has been seen to cause
+ * enumeration failures, so disable this enumeration scheme for USB3
+ * devices.
+ */
+static bool use_new_scheme(struct usb_device *udev, int retry)
+{
+       if (udev->speed == USB_SPEED_SUPER)
+               return false;
+
+       return USE_NEW_SCHEME(retry);
+}
+
 static int hub_port_reset(struct usb_hub *hub, int port1,
                        struct usb_device *udev, unsigned int delay, bool warm);
 
@@ -3981,6 +3971,20 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
        }
 }
 
+static int hub_enable_device(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       if (!hcd->driver->enable_device)
+               return 0;
+       if (udev->state == USB_STATE_ADDRESS)
+               return 0;
+       if (udev->state != USB_STATE_DEFAULT)
+               return -EINVAL;
+
+       return hcd->driver->enable_device(hcd, udev);
+}
+
 /* Reset device, (re)assign address, get device descriptor.
  * Device connection must be stable, no more debouncing needed.
  * Returns device in USB_STATE_ADDRESS, except on error.
@@ -4093,7 +4097,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         * this area, and this is how Linux has done it for ages.
         * Change it cautiously.
         *
-        * NOTE:  If USE_NEW_SCHEME() is true we will start by issuing
+        * NOTE:  If use_new_scheme() is true we will start by issuing
         * a 64-byte GET_DESCRIPTOR request.  This is what Windows does,
         * so it may help with some non-standards-compliant devices.
         * Otherwise we start with SET_ADDRESS and then try to read the
@@ -4101,10 +4105,17 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         * value.
         */
        for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
-               if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
+               bool did_new_scheme = false;
+
+               if (use_new_scheme(udev, retry_counter)) {
                        struct usb_device_descriptor *buf;
                        int r = 0;
 
+                       did_new_scheme = true;
+                       retval = hub_enable_device(udev);
+                       if (retval < 0)
+                               goto fail;
+
 #define GET_DESCRIPTOR_BUFSIZE 64
                        buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
                        if (!buf) {
@@ -4193,7 +4204,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                         *  - read ep0 maxpacket even for high and low speed,
                         */
                        msleep(10);
-                       if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
+                       /* use_new_scheme() checks the speed which may have
+                        * changed since the initial look so we cache the result
+                        * in did_new_scheme
+                        */
+                       if (did_new_scheme)
                                break;
                }