Commit 2bf4086d authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

usbcore: set device and power states properly

This patch (as733) fixes up the places where device states and power
states are set in usbcore.  Right now things are duplicated or missing;
this should straighten things out.

The idea is that udev->state is USB_STATE_SUSPENDED exactly when the
device's upstream port has been suspended, whereas
udev->dev.power.power_state.event reflects the result of the last call
to the suspend/resume routines (which might not actually change the
device state, especially if CONFIG_USB_SUSPEND isn't set).
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4d064c08
...@@ -755,48 +755,57 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); ...@@ -755,48 +755,57 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
static int suspend_device(struct usb_device *udev, pm_message_t msg) static int suspend_device(struct usb_device *udev, pm_message_t msg)
{ {
struct usb_device_driver *udriver; struct usb_device_driver *udriver;
int status = 0;
if (udev->dev.driver == NULL) if (udev->dev.driver == NULL)
return 0; goto done;
udriver = to_usb_device_driver(udev->dev.driver); udriver = to_usb_device_driver(udev->dev.driver);
if (udev->dev.power.power_state.event == msg.event) if (udev->dev.power.power_state.event == msg.event)
return 0; goto done;
return udriver->suspend(udev, msg); status = udriver->suspend(udev, msg);
done:
if (status == 0)
udev->dev.power.power_state.event = msg.event;
return status;
} }
/* Caller has locked udev */ /* Caller has locked udev */
static int resume_device(struct usb_device *udev) static int resume_device(struct usb_device *udev)
{ {
struct usb_device_driver *udriver; struct usb_device_driver *udriver;
int status = 0;
if (udev->dev.power.power_state.event == PM_EVENT_ON) if (udev->dev.power.power_state.event == PM_EVENT_ON)
return 0; goto done;
/* mark things as "on" immediately, no matter what errors crop up */
udev->dev.power.power_state.event = PM_EVENT_ON;
if (udev->dev.driver == NULL) if (udev->dev.driver == NULL)
return 0; goto done;
udriver = to_usb_device_driver(udev->dev.driver); udriver = to_usb_device_driver(udev->dev.driver);
if (udev->state == USB_STATE_NOTATTACHED) if (udev->state == USB_STATE_NOTATTACHED)
return 0; goto done;
return udriver->resume(udev); status = udriver->resume(udev);
done:
if (status == 0)
udev->dev.power.power_state.event = PM_EVENT_ON;
return status;
} }
/* Caller has locked intf's usb_device */ /* Caller has locked intf's usb_device */
static int suspend_interface(struct usb_interface *intf, pm_message_t msg) static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
{ {
struct usb_driver *driver; struct usb_driver *driver;
int status; int status = 0;
if (intf->dev.driver == NULL) if (intf->dev.driver == NULL)
return 0; goto done;
driver = to_usb_driver(intf->dev.driver); driver = to_usb_driver(intf->dev.driver);
/* with no hardware, USB interfaces only use FREEZE and ON states */ /* with no hardware, USB interfaces only use FREEZE and ON states */
if (!is_active(intf)) if (!is_active(intf))
return 0; goto done;
if (driver->suspend && driver->resume) { if (driver->suspend && driver->resume) {
status = driver->suspend(intf, msg); status = driver->suspend(intf, msg);
...@@ -810,8 +819,11 @@ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) ...@@ -810,8 +819,11 @@ static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
dev_warn(&intf->dev, "no suspend for driver %s?\n", dev_warn(&intf->dev, "no suspend for driver %s?\n",
driver->name); driver->name);
mark_quiesced(intf); mark_quiesced(intf);
status = 0;
} }
done:
if (status == 0)
intf->dev.power.power_state.event = msg.event;
return status; return status;
} }
...@@ -820,24 +832,19 @@ static int resume_interface(struct usb_interface *intf) ...@@ -820,24 +832,19 @@ static int resume_interface(struct usb_interface *intf)
{ {
struct usb_driver *driver; struct usb_driver *driver;
struct usb_device *udev; struct usb_device *udev;
int status; int status = 0;
if (intf->dev.power.power_state.event == PM_EVENT_ON) if (intf->dev.power.power_state.event == PM_EVENT_ON)
return 0; goto done;
/* mark things as "on" immediately, no matter what errors crop up */ if (intf->dev.driver == NULL)
intf->dev.power.power_state.event = PM_EVENT_ON; goto done;
if (intf->dev.driver == NULL) {
intf->dev.power.power_state.event = PM_EVENT_FREEZE;
return 0;
}
driver = to_usb_driver(intf->dev.driver); driver = to_usb_driver(intf->dev.driver);
udev = interface_to_usbdev(intf); udev = interface_to_usbdev(intf);
if (udev->state == USB_STATE_NOTATTACHED) if (udev->state == USB_STATE_NOTATTACHED)
return 0; goto done;
/* if driver was suspended, it has a resume method; /* if driver was suspended, it has a resume method;
* however, sysfs can wrongly mark things as suspended * however, sysfs can wrongly mark things as suspended
...@@ -845,15 +852,21 @@ static int resume_interface(struct usb_interface *intf) ...@@ -845,15 +852,21 @@ static int resume_interface(struct usb_interface *intf)
*/ */
if (driver->resume) { if (driver->resume) {
status = driver->resume(intf); status = driver->resume(intf);
if (status) { if (status)
dev_err(&intf->dev, "%s error %d\n", dev_err(&intf->dev, "%s error %d\n",
"resume", status); "resume", status);
mark_quiesced(intf); else
} mark_active(intf);
} else } else {
dev_warn(&intf->dev, "no resume for driver %s?\n", dev_warn(&intf->dev, "no resume for driver %s?\n",
driver->name); driver->name);
return 0; mark_active(intf);
}
done:
if (status == 0)
intf->dev.power.power_state.event = PM_EVENT_ON;
return status;
} }
/* Caller has locked udev */ /* Caller has locked udev */
......
...@@ -1582,9 +1582,10 @@ static int __usb_port_suspend (struct usb_device *udev, int port1) ...@@ -1582,9 +1582,10 @@ static int __usb_port_suspend (struct usb_device *udev, int port1)
if (udev->parent) if (udev->parent)
status = hub_port_suspend(hdev_to_hub(udev->parent), port1, status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
udev); udev);
else {
if (status == 0) dev_dbg(&udev->dev, "usb suspend\n");
udev->dev.power.power_state = PMSG_SUSPEND; usb_set_device_state(udev, USB_STATE_SUSPENDED);
}
return status; return status;
} }
...@@ -1617,8 +1618,6 @@ int usb_port_suspend(struct usb_device *udev) ...@@ -1617,8 +1618,6 @@ int usb_port_suspend(struct usb_device *udev)
return -ENODEV; return -ENODEV;
return __usb_port_suspend(udev, udev->portnum); return __usb_port_suspend(udev, udev->portnum);
#else #else
/* NOTE: udev->state unchanged, it's not lying ... */
udev->dev.power.power_state = PMSG_SUSPEND;
return 0; return 0;
#endif #endif
} }
...@@ -1647,7 +1646,6 @@ static int finish_port_resume(struct usb_device *udev) ...@@ -1647,7 +1646,6 @@ static int finish_port_resume(struct usb_device *udev)
usb_set_device_state(udev, udev->actconfig usb_set_device_state(udev, udev->actconfig
? USB_STATE_CONFIGURED ? USB_STATE_CONFIGURED
: USB_STATE_ADDRESS); : USB_STATE_ADDRESS);
udev->dev.power.power_state = PMSG_ON;
/* 10.5.4.5 says be sure devices in the tree are still there. /* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume, * For now let's assume the device didn't go crazy on resume,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment