Greg:
This patch (as718) includes a large cleanup of the suspend/resume code in
usbcore. The most notable change is the addition of a new field to the
usb_interface structure, for keeping track of whether the interface is
active or suspended. The intf->dev.power.power_state.event field can't be
used safely for this purpose because the PM core believes it owns the
value and can change it unilaterally.
Some duplicated code and unnecessary checks are removed and more code is
included under the "#ifdef CONFIG_PM" umbrella. The code that puts an
interface into altsetting 0 is moved so that it runs just before a driver
is bound (which is when it matters) instead of just after a driver is
unbound.
Alan Stern
Signed-off-by: Alan Stern <[EMAIL PROTECTED]>
---
Index: usb-2.6/include/linux/usb.h
===================================================================
--- usb-2.6.orig/include/linux/usb.h
+++ usb-2.6/include/linux/usb.h
@@ -101,6 +101,7 @@ enum usb_interface_condition {
* number from the USB core by calling usb_register_dev().
* @condition: binding state of the interface: not bound, binding
* (in probe()), bound to a driver, or unbinding (in disconnect())
+ * @is_active: flag set when the interface is bound and not suspended.
* @dev: driver model's view of this device
* @class_dev: driver model's class view of this device.
*
@@ -141,6 +142,8 @@ struct usb_interface {
int minor; /* minor number this interface is
* bound to */
enum usb_interface_condition condition; /* state of binding */
+ int is_active; /* the interface is not suspended */
+
struct device dev; /* interface specific device info */
struct class_device *class_dev;
};
Index: usb-2.6/drivers/usb/core/hub.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/hub.c
+++ usb-2.6/drivers/usb/core/hub.c
@@ -1042,7 +1042,7 @@ void usb_set_device_state(struct usb_dev
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM
/**
* usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
@@ -1470,6 +1470,7 @@ static void hub_port_logical_disconnect(
kick_khubd(hub);
}
+#ifdef CONFIG_PM
#ifdef CONFIG_USB_SUSPEND
@@ -1555,40 +1556,19 @@ static int __usb_port_suspend (struct us
if (port1 < 0)
return port1;
- if (udev->state == USB_STATE_SUSPENDED
- || udev->state == USB_STATE_NOTATTACHED) {
- return 0;
- }
-
- /* all interfaces must already be suspended */
- if (udev->actconfig) {
- int i;
-
- for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
- struct usb_interface *intf;
-
- intf = udev->actconfig->interface[i];
- if (is_active(intf)) {
- dev_dbg(&intf->dev, "nyet suspended\n");
- return -EBUSY;
- }
- }
- }
-
/* we change the device's upstream USB link,
* but root hubs have no upstream USB link.
*/
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;
}
-#endif
-
/*
* usb_port_suspend - suspend a usb device's upstream port
* @udev: device that's no longer in active use
@@ -1611,17 +1591,19 @@ static int __usb_port_suspend (struct us
*/
int usb_port_suspend(struct usb_device *udev)
{
-#ifdef CONFIG_USB_SUSPEND
- if (udev->state == USB_STATE_NOTATTACHED)
- 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;
+}
+
+#else /* CONFIG_USB_SUSPEND */
+
+/* When CONFIG_USB_SUSPEND isn't set, we never suspend any ports. */
+int usb_port_suspend(struct usb_device *udev)
+{
return 0;
-#endif
}
+#endif
+
/*
* If the USB "suspend" state is in use (rather than "global suspend"),
* many devices will be individually taken out of suspend state using
@@ -1646,7 +1628,6 @@ static int finish_port_resume(struct usb
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,
@@ -1740,6 +1721,15 @@ hub_port_resume(struct usb_hub *hub, int
return status;
}
+#else /* CONFIG_USB_SUSPEND */
+
+/* When CONFIG_USB_SUSPEND isn't set, we never resume any ports. */
+static inline int
+hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
+{
+ return 0;
+}
+
#endif
/*
@@ -1759,34 +1749,18 @@ int usb_port_resume(struct usb_device *u
{
int status;
- if (udev->state == USB_STATE_NOTATTACHED)
- return -ENODEV;
-
/* we change the device's upstream USB link,
* but root hubs have no upstream USB link.
*/
if (udev->parent) {
-#ifdef CONFIG_USB_SUSPEND
- if (udev->state == USB_STATE_SUSPENDED) {
- // NOTE swsusp may bork us, device state being wrong...
- // NOTE this fails if parent is also suspended...
- status = hub_port_resume(hdev_to_hub(udev->parent),
- udev->portnum, udev);
- } else
-#endif
- status = 0;
+ // NOTE this fails if parent is also suspended...
+ status = hub_port_resume(hdev_to_hub(udev->parent),
+ udev->portnum, udev);
} else
status = finish_port_resume(udev);
- if (status < 0)
- dev_dbg(&udev->dev, "can't resume, status %d\n",
- status);
- /* rebind drivers that had no suspend() */
- if (status == 0) {
- usb_unlock_device(udev);
- bus_rescan_devices(&usb_bus_type);
- usb_lock_device(udev);
- }
+ if (status < 0)
+ dev_dbg(&udev->dev, "can't resume, status %d\n", status);
return status;
}
@@ -1820,17 +1794,16 @@ static int hub_suspend(struct usb_interf
struct usb_device *hdev = hub->hdev;
unsigned port1;
- /* fail if children aren't already suspended */
+ /* all children must already be suspended or frozen */
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
struct usb_device *udev;
+ /* Regardless of whether CONFIG_USB_SUSPEND is set,
+ * the power_state member records the fictitious state
+ * we want to check here.
+ */
udev = hdev->children [port1-1];
- if (udev && (udev->dev.power.power_state.event
- == PM_EVENT_ON
-#ifdef CONFIG_USB_SUSPEND
- || udev->state != USB_STATE_SUSPENDED
-#endif
- )) {
+ if (udev && udev->dev.power.power_state.event == PM_EVENT_ON) {
dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
return -EBUSY;
}
@@ -1885,6 +1858,15 @@ static int hub_resume(struct usb_interfa
return 0;
}
+#else /* CONFIG_PM */
+
+static inline int remote_wakeup(struct usb_device *udev)
+{
+ return 0;
+}
+
+#endif
+
void usb_suspend_root_hub(struct usb_device *hdev)
{
struct usb_hub *hub = hdev_to_hub(hdev);
@@ -1894,7 +1876,7 @@ void usb_suspend_root_hub(struct usb_dev
* that the root hub status URB will be canceled.
*/
__hub_quiesce(hub);
- mark_quiesced(to_usb_interface(hub->intfdev));
+ to_usb_interface(hub->intfdev)->is_active = 0;
}
void usb_resume_root_hub(struct usb_device *hdev)
Index: usb-2.6/drivers/usb/core/driver.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/driver.c
+++ usb-2.6/drivers/usb/core/driver.c
@@ -182,42 +182,46 @@ static void generic_disconnect(struct us
usb_set_configuration(udev, 0);
usb_remove_sysfs_dev_files(udev);
-
- /* in case the call failed or the device was suspended */
- if (udev->state >= USB_STATE_CONFIGURED)
- usb_disable_device(udev, 0);
}
#ifdef CONFIG_PM
-static int verify_suspended(struct device *dev, void *unused)
-{
- return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
-}
-
static int generic_suspend(struct usb_interface *_udev, pm_message_t msg)
{
struct usb_device *udev = (struct usb_device *) _udev;
- int status;
- /* rule out bogus requests through sysfs */
- status = device_for_each_child(&udev->dev, NULL, verify_suspended);
- if (status)
- return status;
+ /* all interfaces must already be suspended */
+ if (udev->actconfig) {
+ int i;
+
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ struct usb_interface *intf =
+ udev->actconfig->interface[i];
+
+ if (intf->is_active) {
+ dev_dbg(&intf->dev, "nyet suspended\n");
+ return -EBUSY;
+ }
+ }
+ }
/* USB devices enter SUSPEND state through their hubs, but can be
* marked for FREEZE as soon as their children are already idled.
- * But those semantics are useless, so we equate the two (sigh).
*/
- return usb_port_suspend(udev);
+ if (msg.event == PM_EVENT_FREEZE)
+ return 0;
+ return usb_port_suspend(udev);
}
static int generic_resume(struct usb_interface *_udev)
{
struct usb_device *udev = (struct usb_device *) _udev;
- if (udev->state == USB_STATE_NOTATTACHED)
- return 0;
+ /* We can't resume if our 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;
+ }
return usb_port_resume(udev);
}
@@ -428,11 +432,19 @@ static int usb_probe_interface(struct de
* 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->is_active = 1;
intf->condition = USB_INTERFACE_BINDING;
+
+ /* Try to make sure the driver sees altsetting 0 */
+ if (intf->cur_altsetting->desc.bAlternateSetting != 0)
+ usb_set_interface(interface_to_usbdev(intf),
+ intf->altsetting[0].desc.
+ bInterfaceNumber,
+ 0);
+
error = driver->probe(intf, id);
if (error) {
- mark_quiesced(intf);
+ intf->is_active = 0;
intf->condition = USB_INTERFACE_UNBOUND;
} else
intf->condition = USB_INTERFACE_BOUND;
@@ -457,13 +469,9 @@ static int usb_unbind_interface(struct d
if (driver->disconnect)
driver->disconnect(intf);
- /* reset other interface state */
- usb_set_interface(interface_to_usbdev(intf),
- intf->altsetting[0].desc.bInterfaceNumber,
- 0);
usb_set_intfdata(intf, NULL);
intf->condition = USB_INTERFACE_UNBOUND;
- mark_quiesced(intf);
+ intf->is_active = 0;
return 0;
}
@@ -499,7 +507,7 @@ int usb_driver_claim_interface(struct us
dev->driver = &driver->driver;
usb_set_intfdata(iface, priv);
iface->condition = USB_INTERFACE_BOUND;
- mark_active(iface);
+ iface->is_active = 1;
/* if interface was already added, bind now; else let
* the future device_add() bind it, bypassing probe()
@@ -547,7 +555,7 @@ void usb_driver_release_interface(struct
dev->driver = NULL;
usb_set_intfdata(iface, NULL);
iface->condition = USB_INTERFACE_UNBOUND;
- mark_quiesced(iface);
+ iface->is_active = 0;
}
EXPORT_SYMBOL(usb_driver_release_interface);
@@ -930,14 +938,19 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister)
static int suspend_device(struct usb_device *udev, pm_message_t msg)
{
struct usb_driver *driver;
- int status;
+ int status = 0;
- if (udev->dev.power.power_state.event != PM_EVENT_ON
- || udev->dev.driver == NULL)
- return 0;
+ if (udev->state == USB_STATE_NOTATTACHED)
+ goto done;
+ if (udev->state == USB_STATE_SUSPENDED || udev->dev.driver == NULL)
+ goto done;
driver = to_usb_driver(udev->dev.driver);
status = driver->suspend((struct usb_interface *) udev, msg);
+
+done:
+ if (status == 0)
+ udev->dev.power.power_state.event = msg.event;
return status;
}
@@ -945,24 +958,19 @@ static int suspend_device(struct usb_dev
static int resume_device(struct usb_device *udev)
{
struct usb_driver *driver;
- int status;
-
- 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;
-
- if (udev->dev.driver == NULL) {
- udev->dev.power.power_state.event = PM_EVENT_FREEZE;
- return 0;
- }
+ int status = 0;
if (udev->state == USB_STATE_NOTATTACHED)
- return 0;
+ return -ENODEV;
+ if (udev->state != USB_STATE_SUSPENDED || udev->dev.driver == NULL)
+ goto done;
driver = to_usb_driver(udev->dev.driver);
status = driver->resume((struct usb_interface *) udev);
+
+done:
+ if (status == 0)
+ udev->dev.power.power_state.event = PM_EVENT_ON;
return status;
}
@@ -970,15 +978,12 @@ static int resume_device(struct usb_devi
static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
{
struct usb_driver *driver;
- int status;
-
- if (intf->dev.power.power_state.event != PM_EVENT_ON
- || intf->dev.driver == NULL)
- return 0;
+ int status = 0;
- /* with no hardware, USB interfaces only use FREEZE and ON states */
- if (!is_active(intf))
- return 0;
+ if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED)
+ goto done;
+ if (!intf->is_active || intf->dev.driver == NULL)
+ goto done;
driver = to_usb_driver(intf->dev.driver);
if (driver->suspend && driver->resume) {
@@ -987,14 +992,18 @@ static int suspend_interface(struct usb_
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
else
- mark_quiesced(intf);
+ intf->is_active = 0;
} else {
// FIXME else if there's no suspend method, disconnect...
dev_warn(&intf->dev, "no suspend for driver %s?\n",
driver->name);
- mark_quiesced(intf);
status = 0;
+ intf->is_active = 0;
}
+
+done:
+ if (status == 0)
+ intf->dev.power.power_state.event = msg.event;
return status;
}
@@ -1002,38 +1011,32 @@ static int suspend_interface(struct usb_
static int resume_interface(struct usb_interface *intf)
{
struct usb_driver *driver;
- int status;
-
- 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;
-
- if (intf->dev.driver == NULL) {
- intf->dev.power.power_state.event = PM_EVENT_FREEZE;
- return 0;
- }
+ int status = 0;
if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED)
- return 0;
+ return -ENODEV;
+ if (intf->is_active || intf->dev.driver == NULL)
+ 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)
- */
driver = to_usb_driver(intf->dev.driver);
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
+ intf->is_active = 1;
+ } else {
dev_warn(&intf->dev, "no resume for driver %s?\n",
driver->name);
- return 0;
+ status = 0;
+ intf->is_active = 1;
+ }
+
+done:
+ if (status == 0)
+ intf->dev.power.power_state.event = PM_EVENT_ON;
+ return status;
}
/* Caller has locked udev */
@@ -1070,8 +1073,11 @@ static int usb_core_resume(struct device
if (is_usb_device(dev))
status = resume_device(to_usb_device(dev));
- else
+ else {
status = resume_interface(to_usb_interface(dev));
+
+ /* Rebind drivers that had no suspend method? */
+ }
return status;
}
Index: usb-2.6/drivers/usb/core/usb.h
===================================================================
--- usb-2.6.orig/drivers/usb/core/usb.h
+++ usb-2.6/drivers/usb/core/usb.h
@@ -27,10 +27,20 @@ extern void usb_major_cleanup(void);
extern int usb_host_init(void);
extern void usb_host_cleanup(void);
+#ifdef CONFIG_PM
+
extern void usb_resume_both(struct usb_device *udev);
extern int usb_port_suspend(struct usb_device *dev);
extern int usb_port_resume(struct usb_device *dev);
+#else
+
+#define usb_resume_both(udev) do {} while (0)
+#define usb_port_suspend(dev) do {} while (0)
+#define usb_port_resume(dev) do {} while (0)
+
+#endif
+
extern struct bus_type usb_bus_type;
extern struct usb_driver usb_generic_driver;
@@ -43,24 +53,6 @@ static inline int is_usb_device(struct d
return dev->platform_data == &usb_generic_driver;
}
-/* 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 */
extern const char *usbcore_name;
Index: usb-2.6/drivers/usb/core/message.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/message.c
+++ usb-2.6/drivers/usb/core/message.c
@@ -1474,7 +1474,6 @@ free_interfaces:
intf->dev.dma_mask = dev->dev.dma_mask;
intf->dev.release = release_interface;
device_initialize (&intf->dev);
- mark_quiesced(intf);
sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration, alt->desc.bInterfaceNumber);
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel