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

[PATCH] export usb_set_device_state(), use in ohci

This patch is mostly cleanup, but it all helps make PM_SUSPEND_DISK
start to behave better.


This exports the new usb_set_device_state() routine for the virtual root
hubs, and uses it in OHCI during resume after power-off to replace some
HC-private code doing almost the same thing.

Note that all HCDs will likely need the same kind of suspend-to-disk
support (though it's different when BIOS kicks in).  Some systems
even power-off during suspend-to-ram (to save extra power), which is
why OHCI already has this logic!

Related updates:

 - Use usb_set_device_state() immediately when an HC dies, making khubd
   handle disconnect processing instead of a workqueue.  So now drivers
   won't self-deadlock in this should-be-rare path, when disconnect()
   calls flush_scheduled_work().

 - Don't warn about "Unlink after no-IRQ" for the the root hub's status
   URB ... like when suspending an HCD that never enumerated a device.

 - Minor IRQ handler cleanup, including more accurate tracking of whether
   this driver ever returned IRQ_HANDLED (shared IRQs don't count).
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 6cd8c6c6
......@@ -1263,7 +1263,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
* never get completion IRQs ... maybe even the ones we need to
* finish unlinking the initial failed usb_set_address().
*/
if (!hcd->saw_irq) {
if (!hcd->saw_irq && hcd->rh_timer.data != (unsigned long) urb) {
dev_warn (hcd->self.controller, "Unlink after no-IRQ? "
"Different ACPI or APIC settings may help."
"\n");
......@@ -1572,13 +1572,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
if (start == USB_STATE_HALT)
return IRQ_NONE;
hcd->saw_irq = 1;
if (hcd->driver->irq (hcd, r) == IRQ_NONE)
return IRQ_NONE;
hcd->saw_irq = 1;
if (hcd->state != start && hcd->state == USB_STATE_HALT)
usb_hc_died (hcd);
return IRQ_HANDLED;
......@@ -1587,22 +1586,6 @@ EXPORT_SYMBOL (usb_hcd_irq);
/*-------------------------------------------------------------------------*/
static void hcd_panic (void *_hcd)
{
struct usb_hcd *hcd = _hcd;
struct usb_device *hub = hcd->self.root_hub;
unsigned i;
/* hc's root hub is removed later removed in hcd->stop() */
down (&hub->serialize);
usb_set_device_state(hub, USB_STATE_NOTATTACHED);
for (i = 0; i < hub->maxchild; i++) {
if (hub->children [i])
usb_disconnect (&hub->children [i]);
}
up (&hub->serialize);
}
/**
* usb_hc_died - report abnormal shutdown of a host controller (bus glue)
* @hcd: pointer to the HCD representing the controller
......@@ -1615,9 +1598,9 @@ void usb_hc_died (struct usb_hcd *hcd)
{
dev_err (hcd->self.controller, "HC died; cleaning up\n");
/* clean up old urbs and devices; needs a task context */
INIT_WORK (&hcd->work, hcd_panic, hcd);
(void) schedule_work (&hcd->work);
/* make khubd clean up old urbs and devices */
usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED);
mod_timer(&hcd->rh_timer, jiffies);
}
EXPORT_SYMBOL (usb_hc_died);
......@@ -67,7 +67,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
struct timer_list rh_timer; /* drives root hub */
struct list_head dev_list; /* devices on this bus */
struct work_struct work;
/*
* hardware info/state
......@@ -363,6 +362,9 @@ static inline int hcd_register_root (struct usb_device *usb_dev,
return usb_register_root_hub (usb_dev, hcd->self.controller);
}
extern void usb_set_device_state(struct usb_device *udev,
enum usb_device_state new_state);
/*-------------------------------------------------------------------------*/
/* exported only within usbcore */
......
......@@ -910,7 +910,7 @@ static int locktree(struct usb_device *udev)
}
/**
* usb_set_device_state - change a device's current state (usbcore-internal)
* usb_set_device_state - change a device's current state (usbcore, hcds)
* @udev: pointer to device whose state should be changed
* @new_state: new state value to be stored
*
......@@ -941,6 +941,7 @@ void usb_set_device_state(struct usb_device *udev,
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
}
EXPORT_SYMBOL(usb_set_device_state);
static void choose_address(struct usb_device *udev)
......
......@@ -22,8 +22,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern void usb_set_device_state(struct usb_device *udev,
enum usb_device_state new_state);
/* for labeling diagnostics */
extern const char *usbcore_name;
......
......@@ -726,18 +726,6 @@ static void ohci_stop (struct usb_hcd *hcd)
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
static void mark_children_gone (struct usb_device *dev)
{
unsigned i;
for (i = 0; i < dev->maxchild; i++) {
if (dev->children [i] == 0)
continue;
dev->children [i]->state = USB_STATE_NOTATTACHED;
mark_children_gone (dev->children [i]);
}
}
static int hc_restart (struct ohci_hcd *ohci)
{
int temp;
......@@ -751,7 +739,7 @@ static int hc_restart (struct ohci_hcd *ohci)
*/
spin_lock_irq(&ohci->lock);
disable (ohci);
mark_children_gone (ohci->hcd.self.root_hub);
usb_set_device_state (ohci->hcd.self.root_hub, USB_STATE_NOTATTACHED);
if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
......
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