Skip to content

Commit

Permalink
[PATCH] root hub changes (lesser half)
Browse files Browse the repository at this point in the history
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 <dbrownell@users.sourceforge.net>
Signed-off-by: Greg 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(-)
  • Loading branch information
David Brownell authored and Greg Kroah-Hartman committed Oct 28, 2005
1 parent 9293677 commit 979d519
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 21 deletions.
1 change: 1 addition & 0 deletions drivers/base/power/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void dpm_runtime_resume(struct device * dev)
runtime_resume(dev);
up(&dpm_sem);
}
EXPORT_SYMBOL(dpm_runtime_resume);


/**
Expand Down
64 changes: 57 additions & 7 deletions drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -1471,14 +1477,58 @@ 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
*
* 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)
{
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/core/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
45 changes: 37 additions & 8 deletions drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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. */
Expand Down
20 changes: 14 additions & 6 deletions drivers/usb/core/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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));
}

Expand Down Expand Up @@ -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))
Expand All @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/core/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 979d519

Please sign in to comment.