Commit 65bfd296 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

USB: Enhance usage of pm_message_t



This patch (as1177) modifies the USB core suspend and resume
routines.  The resume functions now will take a pm_message_t argument,
so they will know what sort of resume is occurring.  The new argument
is also passed to the port suspend/resume and bus suspend/resume
routines (although they don't use it for anything but debugging).

In addition, special pm_message_t values are used for user-initiated,
device-initiated (i.e., remote wakeup), and automatic suspend/resume.
By testing these values, drivers can tell whether or not a particular
suspend was an autosuspend.  Unfortunately, they can't do the same for
resumes -- not until the pm_message_t argument is also passed to the
drivers' resume methods.  That will require a bigger change.

IMO, the whole Power Management framework should have been set up this
way in the first place.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4ec06d62
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1275,7 +1275,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
	struct acm *acm = usb_get_intfdata(intf);
	int cnt;

	if (acm->dev->auto_pm) {
	if (message.event & PM_EVENT_AUTO) {
		int b;

		spin_lock_irq(&acm->read_lock);
+2 −1
Original line number Diff line number Diff line
@@ -764,7 +764,8 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)

	mutex_lock(&desc->plock);
#ifdef CONFIG_PM
	if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) {
	if ((message.event & PM_EVENT_AUTO) &&
			test_bit(WDM_IN_USE, &desc->flags)) {
		rv = -EBUSY;
	} else {
#endif
+34 −27
Original line number Diff line number Diff line
@@ -922,7 +922,7 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
}

/* Caller has locked udev's pm_mutex */
static int usb_resume_device(struct usb_device *udev)
static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
{
	struct usb_device_driver	*udriver;
	int				status = 0;
@@ -940,7 +940,7 @@ static int usb_resume_device(struct usb_device *udev)
		udev->reset_resume = 1;

	udriver = to_usb_device_driver(udev->dev.driver);
	status = udriver->resume(udev);
	status = udriver->resume(udev, msg);

 done:
	dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
@@ -969,7 +969,7 @@ static int usb_suspend_interface(struct usb_device *udev,
		status = driver->suspend(intf, msg);
		if (status == 0)
			mark_quiesced(intf);
		else if (!udev->auto_pm)
		else if (!(msg.event & PM_EVENT_AUTO))
			dev_err(&intf->dev, "%s error %d\n",
					"suspend", status);
	} else {
@@ -987,7 +987,7 @@ static int usb_suspend_interface(struct usb_device *udev,

/* Caller has locked intf's usb_device's pm_mutex */
static int usb_resume_interface(struct usb_device *udev,
		struct usb_interface *intf, int reset_resume)
		struct usb_interface *intf, pm_message_t msg, int reset_resume)
{
	struct usb_driver	*driver;
	int			status = 0;
@@ -1138,10 +1138,9 @@ static inline int autosuspend_check(struct usb_device *udev, int reschedule)
 * all the interfaces which were suspended are resumed so that they remain
 * in the same state as the device.
 *
 * If an autosuspend is in progress (@udev->auto_pm is set), the routine
 * checks first to make sure that neither the device itself or any of its
 * active interfaces is in use (pm_usage_cnt is greater than 0).  If they
 * are, the autosuspend fails.
 * If an autosuspend is in progress the routine checks first to make sure
 * that neither the device itself or any of its active interfaces is in use
 * (pm_usage_cnt is greater than 0).  If they are, the autosuspend fails.
 *
 * If the suspend succeeds, the routine recursively queues an autosuspend
 * request for @udev's parent device, thereby propagating the change up
@@ -1176,7 +1175,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)

	udev->do_remote_wakeup = device_may_wakeup(&udev->dev);

	if (udev->auto_pm) {
	if (msg.event & PM_EVENT_AUTO) {
		status = autosuspend_check(udev, 0);
		if (status < 0)
			goto done;
@@ -1196,13 +1195,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)

	/* If the suspend failed, resume interfaces that did get suspended */
	if (status != 0) {
		pm_message_t msg2;

		msg2.event = msg.event ^ (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
		while (--i >= 0) {
			intf = udev->actconfig->interface[i];
			usb_resume_interface(udev, intf, 0);
			usb_resume_interface(udev, intf, msg2, 0);
		}

		/* Try another autosuspend when the interfaces aren't busy */
		if (udev->auto_pm)
		if (msg.event & PM_EVENT_AUTO)
			autosuspend_check(udev, status == -EBUSY);

	/* If the suspend succeeded then prevent any more URB submissions,
@@ -1232,6 +1234,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
/**
 * usb_resume_both - resume a USB device and its interfaces
 * @udev: the usb_device to resume
 * @msg: Power Management message describing this state transition
 *
 * This is the central routine for resuming USB devices.  It calls the
 * the resume method for @udev and then calls the resume methods for all
@@ -1257,7 +1260,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
 *
 * This routine can run only in process context.
 */
static int usb_resume_both(struct usb_device *udev)
static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
{
	int			status = 0;
	int			i;
@@ -1273,14 +1276,15 @@ static int usb_resume_both(struct usb_device *udev)

	/* Propagate the resume up the tree, if necessary */
	if (udev->state == USB_STATE_SUSPENDED) {
		if (udev->auto_pm && udev->autoresume_disabled) {
		if ((msg.event & PM_EVENT_AUTO) &&
				udev->autoresume_disabled) {
			status = -EPERM;
			goto done;
		}
		if (parent) {
			status = usb_autoresume_device(parent);
			if (status == 0) {
				status = usb_resume_device(udev);
				status = usb_resume_device(udev, msg);
				if (status || udev->state ==
						USB_STATE_NOTATTACHED) {
					usb_autosuspend_device(parent);
@@ -1303,15 +1307,16 @@ static int usb_resume_both(struct usb_device *udev)
			/* We can't progagate beyond the USB subsystem,
			 * so if a root hub's controller is suspended
			 * then we're stuck. */
			status = usb_resume_device(udev);
			status = usb_resume_device(udev, msg);
		}
	} else if (udev->reset_resume)
		status = usb_resume_device(udev);
		status = usb_resume_device(udev, msg);

	if (status == 0 && udev->actconfig) {
		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
			intf = udev->actconfig->interface[i];
			usb_resume_interface(udev, intf, udev->reset_resume);
			usb_resume_interface(udev, intf, msg,
					udev->reset_resume);
		}
	}

@@ -1339,13 +1344,13 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
		udev->last_busy = jiffies;
	if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) {
		if (udev->state == USB_STATE_SUSPENDED)
			status = usb_resume_both(udev);
			status = usb_resume_both(udev, PMSG_AUTO_RESUME);
		if (status != 0)
			udev->pm_usage_cnt -= inc_usage_cnt;
		else if (inc_usage_cnt)
			udev->last_busy = jiffies;
	} else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) {
		status = usb_suspend_both(udev, PMSG_SUSPEND);
		status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
	}
	usb_pm_unlock(udev);
	return status;
@@ -1469,13 +1474,14 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
		udev->last_busy = jiffies;
		if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) {
			if (udev->state == USB_STATE_SUSPENDED)
				status = usb_resume_both(udev);
				status = usb_resume_both(udev,
						PMSG_AUTO_RESUME);
			if (status != 0)
				intf->pm_usage_cnt -= inc_usage_cnt;
			else
				udev->last_busy = jiffies;
		} else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
			status = usb_suspend_both(udev, PMSG_SUSPEND);
			status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
		}
	}
	usb_pm_unlock(udev);
@@ -1700,6 +1706,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
/**
 * usb_external_resume_device - external resume of a USB device and its interfaces
 * @udev: the usb_device to resume
 * @msg: Power Management message describing this state transition
 *
 * This routine handles external resume requests: ones not generated
 * internally by a USB driver (autoresume) but rather coming from the user
@@ -1708,13 +1715,13 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
 *
 * The caller must hold @udev's device lock.
 */
int usb_external_resume_device(struct usb_device *udev)
int usb_external_resume_device(struct usb_device *udev, pm_message_t msg)
{
	int	status;

	usb_pm_lock(udev);
	udev->auto_pm = 0;
	status = usb_resume_both(udev);
	status = usb_resume_both(udev, msg);
	udev->last_busy = jiffies;
	usb_pm_unlock(udev);
	if (status == 0)
@@ -1727,7 +1734,7 @@ int usb_external_resume_device(struct usb_device *udev)
	return status;
}

int usb_suspend(struct device *dev, pm_message_t message)
int usb_suspend(struct device *dev, pm_message_t msg)
{
	struct usb_device	*udev;

@@ -1746,10 +1753,10 @@ int usb_suspend(struct device *dev, pm_message_t message)
	}

	udev->skip_sys_resume = 0;
	return usb_external_suspend_device(udev, message);
	return usb_external_suspend_device(udev, msg);
}

int usb_resume(struct device *dev)
int usb_resume(struct device *dev, pm_message_t msg)
{
	struct usb_device	*udev;

@@ -1761,7 +1768,7 @@ int usb_resume(struct device *dev)
	 */
	if (udev->skip_sys_resume)
		return 0;
	return usb_external_resume_device(udev);
	return usb_external_resume_device(udev, msg);
}

#endif /* CONFIG_PM */
+5 −5
Original line number Diff line number Diff line
@@ -200,18 +200,18 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
	 * interfaces manually by doing a bus (or "global") suspend.
	 */
	if (!udev->parent)
		rc = hcd_bus_suspend(udev);
		rc = hcd_bus_suspend(udev, msg);

	/* Non-root devices don't need to do anything for FREEZE or PRETHAW */
	else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
		rc = 0;
	else
		rc = usb_port_suspend(udev);
		rc = usb_port_suspend(udev, msg);

	return rc;
}

static int generic_resume(struct usb_device *udev)
static int generic_resume(struct usb_device *udev, pm_message_t msg)
{
	int rc;

@@ -221,9 +221,9 @@ static int generic_resume(struct usb_device *udev)
	 * interfaces manually by doing a bus (or "global") resume.
	 */
	if (!udev->parent)
		rc = hcd_bus_resume(udev);
		rc = hcd_bus_resume(udev, msg);
	else
		rc = usb_port_resume(udev);
		rc = usb_port_resume(udev, msg);
	return rc;
}

+5 −5
Original line number Diff line number Diff line
@@ -1573,14 +1573,14 @@ int usb_hcd_get_frame_number (struct usb_device *udev)

#ifdef	CONFIG_PM

int hcd_bus_suspend(struct usb_device *rhdev)
int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
{
	struct usb_hcd	*hcd = container_of(rhdev->bus, struct usb_hcd, self);
	int		status;
	int		old_state = hcd->state;

	dev_dbg(&rhdev->dev, "bus %s%s\n",
			rhdev->auto_pm ? "auto-" : "", "suspend");
			(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend");
	if (!hcd->driver->bus_suspend) {
		status = -ENOENT;
	} else {
@@ -1598,14 +1598,14 @@ int hcd_bus_suspend(struct usb_device *rhdev)
	return status;
}

int hcd_bus_resume(struct usb_device *rhdev)
int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
{
	struct usb_hcd	*hcd = container_of(rhdev->bus, struct usb_hcd, self);
	int		status;
	int		old_state = hcd->state;

	dev_dbg(&rhdev->dev, "usb %s%s\n",
			rhdev->auto_pm ? "auto-" : "", "resume");
			(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
	if (!hcd->driver->bus_resume)
		return -ENOENT;
	if (hcd->state == HC_STATE_RUNNING)
@@ -1638,7 +1638,7 @@ static void hcd_resume_work(struct work_struct *work)

	usb_lock_device(udev);
	usb_mark_last_busy(udev);
	usb_external_resume_device(udev);
	usb_external_resume_device(udev, PMSG_REMOTE_RESUME);
	usb_unlock_device(udev);
}

Loading