Merge tag 'random_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[cascardo/linux.git] / drivers / usb / core / hub.c
index 8fb4849..128a804 100644 (file)
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
 #include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
 #include <linux/usb/quirks.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/random.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
@@ -81,7 +83,7 @@ struct usb_hub {
        u8                      indicator[USB_MAXCHILDREN];
        struct delayed_work     leds;
        struct delayed_work     init_work;
-       void                    **port_owners;
+       struct dev_state        **port_owners;
 };
 
 static inline int hub_is_superspeed(struct usb_device *hdev)
@@ -1271,7 +1273,8 @@ static int hub_configure(struct usb_hub *hub,
 
        hdev->children = kzalloc(hdev->maxchild *
                                sizeof(struct usb_device *), GFP_KERNEL);
-       hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
+       hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *),
+                               GFP_KERNEL);
        if (!hdev->children || !hub->port_owners) {
                ret = -ENOMEM;
                goto fail;
@@ -1649,7 +1652,7 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
  * to one of these "claimed" ports, the program will "own" the device.
  */
 static int find_port_owner(struct usb_device *hdev, unsigned port1,
-               void ***ppowner)
+               struct dev_state ***ppowner)
 {
        if (hdev->state == USB_STATE_NOTATTACHED)
                return -ENODEV;
@@ -1664,10 +1667,11 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1,
 }
 
 /* In the following three functions, the caller must hold hdev's lock */
-int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
+int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
+                      struct dev_state *owner)
 {
        int rc;
-       void **powner;
+       struct dev_state **powner;
 
        rc = find_port_owner(hdev, port1, &powner);
        if (rc)
@@ -1678,10 +1682,11 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
        return rc;
 }
 
-int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
+int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
+                        struct dev_state *owner)
 {
        int rc;
-       void **powner;
+       struct dev_state **powner;
 
        rc = find_port_owner(hdev, port1, &powner);
        if (rc)
@@ -1692,10 +1697,10 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
        return rc;
 }
 
-void usb_hub_release_all_ports(struct usb_device *hdev, void *owner)
+void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
 {
        int n;
-       void **powner;
+       struct dev_state **powner;
 
        n = find_port_owner(hdev, 1, &powner);
        if (n == 0) {
@@ -2065,7 +2070,7 @@ static int usb_enumerate_device(struct usb_device *udev)
                if (err < 0) {
                        dev_err(&udev->dev, "can't read configurations, error %d\n",
                                err);
-                       goto fail;
+                       return err;
                }
        }
        if (udev->wusb == 1 && udev->authorized == 0) {
@@ -2081,8 +2086,12 @@ static int usb_enumerate_device(struct usb_device *udev)
                udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
        }
        err = usb_enumerate_device_otg(udev);
-fail:
-       return err;
+       if (err < 0)
+               return err;
+
+       usb_detect_interface_quirks(udev);
+
+       return 0;
 }
 
 static void set_usb_port_removable(struct usb_device *udev)
@@ -2173,6 +2182,14 @@ int usb_new_device(struct usb_device *udev)
        /* Tell the world! */
        announce_device(udev);
 
+       if (udev->serial)
+               add_device_randomness(udev->serial, strlen(udev->serial));
+       if (udev->product)
+               add_device_randomness(udev->product, strlen(udev->product));
+       if (udev->manufacturer)
+               add_device_randomness(udev->manufacturer,
+                                     strlen(udev->manufacturer));
+
        device_enable_async_suspend(&udev->dev);
 
        /*
@@ -2611,6 +2628,50 @@ static int check_port_resume_type(struct usb_device *udev,
        return status;
 }
 
+int usb_disable_ltm(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       /* Check if the roothub and device supports LTM. */
+       if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+                       !usb_device_supports_ltm(udev))
+               return 0;
+
+       /* Clear Feature LTM Enable can only be sent if the device is
+        * configured.
+        */
+       if (!udev->actconfig)
+               return 0;
+
+       return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                       USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
+                       USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+                       USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       /* Check if the roothub and device supports LTM. */
+       if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+                       !usb_device_supports_ltm(udev))
+               return;
+
+       /* Set Feature LTM Enable can only be sent if the device is
+        * configured.
+        */
+       if (!udev->actconfig)
+               return;
+
+       usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                       USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+                       USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+                       USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
+
 #ifdef CONFIG_USB_SUSPEND
 
 /*
@@ -2706,6 +2767,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
        if (udev->usb2_hw_lpm_enabled == 1)
                usb_set_usb2_hardware_lpm(udev, 0);
 
+       if (usb_disable_ltm(udev)) {
+               dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.",
+                               __func__);
+               return -ENOMEM;
+       }
        if (usb_unlocked_disable_lpm(udev)) {
                dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
                                __func__);
@@ -2735,7 +2801,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                if (udev->usb2_hw_lpm_capable == 1)
                        usb_set_usb2_hardware_lpm(udev, 1);
 
-               /* Try to enable USB3 LPM again */
+               /* Try to enable USB3 LTM and LPM again */
+               usb_enable_ltm(udev);
                usb_unlocked_enable_lpm(udev);
 
                /* System sleep transitions should never fail */
