Commit 979d5199 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] root hub changes (lesser half)

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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>

 drivers/base/power/runtime.c |    1
 drivers/usb/core/hcd.c       |   64 ++++++++++++++++++++++++++++++++++++++-----
 drivers/usb/core/hcd.h       |    1
 drivers/usb/core/hub.c       |   45 ++++++++++++++++++++++++------
 drivers/usb/core/usb.c       |   20 +++++++++----
 drivers/usb/core/usb.h       |    1
 6 files changed, 111 insertions(+), 21 deletions(-)
parent 9293677a
......@@ -36,6 +36,7 @@ void dpm_runtime_resume(struct device * dev)
runtime_resume(dev);
up(&dpm_sem);
}
EXPORT_SYMBOL(dpm_runtime_resume);
/**
......
......@@ -1143,10 +1143,20 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
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;
......@@ -1294,12 +1304,6 @@ static int hcd_unlink_urb (struct urb *urb, int status)
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)
......@@ -1459,6 +1463,8 @@ static int hcd_hub_resume (struct usb_bus *bus)
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)
......@@ -1471,6 +1477,50 @@ static int hcd_hub_resume (struct usb_bus *bus)
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
......@@ -1478,7 +1528,7 @@ static int hcd_hub_resume (struct usb_bus *bus)
* 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)
{
......
......@@ -355,6 +355,7 @@ extern long usb_calc_bus_time (int speed, int is_input,
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,
......
......@@ -449,11 +449,18 @@ static void hub_power_on(struct usb_hub *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)
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_interface *intf)
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. */
......
......@@ -1427,6 +1427,7 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message)
/* 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 device *dev, pm_message_t message)
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 device *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 device *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) {
......
......@@ -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);
......
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