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) ...@@ -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 * never get completion IRQs ... maybe even the ones we need to
* finish unlinking the initial failed usb_set_address(). * 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? " dev_warn (hcd->self.controller, "Unlink after no-IRQ? "
"Different ACPI or APIC settings may help." "Different ACPI or APIC settings may help."
"\n"); "\n");
...@@ -1572,13 +1572,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) ...@@ -1572,13 +1572,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
struct usb_hcd *hcd = __hcd; struct usb_hcd *hcd = __hcd;
int start = hcd->state; int start = hcd->state;
if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ if (start == USB_STATE_HALT)
return IRQ_NONE; return IRQ_NONE;
hcd->saw_irq = 1;
if (hcd->driver->irq (hcd, r) == IRQ_NONE) if (hcd->driver->irq (hcd, r) == IRQ_NONE)
return IRQ_NONE; return IRQ_NONE;
hcd->saw_irq = 1;
if (hcd->state != start && hcd->state == USB_STATE_HALT) if (hcd->state != start && hcd->state == USB_STATE_HALT)
usb_hc_died (hcd); usb_hc_died (hcd);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -1587,22 +1586,6 @@ EXPORT_SYMBOL (usb_hcd_irq); ...@@ -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) * usb_hc_died - report abnormal shutdown of a host controller (bus glue)
* @hcd: pointer to the HCD representing the controller * @hcd: pointer to the HCD representing the controller
...@@ -1615,9 +1598,9 @@ void usb_hc_died (struct usb_hcd *hcd) ...@@ -1615,9 +1598,9 @@ void usb_hc_died (struct usb_hcd *hcd)
{ {
dev_err (hcd->self.controller, "HC died; cleaning up\n"); dev_err (hcd->self.controller, "HC died; cleaning up\n");
/* clean up old urbs and devices; needs a task context */ /* make khubd clean up old urbs and devices */
INIT_WORK (&hcd->work, hcd_panic, hcd); usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED);
(void) schedule_work (&hcd->work); mod_timer(&hcd->rh_timer, jiffies);
} }
EXPORT_SYMBOL (usb_hc_died); EXPORT_SYMBOL (usb_hc_died);
...@@ -67,7 +67,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ ...@@ -67,7 +67,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
struct timer_list rh_timer; /* drives root hub */ struct timer_list rh_timer; /* drives root hub */
struct list_head dev_list; /* devices on this bus */ struct list_head dev_list; /* devices on this bus */
struct work_struct work;
/* /*
* hardware info/state * hardware info/state
...@@ -363,6 +362,9 @@ static inline int hcd_register_root (struct usb_device *usb_dev, ...@@ -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); 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 */ /* exported only within usbcore */
......
...@@ -910,7 +910,7 @@ static int locktree(struct usb_device *udev) ...@@ -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 * @udev: pointer to device whose state should be changed
* @new_state: new state value to be stored * @new_state: new state value to be stored
* *
...@@ -941,6 +941,7 @@ void usb_set_device_state(struct usb_device *udev, ...@@ -941,6 +941,7 @@ void usb_set_device_state(struct usb_device *udev,
recursively_mark_NOTATTACHED(udev); recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags); spin_unlock_irqrestore(&device_state_lock, flags);
} }
EXPORT_SYMBOL(usb_set_device_state);
static void choose_address(struct usb_device *udev) static void choose_address(struct usb_device *udev)
......
...@@ -22,8 +22,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev, ...@@ -22,8 +22,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size); unsigned int size);
extern int usb_set_configuration(struct usb_device *dev, int configuration); 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 */ /* for labeling diagnostics */
extern const char *usbcore_name; extern const char *usbcore_name;
......
...@@ -726,18 +726,6 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -726,18 +726,6 @@ static void ohci_stop (struct usb_hcd *hcd)
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) #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) static int hc_restart (struct ohci_hcd *ohci)
{ {
int temp; int temp;
...@@ -751,7 +739,7 @@ static int hc_restart (struct ohci_hcd *ohci) ...@@ -751,7 +739,7 @@ static int hc_restart (struct ohci_hcd *ohci)
*/ */
spin_lock_irq(&ohci->lock); spin_lock_irq(&ohci->lock);
disable (ohci); 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)) if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n"); ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) { 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