@@ -2936,7 +3003,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
                if (udev->usb2_hw_lpm_capable == 1)
                        usb_set_usb2_hardware_lpm(udev, 1);
 
-               /* Try to enable USB3 LPM */
+               /* Try to enable USB3 LTM and LPM */
+               usb_enable_ltm(udev);
                usb_unlocked_enable_lpm(udev);
        }
 
@@ -3489,6 +3557,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
 
 void usb_unlocked_enable_lpm(struct usb_device *udev) { }
 EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+
+int usb_disable_ltm(struct usb_device *udev)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev) { }
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
 #endif
 
 
@@ -4038,6 +4115,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
        }
 
+       if (hcd->phy && !hdev->parent) {
+               if (portstatus & USB_PORT_STAT_CONNECTION)
+                       usb_phy_notify_connect(hcd->phy, port1);
+               else
+                       usb_phy_notify_disconnect(hcd->phy, port1);
+       }
+
        /* Return now if debouncing failed or nothing is connected or
         * the device was "removed".
         */
@@ -4672,6 +4756,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        }
        parent_hub = hdev_to_hub(parent_hdev);
 
+       /* Disable LPM and LTM while we reset the device and reinstall the alt
+        * settings.  Device-initiated LPM settings, and system exit latency
+        * settings are cleared when the device is reset, so we have to set
+        * them up again.
+        */
+       ret = usb_unlocked_disable_lpm(udev);
+       if (ret) {
+               dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
+               goto re_enumerate;
+       }
+       ret = usb_disable_ltm(udev);
+       if (ret) {
+               dev_err(&udev->dev, "%s Failed to disable LTM\n.",
+                               __func__);
+               goto re_enumerate;
+       }
+
        set_bit(port1, parent_hub->busy_bits);
        for (i = 0; i < SET_CONFIG_TRIES; ++i) {
 
@@ -4699,22 +4800,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                goto done;
 
        mutex_lock(hcd->bandwidth_mutex);
-       /* Disable LPM while we reset the device and reinstall the alt settings.
-        * Device-initiated LPM settings, and system exit latency settings are
-        * cleared when the device is reset, so we have to set them up again.
-        */
-       ret = usb_disable_lpm(udev);
-       if (ret) {
-               dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
-               mutex_unlock(hcd->bandwidth_mutex);
-               goto done;
-       }
        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");
-               usb_enable_lpm(udev);
                mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
        }
@@ -4726,7 +4816,6 @@ 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);
-               usb_enable_lpm(udev);
                mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
        }
@@ -4765,17 +4854,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                                desc->bInterfaceNumber,
                                desc->bAlternateSetting,
                                ret);
-                       usb_unlocked_enable_lpm(udev);
                        goto re_enumerate;
                }
        }
 
-       /* Now that the alt settings are re-installed, enable LPM. */
-       usb_unlocked_enable_lpm(udev);
 done:
+       /* Now that the alt settings are re-installed, enable LTM and LPM. */
+       usb_unlocked_enable_lpm(udev);
+       usb_enable_ltm(udev);
        return 0;
  
 re_enumerate:
+       /* LPM state doesn't matter when we're about to destroy the device. */
        hub_port_logical_disconnect(parent_hub, port1);
        return -ENODEV;
 }