mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 05:41:55 +00:00
[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: 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(-)
This commit is contained in:
parent
9293677af3
commit
979d5199fe
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user