Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 303994
b: refs/heads/master
c: 8306095
h: refs/heads/master
v: v3
  • Loading branch information
Sarah Sharp committed May 18, 2012
1 parent 52164b9 commit b5bff30
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 1ea7e0e8e3d0f50901d335ea4178ab2aa8c88201
refs/heads/master: 8306095fd2c1100e8244c09bf560f97aca5a311d
54 changes: 53 additions & 1 deletion trunk/drivers/usb/core/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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__);

Expand Down Expand Up @@ -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].
Expand All @@ -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;

Expand All @@ -361,14 +386,21 @@ 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;

/* Autoresume for set_interface call below */
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.
*/
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down
27 changes: 27 additions & 0 deletions trunk/drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -2702,6 +2702,12 @@ 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_unlocked_disable_lpm(udev)) {
dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
__func__);
return -ENOMEM;
}

/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
status = set_port_feature(hub->hdev,
Expand All @@ -2725,6 +2731,9 @@ 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 */
usb_unlocked_enable_lpm(udev);

/* System sleep transitions should never fail */
if (!PMSG_IS_AUTO(msg))
status = 0;
Expand Down Expand Up @@ -2922,6 +2931,9 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
/* Try to enable USB2 hardware LPM */
if (udev->usb2_hw_lpm_capable == 1)
usb_set_usb2_hardware_lpm(udev, 1);

/* Try to enable USB3 LPM */
usb_unlocked_enable_lpm(udev);
}

return status;
Expand Down Expand Up @@ -4681,11 +4693,22 @@ 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;
}
Expand All @@ -4697,6 +4720,7 @@ 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;
}
Expand Down Expand Up @@ -4735,10 +4759,13 @@ 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:
return 0;

Expand Down
38 changes: 38 additions & 0 deletions trunk/drivers/usb/core/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -1308,10 +1308,19 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
* Remove the current alt setting and add the new alt setting.
*/
mutex_lock(hcd->bandwidth_mutex);
/* Disable LPM, and re-enable it once the new alt setting is installed,
* so that the xHCI driver can recalculate the U1/U2 timeouts.
*/
if (usb_disable_lpm(dev)) {
dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__);
mutex_unlock(hcd->bandwidth_mutex);
return -ENOMEM;
}
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
if (ret < 0) {
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
alternate);
usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
return ret;
}
Expand All @@ -1334,6 +1343,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
} else if (ret < 0) {
/* Re-instate the old alt setting */
usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
return ret;
}
Expand All @@ -1354,6 +1364,9 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)

iface->cur_altsetting = alt;

/* Now that the interface is installed, re-enable LPM. */
usb_unlocked_enable_lpm(dev);

/* If the interface only has one altsetting and the device didn't
* accept the request, we attempt to carry out the equivalent action
* by manually clearing the HALT feature for each endpoint in the
Expand Down Expand Up @@ -1437,6 +1450,14 @@ int usb_reset_configuration(struct usb_device *dev)
config = dev->actconfig;
retval = 0;
mutex_lock(hcd->bandwidth_mutex);
/* Disable LPM, and re-enable it once the configuration is reset, so
* that the xHCI driver can recalculate the U1/U2 timeouts.
*/
if (usb_disable_lpm(dev)) {
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
mutex_unlock(hcd->bandwidth_mutex);
return -ENOMEM;
}
/* Make sure we have enough bandwidth for each alternate setting 0 */
for (i = 0; i < config->desc.bNumInterfaces; i++) {
struct usb_interface *intf = config->interface[i];
Expand Down Expand Up @@ -1465,6 +1486,7 @@ int usb_reset_configuration(struct usb_device *dev)
usb_hcd_alloc_bandwidth(dev, NULL,
alt, intf->cur_altsetting);
}
usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
return retval;
}
Expand Down Expand Up @@ -1502,6 +1524,8 @@ int usb_reset_configuration(struct usb_device *dev)
create_intf_ep_devs(intf);
}
}
/* Now that the interfaces are installed, re-enable LPM. */
usb_unlocked_enable_lpm(dev);
return 0;
}
EXPORT_SYMBOL_GPL(usb_reset_configuration);
Expand Down Expand Up @@ -1763,8 +1787,18 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
* this call fails, the device state is unchanged.
*/
mutex_lock(hcd->bandwidth_mutex);
/* Disable LPM, and re-enable it once the new configuration is
* installed, so that the xHCI driver can recalculate the U1/U2
* timeouts.
*/
if (usb_disable_lpm(dev)) {
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
mutex_unlock(hcd->bandwidth_mutex);
return -ENOMEM;
}
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
if (ret < 0) {
usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev);
goto free_interfaces;
Expand All @@ -1784,6 +1818,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev);
goto free_interfaces;
Expand Down Expand Up @@ -1838,6 +1873,9 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
cp->string = usb_cache_string(dev, cp->desc.iConfiguration);

/* Now that the interfaces are installed, re-enable LPM. */
usb_unlocked_enable_lpm(dev);

/* Now that all the interfaces are set up, register them
* to trigger binding of drivers to interfaces. probe()
* routines may install different altsettings and may
Expand Down
2 changes: 2 additions & 0 deletions trunk/include/linux/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ struct usb_device {
unsigned lpm_capable:1;
unsigned usb2_hw_lpm_capable:1;
unsigned usb2_hw_lpm_enabled:1;
unsigned usb3_lpm_enabled:1;
int string_langid;

/* static strings from the device */
Expand Down Expand Up @@ -555,6 +556,7 @@ struct usb_device {
struct usb3_lpm_parameters u1_params;
struct usb3_lpm_parameters u2_params;
unsigned lpm_disable_count;
unsigned hub_initiated_lpm_disable_count;
};
#define to_usb_device(d) container_of(d, struct usb_device, dev)

Expand Down

0 comments on commit b5bff30

Please sign in to comment.