These small updates help later patches, notably the OHCI patches
to let autosuspend work again.
This patch collects various small updates related to root hubs, to shrink
later patches which build on them.
- For root hub suspend/resume support:
* Make the existing usb_hcd_resume_root_hub() routine respect pmcore
locking, exporting and using the dpm_runtime_resume() method.
* Add a new usb_hcd_suspend_root_hub() to pair with that routine.
(Essential to make OHCI autosuspend behave again...)
* HC_SUSPENDED by itself only refers to the root hub's downstream ports.
So let HCDs see root hub URBs unless the parent device is suspended.
- Remove an assertion we no longer need (and now, also don't want).
- Generic suspend/resume updates to work better with swsusp.
* Ignore the FREEZE vs SUSPEND distinction for hardware; trying to
use it breaks the swsusp snapshots it's supposed to help (sigh).
* On resume, mark devices as resumed right away, but then
do nothing else if the device is marked NOTATTACHED.
These changes shouldn't be very noticable by themselves.
Signed-off-by: David Brownell <[EMAIL PROTECTED]>
--- g26.orig/drivers/usb/core/hcd.h 2005-09-22 22:12:32.000000000 -0700
+++ g26/drivers/usb/core/hcd.h 2005-09-22 22:15:14.000000000 -0700
@@ -355,6 +355,7 @@ extern long usb_calc_bus_time (int speed
extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
+extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd);
extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
extern void usb_set_device_state(struct usb_device *udev,
--- g26.orig/drivers/usb/core/hcd.c 2005-09-22 22:15:13.000000000 -0700
+++ g26/drivers/usb/core/hcd.c 2005-09-22 22:15:14.000000000 -0700
@@ -1142,10 +1142,20 @@ static int hcd_submit_urb (struct urb *u
else switch (hcd->state) {
case HC_STATE_RUNNING:
case HC_STATE_RESUMING:
+doit:
usb_get_dev (urb->dev);
list_add_tail (&urb->urb_list, &ep->urb_list);
status = 0;
break;
+ case HC_STATE_SUSPENDED:
+ /* HC upstream links (register access, wakeup signaling) can work
+ * even when the downstream links (and DMA etc) are quiesced; let
+ * usbcore talk to the root hub.
+ */
+ if (hcd->self.controller->power.power_state.event == PM_EVENT_ON
+ && urb->dev->parent == NULL)
+ goto doit;
+ /* FALL THROUGH */
default:
status = -ESHUTDOWN;
break;
@@ -1293,12 +1303,6 @@ static int hcd_unlink_urb (struct urb *u
goto done;
}
- /* running ~= hc unlink handshake works (irq, timer, etc)
- * halted ~= no unlink handshake is needed
- * suspended, resuming == should never happen
- */
- WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT);
-
/* insist the urb is still queued */
list_for_each(tmp, &ep->urb_list) {
if (tmp == &urb->urb_list)
@@ -1458,6 +1462,8 @@ static int hcd_hub_resume (struct usb_bu
hcd = container_of (bus, struct usb_hcd, self);
if (!hcd->driver->hub_resume)
return -ENOENT;
+ if (hcd->state == HC_STATE_RUNNING)
+ return 0;
hcd->state = HC_STATE_RESUMING;
status = hcd->driver->hub_resume (hcd);
if (status == 0)
@@ -1470,6 +1476,50 @@ static int hcd_hub_resume (struct usb_bu
return status;
}
+/*
+ * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports
+ * @hcd: host controller for this root hub
+ *
+ * This call arranges that usb_hcd_resume_root_hub() is safe to call later;
+ * that the HCD's root hub polling is deactivated; and that the root's hub
+ * driver is suspended. HCDs may call this to autosuspend when their root
+ * hub's downstream ports are all inactive: unpowered, disconnected,
+ * disabled, or suspended.
+ *
+ * The HCD will autoresume on device connect change detection (using SRP
+ * or a D+/D- pullup). The HCD also autoresumes on remote wakeup signaling
+ * from any ports that are suspended (if that is enabled). In most cases,
+ * overcurrent signaling (on powered ports) will also start autoresume.
+ *
+ * Always called with IRQs blocked.
+ */
+void usb_hcd_suspend_root_hub (struct usb_hcd *hcd)
+{
+ struct urb *urb;
+
+ spin_lock (&hcd_root_hub_lock);
+ usb_suspend_root_hub (hcd->self.root_hub);
+
+ /* force status urb to complete/unlink while suspended */
+ if (hcd->status_urb) {
+ urb = hcd->status_urb;
+ urb->status = -ECONNRESET;
+ urb->hcpriv = NULL;
+ urb->actual_length = 0;
+
+ del_timer (&hcd->rh_timer);
+ hcd->poll_pending = 0;
+ hcd->status_urb = NULL;
+ } else
+ urb = NULL;
+ spin_unlock (&hcd_root_hub_lock);
+ hcd->state = HC_STATE_SUSPENDED;
+
+ if (urb)
+ usb_hcd_giveback_urb (hcd, urb, NULL);
+}
+EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
+
/**
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
@@ -1477,7 +1527,7 @@ static int hcd_hub_resume (struct usb_bu
* The USB host controller calls this function when its root hub is
* suspended (with the remote wakeup feature enabled) and a remote
* wakeup request is received. It queues a request for khubd to
- * resume the root hub.
+ * resume the root hub (that is, manage its downstream ports again).
*/
void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
{
--- g26.orig/drivers/usb/core/usb.h 2005-09-22 22:14:39.000000000 -0700
+++ g26/drivers/usb/core/usb.h 2005-09-22 22:15:14.000000000 -0700
@@ -19,6 +19,7 @@ extern void usb_lock_all_devices(void);
extern void usb_unlock_all_devices(void);
extern void usb_kick_khubd(struct usb_device *dev);
+extern void usb_suspend_root_hub(struct usb_device *hdev);
extern void usb_resume_root_hub(struct usb_device *dev);
extern int usb_hub_init(void);
--- g26.orig/drivers/base/power/runtime.c 2005-09-22 22:12:32.000000000 -0700
+++ g26/drivers/base/power/runtime.c 2005-09-22 22:15:14.000000000 -0700
@@ -36,6 +36,7 @@ void dpm_runtime_resume(struct device *
runtime_resume(dev);
up(&dpm_sem);
}
+EXPORT_SYMBOL(dpm_runtime_resume);
/**
--- g26.orig/drivers/usb/core/hub.c 2005-09-22 22:15:05.000000000 -0700
+++ g26/drivers/usb/core/hub.c 2005-09-22 22:15:14.000000000 -0700
@@ -449,11 +449,18 @@ static void hub_power_on(struct usb_hub
msleep(max(pgood_delay, (unsigned) 100));
}
-static void hub_quiesce(struct usb_hub *hub)
+static inline void __hub_quiesce(struct usb_hub *hub)
{
- /* stop khubd and related activity */
+ /* (nonblocking) khubd and related activity won't re-trigger */
hub->quiescing = 1;
hub->activating = 0;
+ hub->resume_root_hub = 0;
+}
+
+static void hub_quiesce(struct usb_hub *hub)
+{
+ /* (blocking) stop khubd and related activity */
+ __hub_quiesce(hub);
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work(&hub->leds);
@@ -467,6 +474,7 @@ static void hub_activate(struct usb_hub
hub->quiescing = 0;
hub->activating = 1;
+ hub->resume_root_hub = 0;
status = usb_submit_urb(hub->urb, GFP_NOIO);
if (status < 0)
dev_err(hub->intfdev, "activate --> %d\n", status);
@@ -1959,6 +1967,18 @@ static int hub_resume(struct usb_interfa
return 0;
}
+void usb_suspend_root_hub(struct usb_device *hdev)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ /* This also makes any led blinker stop retriggering. We're called
+ * from irq, so the blinker might still be scheduled. Caller promises
+ * that the root hub status URB will be canceled.
+ */
+ __hub_quiesce(hub);
+ mark_quiesced(to_usb_interface(hub->intfdev));
+}
+
void usb_resume_root_hub(struct usb_device *hdev)
{
struct usb_hub *hub = hdev_to_hub(hdev);
@@ -2616,21 +2636,30 @@ static void hub_events(void)
intf = to_usb_interface(hub->intfdev);
hub_dev = &intf->dev;
- dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
+ i = hub->resume_root_hub;
+
+ dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x%s\n",
hdev->state, hub->descriptor
? hub->descriptor->bNbrPorts
: 0,
/* NOTE: expects max 15 ports... */
(u16) hub->change_bits[0],
- (u16) hub->event_bits[0]);
+ (u16) hub->event_bits[0],
+ i ? ", resume root" : "");
usb_get_intf(intf);
- i = hub->resume_root_hub;
spin_unlock_irq(&hub_event_lock);
- /* Is this is a root hub wanting to be resumed? */
- if (i)
- usb_resume_device(hdev);
+ /* Is this is a root hub wanting to reactivate the downstream
+ * ports? If so, be sure the interface resumes even if its
+ * stub "device" node was never suspended.
+ */
+ if (i) {
+ extern void dpm_runtime_resume(struct device *);
+
+ dpm_runtime_resume(&hdev->dev);
+ dpm_runtime_resume(&intf->dev);
+ }
/* Lock the device, then check to see if we were
* disconnected while waiting for the lock to succeed. */
--- g26.orig/drivers/usb/core/usb.c 2005-09-22 22:14:41.000000000 -0700
+++ g26/drivers/usb/core/usb.c 2005-09-22 22:15:14.000000000 -0700
@@ -1427,6 +1427,7 @@ static int usb_generic_suspend(struct de
/* 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).
*/
if (dev->driver == &usb_generic_driver) {
if (dev->power.power_state.event == message.event)
@@ -1435,10 +1436,6 @@ static int usb_generic_suspend(struct de
status = device_for_each_child(dev, NULL, verify_suspended);
if (status)
return status;
- if (message.event == PM_EVENT_FREEZE) {
- dev->power.power_state = message;
- return 0;
- }
return usb_suspend_device (to_usb_device(dev));
}
@@ -1471,14 +1468,22 @@ static int usb_generic_resume(struct dev
{
struct usb_interface *intf;
struct usb_driver *driver;
+ struct usb_device *udev;
int status;
if (dev->power.power_state.event == PM_EVENT_ON)
return 0;
+ /* mark things as "on" immediately, no matter what errors crop up */
+ dev->power.power_state.event = PM_EVENT_ON;
+
/* devices resume through their hubs */
- if (dev->driver == &usb_generic_driver)
+ if (dev->driver == &usb_generic_driver) {
+ udev = to_usb_device(dev);
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return 0;
return usb_resume_device (to_usb_device(dev));
+ }
if ((dev->driver == NULL) ||
(dev->driver_data == &usb_generic_driver_data))
@@ -1487,11 +1492,14 @@ static int usb_generic_resume(struct dev
intf = to_usb_interface(dev);
driver = to_usb_driver(dev->driver);
+ udev = interface_to_usbdev(intf);
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return 0;
+
/* 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)
*/
- mark_active(intf);
if (driver->resume) {
status = driver->resume(intf);
if (status) {