|
@@ -288,6 +288,7 @@ static int usb_probe_interface(struct device *dev)
|
|
|
struct usb_device *udev = interface_to_usbdev(intf);
|
|
|
const struct usb_device_id *id;
|
|
|
int error = -ENODEV;
|
|
|
+ int lpm_disable_error;
|
|
|
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
|
@@ -324,6 +325,25 @@ static int usb_probe_interface(struct device *dev)
|
|
|
if (driver->supports_autosuspend)
|
|
|
pm_runtime_enable(dev);
|
|
|
|
|
|
+ /* If the new driver doesn't allow hub-initiated LPM, and we can't
|
|
|
+ * disable hub-initiated LPM, then fail the probe.
|
|
|
+ *
|
|
|
+ * Otherwise, leaving LPM enabled should be harmless, because the
|
|
|
+ * endpoint intervals should remain the same, and the U1/U2 timeouts
|
|
|
+ * should remain the same.
|
|
|
+ *
|
|
|
+ * If we need to install alt setting 0 before probe, or another alt
|
|
|
+ * setting during probe, that should also be fine. usb_set_interface()
|
|
|
+ * will attempt to disable LPM, and fail if it can't disable it.
|
|
|
+ */
|
|
|
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
|
|
+ if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
|
|
|
+ dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
|
|
|
+ __func__, driver->name);
|
|
|
+ error = lpm_disable_error;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
/* Carry out a deferred switch to altsetting 0 */
|
|
|
if (intf->needs_altsetting0) {
|
|
|
error = usb_set_interface(udev, intf->altsetting[0].
|
|
@@ -338,6 +358,11 @@ static int usb_probe_interface(struct device *dev)
|
|
|
goto err;
|
|
|
|
|
|
intf->condition = USB_INTERFACE_BOUND;
|
|
|
+
|
|
|
+ /* If the LPM disable succeeded, balance the ref counts. */
|
|
|
+ if (!lpm_disable_error)
|
|
|
+ usb_unlocked_enable_lpm(udev);
|
|
|
+
|
|
|
usb_autosuspend_device(udev);
|
|
|
return error;
|
|
|
|
|
@@ -361,7 +386,7 @@ static int usb_unbind_interface(struct device *dev)
|
|
|
struct usb_driver *driver = to_usb_driver(dev->driver);
|
|
|
struct usb_interface *intf = to_usb_interface(dev);
|
|
|
struct usb_device *udev;
|
|
|
- int error, r;
|
|
|
+ int error, r, lpm_disable_error;
|
|
|
|
|
|
intf->condition = USB_INTERFACE_UNBINDING;
|
|
|
|
|
@@ -369,6 +394,13 @@ static int usb_unbind_interface(struct device *dev)
|
|
|
udev = interface_to_usbdev(intf);
|
|
|
error = usb_autoresume_device(udev);
|
|
|
|
|
|
+ /* Hub-initiated LPM policy may change, so attempt to disable LPM until
|
|
|
+ * the driver is unbound. If LPM isn't disabled, that's fine because it
|
|
|
+ * wouldn't be enabled unless all the bound interfaces supported
|
|
|
+ * hub-initiated LPM.
|
|
|
+ */
|
|
|
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
|
|
+
|
|
|
/* Terminate all URBs for this interface unless the driver
|
|
|
* supports "soft" unbinding.
|
|
|
*/
|
|
@@ -402,6 +434,10 @@ static int usb_unbind_interface(struct device *dev)
|
|
|
intf->condition = USB_INTERFACE_UNBOUND;
|
|
|
intf->needs_remote_wakeup = 0;
|
|
|
|
|
|
+ /* Attempt to re-enable USB3 LPM, if the disable succeeded. */
|
|
|
+ if (!lpm_disable_error)
|
|
|
+ usb_unlocked_enable_lpm(udev);
|
|
|
+
|
|
|
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
|
|
|
if (driver->supports_autosuspend)
|
|
|
pm_runtime_disable(dev);
|
|
@@ -442,17 +478,29 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|
|
struct usb_interface *iface, void *priv)
|
|
|
{
|
|
|
struct device *dev = &iface->dev;
|
|
|
+ struct usb_device *udev;
|
|
|
int retval = 0;
|
|
|
+ int lpm_disable_error;
|
|
|
|
|
|
if (dev->driver)
|
|
|
return -EBUSY;
|
|
|
|
|
|
+ udev = interface_to_usbdev(iface);
|
|
|
+
|
|
|
dev->driver = &driver->drvwrap.driver;
|
|
|
usb_set_intfdata(iface, priv);
|
|
|
iface->needs_binding = 0;
|
|
|
|
|
|
iface->condition = USB_INTERFACE_BOUND;
|
|
|
|
|
|
+ /* Disable LPM until this driver is bound. */
|
|
|
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
|
|
+ if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
|
|
|
+ dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
|
|
|
+ __func__, driver->name);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
/* Claimed interfaces are initially inactive (suspended) and
|
|
|
* runtime-PM-enabled, but only if the driver has autosuspend
|
|
|
* support. Otherwise they are marked active, to prevent the
|
|
@@ -471,6 +519,10 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|
|
if (device_is_registered(dev))
|
|
|
retval = device_bind_driver(dev);
|
|
|
|
|
|
+ /* Attempt to re-enable USB3 LPM, if the disable was successful. */
|
|
|
+ if (!lpm_disable_error)
|
|
|
+ usb_unlocked_enable_lpm(udev);
|
|
|
+
|
|
|
return retval;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(usb_driver_claim_interface);
|