usbcore: fix up device and power state tests

This patch (as734) rationalizes the various tests of device state and
power states.  There are duplications and mistaken tests in several
places.

Perhaps the most interesting challenge is where the hub driver tests to
see that all the child devices are suspended before allowing itself to
be suspended.  When CONFIG_USB_SUSPEND is set the test is
straightforward, since we expect that the children _will_ be suspended.
But when CONFIG_USB_SUSPEND isn't set, it's not so clear what should be
done.  The code compromises by checking the child's
power.power_state.event field.


Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 92ecc4e..affbfb5 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -757,11 +757,13 @@
 	struct usb_device_driver	*udriver;
 	int				status = 0;
 
+	if (udev->state == USB_STATE_NOTATTACHED ||
+			udev->state == USB_STATE_SUSPENDED)
+		goto done;
+
 	if (udev->dev.driver == NULL)
 		goto done;
 	udriver = to_usb_device_driver(udev->dev.driver);
-	if (udev->dev.power.power_state.event == msg.event)
-		goto done;
 	status = udriver->suspend(udev, msg);
 
 done:
@@ -776,14 +778,13 @@
 	struct usb_device_driver	*udriver;
 	int				status = 0;
 
-	if (udev->dev.power.power_state.event == PM_EVENT_ON)
+	if (udev->state == USB_STATE_NOTATTACHED ||
+			udev->state != USB_STATE_SUSPENDED)
 		goto done;
 
 	if (udev->dev.driver == NULL)
 		goto done;
 	udriver = to_usb_device_driver(udev->dev.driver);
-	if (udev->state == USB_STATE_NOTATTACHED)
-		goto done;
 	status = udriver->resume(udev);
 
 done:
@@ -798,15 +799,15 @@
 	struct usb_driver	*driver;
 	int			status = 0;
 
+	/* with no hardware, USB interfaces only use FREEZE and ON states */
+	if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+			!is_active(intf))
+		goto done;
+
 	if (intf->dev.driver == NULL)
 		goto done;
-
 	driver = to_usb_driver(intf->dev.driver);
 
-	/* with no hardware, USB interfaces only use FREEZE and ON states */
-	if (!is_active(intf))
-		goto done;
-
 	if (driver->suspend && driver->resume) {
 		status = driver->suspend(intf, msg);
 		if (status)
@@ -831,25 +832,16 @@
 static int resume_interface(struct usb_interface *intf)
 {
 	struct usb_driver	*driver;
-	struct usb_device	*udev;
 	int			status = 0;
 
-	if (intf->dev.power.power_state.event == PM_EVENT_ON)
+	if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+			is_active(intf))
 		goto done;
 
 	if (intf->dev.driver == NULL)
 		goto done;
-
 	driver = to_usb_driver(intf->dev.driver);
 
-	udev = interface_to_usbdev(intf);
-	if (udev->state == USB_STATE_NOTATTACHED)
-		goto done;
-
-	/* if driver was suspended, it has a resume method;
-	 * however, sysfs can wrongly mark things as suspended
-	 * (on the "no suspend method" FIXME path above)
-	 */
 	if (driver->resume) {
 		status = driver->resume(intf);
 		if (status)
@@ -904,6 +896,12 @@
 	int			i;
 	struct usb_interface	*intf;
 
+	/* Can't resume if the parent is suspended */
+	if (udev->parent && udev->parent->state == USB_STATE_SUSPENDED) {
+		dev_warn(&udev->dev, "can't resume; parent is suspended\n");
+		return -EHOSTUNREACH;
+	}
+
 	status = resume_device(udev);
 	if (status == 0 && udev->actconfig) {
 		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {