[PATCH] usb_interface power state
This updates the handling of power state for USB interfaces. - Formalizes an existing invariant: interface "power state" is a boolean: ON when I/O is allowed, and FREEZE otherwise. It does so by defining some inlined helpers, then using them. - Adds a useful invariant: the only interfaces marked active are those bound to non-suspended drivers. Later patches build on this invariant. - Simplifies the interface driver API (and removes some error paths) by removing the requirement that they record power state changes during suspend and resume callbacks. Now usbcore does that. A few drivers were simplified to address that last change. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> drivers/usb/core/hub.c | 33 +++++++++------------ drivers/usb/core/message.c | 1 drivers/usb/core/usb.c | 65 +++++++++++++++++++++++++++++++++---------- drivers/usb/core/usb.h | 18 +++++++++++ drivers/usb/input/hid-core.c | 2 - drivers/usb/misc/usbtest.c | 10 ------ drivers/usb/net/pegasus.c | 2 - drivers/usb/net/usbnet.c | 2 - 8 files changed, 85 insertions(+), 48 deletions(-)
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
7586269c0b
commit
db69087437
@@ -1624,7 +1624,7 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
|
|||||||
struct usb_driver *driver;
|
struct usb_driver *driver;
|
||||||
|
|
||||||
intf = udev->actconfig->interface[i];
|
intf = udev->actconfig->interface[i];
|
||||||
if (state.event <= intf->dev.power.power_state.event)
|
if (!is_active(intf))
|
||||||
continue;
|
continue;
|
||||||
if (!intf->dev.driver)
|
if (!intf->dev.driver)
|
||||||
continue;
|
continue;
|
||||||
@@ -1632,11 +1632,12 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
|
|||||||
|
|
||||||
if (driver->suspend) {
|
if (driver->suspend) {
|
||||||
status = driver->suspend(intf, state);
|
status = driver->suspend(intf, state);
|
||||||
if (intf->dev.power.power_state.event != state.event
|
if (status == 0)
|
||||||
|| status)
|
mark_quiesced(intf);
|
||||||
|
else
|
||||||
dev_err(&intf->dev,
|
dev_err(&intf->dev,
|
||||||
"suspend %d fail, code %d\n",
|
"suspend error %d\n",
|
||||||
state.event, status);
|
status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* only drivers with suspend() can ever resume();
|
/* only drivers with suspend() can ever resume();
|
||||||
@@ -1787,7 +1788,7 @@ static int finish_port_resume(struct usb_device *udev)
|
|||||||
struct usb_driver *driver;
|
struct usb_driver *driver;
|
||||||
|
|
||||||
intf = udev->actconfig->interface[i];
|
intf = udev->actconfig->interface[i];
|
||||||
if (intf->dev.power.power_state.event == PM_EVENT_ON)
|
if (is_active(intf))
|
||||||
continue;
|
continue;
|
||||||
if (!intf->dev.driver) {
|
if (!intf->dev.driver) {
|
||||||
/* FIXME maybe force to alt 0 */
|
/* FIXME maybe force to alt 0 */
|
||||||
@@ -1800,12 +1801,14 @@ static int finish_port_resume(struct usb_device *udev)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* can we do better than just logging errors? */
|
/* can we do better than just logging errors? */
|
||||||
|
mark_active(intf);
|
||||||
status = driver->resume(intf);
|
status = driver->resume(intf);
|
||||||
if (intf->dev.power.power_state.event != PM_EVENT_ON
|
if (status < 0) {
|
||||||
|| status)
|
mark_quiesced(intf);
|
||||||
dev_dbg(&intf->dev,
|
dev_dbg(&intf->dev,
|
||||||
"resume fail, state %d code %d\n",
|
"resume error %d\n",
|
||||||
intf->dev.power.power_state.event, status);
|
status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
status = 0;
|
status = 0;
|
||||||
|
|
||||||
@@ -1952,7 +1955,7 @@ static int remote_wakeup(struct usb_device *udev)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hub_suspend(struct usb_interface *intf, pm_message_t state)
|
static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||||
{
|
{
|
||||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||||
struct usb_device *hdev = hub->hdev;
|
struct usb_device *hdev = hub->hdev;
|
||||||
@@ -1970,14 +1973,13 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t state)
|
|||||||
if (!udev)
|
if (!udev)
|
||||||
continue;
|
continue;
|
||||||
down(&udev->serialize);
|
down(&udev->serialize);
|
||||||
status = __usb_suspend_device(udev, port1, state);
|
status = __usb_suspend_device(udev, port1, msg);
|
||||||
up(&udev->serialize);
|
up(&udev->serialize);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
dev_dbg(&intf->dev, "suspend port %d --> %d\n",
|
dev_dbg(&intf->dev, "suspend port %d --> %d\n",
|
||||||
port1, status);
|
port1, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
intf->dev.power.power_state = state;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1988,9 +1990,6 @@ static int hub_resume(struct usb_interface *intf)
|
|||||||
unsigned port1;
|
unsigned port1;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (intf->dev.power.power_state.event == PM_EVENT_ON)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||||
struct usb_device *udev;
|
struct usb_device *udev;
|
||||||
u16 portstat, portchange;
|
u16 portstat, portchange;
|
||||||
@@ -2024,8 +2023,6 @@ static int hub_resume(struct usb_interface *intf)
|
|||||||
}
|
}
|
||||||
up(&udev->serialize);
|
up(&udev->serialize);
|
||||||
}
|
}
|
||||||
intf->dev.power.power_state = PMSG_ON;
|
|
||||||
|
|
||||||
hub->resume_root_hub = 0;
|
hub->resume_root_hub = 0;
|
||||||
hub_activate(hub);
|
hub_activate(hub);
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -1393,6 +1393,7 @@ free_interfaces:
|
|||||||
intf->dev.dma_mask = dev->dev.dma_mask;
|
intf->dev.dma_mask = dev->dev.dma_mask;
|
||||||
intf->dev.release = release_interface;
|
intf->dev.release = release_interface;
|
||||||
device_initialize (&intf->dev);
|
device_initialize (&intf->dev);
|
||||||
|
mark_quiesced(intf);
|
||||||
sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
|
sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
|
||||||
dev->bus->busnum, dev->devpath,
|
dev->bus->busnum, dev->devpath,
|
||||||
configuration,
|
configuration,
|
||||||
|
@@ -107,10 +107,19 @@ static int usb_probe_interface(struct device *dev)
|
|||||||
id = usb_match_id (intf, driver->id_table);
|
id = usb_match_id (intf, driver->id_table);
|
||||||
if (id) {
|
if (id) {
|
||||||
dev_dbg (dev, "%s - got id\n", __FUNCTION__);
|
dev_dbg (dev, "%s - got id\n", __FUNCTION__);
|
||||||
|
|
||||||
|
/* Interface "power state" doesn't correspond to any hardware
|
||||||
|
* state whatsoever. We use it to record when it's bound to
|
||||||
|
* a driver that may start I/0: it's not frozen/quiesced.
|
||||||
|
*/
|
||||||
|
mark_active(intf);
|
||||||
intf->condition = USB_INTERFACE_BINDING;
|
intf->condition = USB_INTERFACE_BINDING;
|
||||||
error = driver->probe (intf, id);
|
error = driver->probe (intf, id);
|
||||||
intf->condition = error ? USB_INTERFACE_UNBOUND :
|
if (error) {
|
||||||
USB_INTERFACE_BOUND;
|
mark_quiesced(intf);
|
||||||
|
intf->condition = USB_INTERFACE_UNBOUND;
|
||||||
|
} else
|
||||||
|
intf->condition = USB_INTERFACE_BOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
@@ -136,6 +145,7 @@ static int usb_unbind_interface(struct device *dev)
|
|||||||
0);
|
0);
|
||||||
usb_set_intfdata(intf, NULL);
|
usb_set_intfdata(intf, NULL);
|
||||||
intf->condition = USB_INTERFACE_UNBOUND;
|
intf->condition = USB_INTERFACE_UNBOUND;
|
||||||
|
mark_quiesced(intf);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -299,6 +309,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||||||
dev->driver = &driver->driver;
|
dev->driver = &driver->driver;
|
||||||
usb_set_intfdata(iface, priv);
|
usb_set_intfdata(iface, priv);
|
||||||
iface->condition = USB_INTERFACE_BOUND;
|
iface->condition = USB_INTERFACE_BOUND;
|
||||||
|
mark_active(iface);
|
||||||
|
|
||||||
/* if interface was already added, bind now; else let
|
/* if interface was already added, bind now; else let
|
||||||
* the future device_add() bind it, bypassing probe()
|
* the future device_add() bind it, bypassing probe()
|
||||||
@@ -345,6 +356,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
|
|||||||
dev->driver = NULL;
|
dev->driver = NULL;
|
||||||
usb_set_intfdata(iface, NULL);
|
usb_set_intfdata(iface, NULL);
|
||||||
iface->condition = USB_INTERFACE_UNBOUND;
|
iface->condition = USB_INTERFACE_UNBOUND;
|
||||||
|
mark_quiesced(iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1406,6 +1418,7 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message)
|
|||||||
{
|
{
|
||||||
struct usb_interface *intf;
|
struct usb_interface *intf;
|
||||||
struct usb_driver *driver;
|
struct usb_driver *driver;
|
||||||
|
int status;
|
||||||
|
|
||||||
if (dev->driver == &usb_generic_driver)
|
if (dev->driver == &usb_generic_driver)
|
||||||
return usb_suspend_device (to_usb_device(dev), message);
|
return usb_suspend_device (to_usb_device(dev), message);
|
||||||
@@ -1417,21 +1430,34 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message)
|
|||||||
intf = to_usb_interface(dev);
|
intf = to_usb_interface(dev);
|
||||||
driver = to_usb_driver(dev->driver);
|
driver = to_usb_driver(dev->driver);
|
||||||
|
|
||||||
/* there's only one USB suspend state */
|
/* with no hardware, USB interfaces only use FREEZE and ON states */
|
||||||
if (intf->dev.power.power_state.event)
|
if (!is_active(intf))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (driver->suspend)
|
if (driver->suspend && driver->resume) {
|
||||||
return driver->suspend(intf, message);
|
status = driver->suspend(intf, message);
|
||||||
return 0;
|
if (status)
|
||||||
|
dev_err(dev, "%s error %d\n", "suspend", status);
|
||||||
|
else
|
||||||
|
mark_quiesced(intf);
|
||||||
|
} else {
|
||||||
|
// FIXME else if there's no suspend method, disconnect...
|
||||||
|
dev_warn(dev, "no %s?\n", "suspend");
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usb_generic_resume(struct device *dev)
|
static int usb_generic_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_interface *intf;
|
struct usb_interface *intf;
|
||||||
struct usb_driver *driver;
|
struct usb_driver *driver;
|
||||||
|
int status;
|
||||||
|
|
||||||
/* devices resume through their hub */
|
if (dev->power.power_state.event == PM_EVENT_ON)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* devices resume through their hubs */
|
||||||
if (dev->driver == &usb_generic_driver)
|
if (dev->driver == &usb_generic_driver)
|
||||||
return usb_resume_device (to_usb_device(dev));
|
return usb_resume_device (to_usb_device(dev));
|
||||||
|
|
||||||
@@ -1442,8 +1468,19 @@ static int usb_generic_resume(struct device *dev)
|
|||||||
intf = to_usb_interface(dev);
|
intf = to_usb_interface(dev);
|
||||||
driver = to_usb_driver(dev->driver);
|
driver = to_usb_driver(dev->driver);
|
||||||
|
|
||||||
if (driver->resume)
|
/* if driver was suspended, it has a resume method;
|
||||||
return driver->resume(intf);
|
* however, sysfs can wrongly mark things as suspended
|
||||||
|
* (on the "no suspend method" FIXME path above)
|
||||||
|
*/
|
||||||
|
mark_active(intf);
|
||||||
|
if (driver->resume) {
|
||||||
|
status = driver->resume(intf);
|
||||||
|
if (status) {
|
||||||
|
dev_err(dev, "%s error %d\n", "resume", status);
|
||||||
|
mark_quiesced(intf);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
dev_warn(dev, "no %s?\n", "resume");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,6 +28,24 @@ extern void usb_major_cleanup(void);
|
|||||||
extern int usb_host_init(void);
|
extern int usb_host_init(void);
|
||||||
extern void usb_host_cleanup(void);
|
extern void usb_host_cleanup(void);
|
||||||
|
|
||||||
|
/* Interfaces and their "power state" are owned by usbcore */
|
||||||
|
|
||||||
|
static inline void mark_active(struct usb_interface *f)
|
||||||
|
{
|
||||||
|
f->dev.power.power_state.event = PM_EVENT_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mark_quiesced(struct usb_interface *f)
|
||||||
|
{
|
||||||
|
f->dev.power.power_state.event = PM_EVENT_FREEZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_active(struct usb_interface *f)
|
||||||
|
{
|
||||||
|
return f->dev.power.power_state.event == PM_EVENT_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* for labeling diagnostics */
|
/* for labeling diagnostics */
|
||||||
extern const char *usbcore_name;
|
extern const char *usbcore_name;
|
||||||
|
|
||||||
|
@@ -1887,7 +1887,6 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
|||||||
struct hid_device *hid = usb_get_intfdata (intf);
|
struct hid_device *hid = usb_get_intfdata (intf);
|
||||||
|
|
||||||
usb_kill_urb(hid->urbin);
|
usb_kill_urb(hid->urbin);
|
||||||
intf->dev.power.power_state = PMSG_SUSPEND;
|
|
||||||
dev_dbg(&intf->dev, "suspend\n");
|
dev_dbg(&intf->dev, "suspend\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1897,7 +1896,6 @@ static int hid_resume(struct usb_interface *intf)
|
|||||||
struct hid_device *hid = usb_get_intfdata (intf);
|
struct hid_device *hid = usb_get_intfdata (intf);
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
intf->dev.power.power_state = PMSG_ON;
|
|
||||||
if (hid->open)
|
if (hid->open)
|
||||||
status = usb_submit_urb(hid->urbin, GFP_NOIO);
|
status = usb_submit_urb(hid->urbin, GFP_NOIO);
|
||||||
else
|
else
|
||||||
|
@@ -1948,21 +1948,11 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id)
|
|||||||
|
|
||||||
static int usbtest_suspend (struct usb_interface *intf, pm_message_t message)
|
static int usbtest_suspend (struct usb_interface *intf, pm_message_t message)
|
||||||
{
|
{
|
||||||
struct usbtest_dev *dev = usb_get_intfdata (intf);
|
|
||||||
|
|
||||||
down (&dev->sem);
|
|
||||||
intf->dev.power.power_state = PMSG_SUSPEND;
|
|
||||||
up (&dev->sem);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usbtest_resume (struct usb_interface *intf)
|
static int usbtest_resume (struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
struct usbtest_dev *dev = usb_get_intfdata (intf);
|
|
||||||
|
|
||||||
down (&dev->sem);
|
|
||||||
intf->dev.power.power_state = PMSG_ON;
|
|
||||||
up (&dev->sem);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1384,7 +1384,6 @@ static int pegasus_suspend (struct usb_interface *intf, pm_message_t message)
|
|||||||
usb_kill_urb(pegasus->rx_urb);
|
usb_kill_urb(pegasus->rx_urb);
|
||||||
usb_kill_urb(pegasus->intr_urb);
|
usb_kill_urb(pegasus->intr_urb);
|
||||||
}
|
}
|
||||||
intf->dev.power.power_state = PMSG_SUSPEND;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1392,7 +1391,6 @@ static int pegasus_resume (struct usb_interface *intf)
|
|||||||
{
|
{
|
||||||
struct pegasus *pegasus = usb_get_intfdata(intf);
|
struct pegasus *pegasus = usb_get_intfdata(intf);
|
||||||
|
|
||||||
intf->dev.power.power_state = PMSG_ON;
|
|
||||||
netif_device_attach (pegasus->net);
|
netif_device_attach (pegasus->net);
|
||||||
if (netif_running(pegasus->net)) {
|
if (netif_running(pegasus->net)) {
|
||||||
pegasus->rx_urb->status = 0;
|
pegasus->rx_urb->status = 0;
|
||||||
|
@@ -1185,7 +1185,6 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
|
|||||||
netif_device_detach (dev->net);
|
netif_device_detach (dev->net);
|
||||||
(void) unlink_urbs (dev, &dev->rxq);
|
(void) unlink_urbs (dev, &dev->rxq);
|
||||||
(void) unlink_urbs (dev, &dev->txq);
|
(void) unlink_urbs (dev, &dev->txq);
|
||||||
intf->dev.power.power_state = PMSG_SUSPEND;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(usbnet_suspend);
|
EXPORT_SYMBOL_GPL(usbnet_suspend);
|
||||||
@@ -1194,7 +1193,6 @@ int usbnet_resume (struct usb_interface *intf)
|
|||||||
{
|
{
|
||||||
struct usbnet *dev = usb_get_intfdata(intf);
|
struct usbnet *dev = usb_get_intfdata(intf);
|
||||||
|
|
||||||
intf->dev.power.power_state = PMSG_ON;
|
|
||||||
netif_device_attach (dev->net);
|
netif_device_attach (dev->net);
|
||||||
tasklet_schedule (&dev->bh);
|
tasklet_schedule (&dev->bh);
|
||||||
return 0;
|
return 0;
|
||||||
|
Reference in New Issue
Block a user