Skip to content

Commit

Permalink
usbcore: set device and power states properly
Browse files Browse the repository at this point in the history
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: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Sep 27, 2006
1 parent 4d064c0 commit 2bf4086
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 35 deletions.
71 changes: 42 additions & 29 deletions drivers/usb/core/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -755,48 +755,57 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
static int suspend_device(struct usb_device *udev, pm_message_t msg)
{
struct usb_device_driver *udriver;
int status = 0;

if (udev->dev.driver == NULL)
return 0;
goto done;
udriver = to_usb_device_driver(udev->dev.driver);
if (udev->dev.power.power_state.event == msg.event)
return 0;
return udriver->suspend(udev, msg);
goto done;
status = udriver->suspend(udev, msg);

done:
if (status == 0)
udev->dev.power.power_state.event = msg.event;
return status;
}

/* Caller has locked udev */
static int resume_device(struct usb_device *udev)
{
struct usb_device_driver *udriver;
int status = 0;

if (udev->dev.power.power_state.event == PM_EVENT_ON)
return 0;

/* mark things as "on" immediately, no matter what errors crop up */
udev->dev.power.power_state.event = PM_EVENT_ON;
goto done;

if (udev->dev.driver == NULL)
return 0;
goto done;
udriver = to_usb_device_driver(udev->dev.driver);
if (udev->state == USB_STATE_NOTATTACHED)
return 0;
return udriver->resume(udev);
goto done;
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 */
static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
{
struct usb_driver *driver;
int status;
int status = 0;

if (intf->dev.driver == NULL)
return 0;
goto done;

driver = to_usb_driver(intf->dev.driver);

/* with no hardware, USB interfaces only use FREEZE and ON states */
if (!is_active(intf))
return 0;
goto done;

if (driver->suspend && driver->resume) {
status = driver->suspend(intf, msg);
Expand All @@ -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",
driver->name);
mark_quiesced(intf);
status = 0;
}

done:
if (status == 0)
intf->dev.power.power_state.event = msg.event;
return status;
}

Expand All @@ -820,40 +832,41 @@ static int resume_interface(struct usb_interface *intf)
{
struct usb_driver *driver;
struct usb_device *udev;
int status;
int status = 0;

if (intf->dev.power.power_state.event == PM_EVENT_ON)
return 0;

/* mark things as "on" immediately, no matter what errors crop up */
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;
}
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)
return 0;
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) {
if (status)
dev_err(&intf->dev, "%s error %d\n",
"resume", status);
mark_quiesced(intf);
}
} else
else
mark_active(intf);
} else {
dev_warn(&intf->dev, "no resume for driver %s?\n",
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 */
Expand Down
10 changes: 4 additions & 6 deletions drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -1582,9 +1582,10 @@ static int __usb_port_suspend (struct usb_device *udev, int port1)
if (udev->parent)
status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
udev);

if (status == 0)
udev->dev.power.power_state = PMSG_SUSPEND;
else {
dev_dbg(&udev->dev, "usb suspend\n");
usb_set_device_state(udev, USB_STATE_SUSPENDED);
}
return status;
}

Expand Down Expand Up @@ -1617,8 +1618,6 @@ int usb_port_suspend(struct usb_device *udev)
return -ENODEV;
return __usb_port_suspend(udev, udev->portnum);
#else
/* NOTE: udev->state unchanged, it's not lying ... */
udev->dev.power.power_state = PMSG_SUSPEND;
return 0;
#endif
}
Expand Down Expand Up @@ -1647,7 +1646,6 @@ static int finish_port_resume(struct usb_device *udev)
usb_set_device_state(udev, udev->actconfig
? USB_STATE_CONFIGURED
: USB_STATE_ADDRESS);
udev->dev.power.power_state = PMSG_ON;

/* 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,
Expand Down

0 comments on commit 2bf4086

Please sign in to comment.