> s2-1107.patch
>  hcd.c |   42 ++++++++++++++++++++++++------------------
>  hub.c |   26 ++++++++++++++------------
>  usb.c |    8 ++++----
>  3 files changed, 42 insertions(+), 34 deletions(-)
Makes usbcore handle some suspend scenarios more consistently, especially
when talking with root hubs.

 - Use usb_device->state, not device->power.power_state.  (The driver model
   power_state is still needed for interfaces.)  With USB_SUSPEND=n, there
   are also cases where the HCD_STATE_SUSPENDED needs to be used instead
   of testing for USB_STATE_SUSPENDED.

 - Updates usb_device->state for root hubs during suspend/resume.

 - Fixes a locking bug (extra "up") that got merged recently, affecting
   CONFIG_USB_SUSPEND.

 - Recover one of the "HC died" cases better.

 - Root hub timer updates, handling various suspend transitions more
   consistently:  don't duplicate earlier submit checks, only work
   when hub is configured, address various submit/resume races.

Together, these help hubs work better regardless of whether USB_SUSPEND
has been enabled (that's the only way USB_STATE_SUSPENDED appears), and
whether or not the HCD is marked as suspended (or maybe Alan would
want to call that "half suspended").

Signed-off-by: David Brownell <[EMAIL PROTECTED]>


--- a/drivers/usb/core/hcd.c	2004-11-07 14:31:07 -08:00
+++ b/drivers/usb/core/hcd.c	2004-11-07 14:31:07 -08:00
@@ -479,6 +479,11 @@
 /*
  * Root Hub interrupt transfers are synthesized with a timer.
  * Completions are called in_interrupt() but not in_irq().
+ *
+ * Note: some root hubs (including common UHCI based designs) can't
+ * correctly issue port change IRQs.  They're the ones that _need_ a
+ * timer; most other root hubs don't.  Some systems could save a
+ * lot of battery power by eliminating these root hub timer IRQs.
  */
 
 static void rh_report_status (unsigned long ptr);
@@ -488,10 +493,7 @@
 	int	len = 1 + (urb->dev->maxchild / 8);
 
 	/* rh_timer protected by hcd_data_lock */
-	if (hcd->rh_timer.data
-			|| urb->status != -EINPROGRESS
-			|| urb->transfer_buffer_length < len
-			|| !HCD_IS_RUNNING (hcd->state)) {
+	if (hcd->rh_timer.data || urb->transfer_buffer_length < len) {
 		dev_dbg (hcd->self.controller,
 				"not queuing rh status urb, stat %d\n",
 				urb->status);
@@ -530,19 +532,19 @@
 		return;
 	}
 
-	if (!HCD_IS_SUSPENDED (hcd->state))
-		length = hcd->driver->hub_status_data (
-					hcd, urb->transfer_buffer);
-
 	/* complete the status urb, or retrigger the timer */
 	spin_lock (&hcd_data_lock);
-	if (length > 0) {
-		hcd->rh_timer.data = 0;
-		urb->actual_length = length;
-		urb->status = 0;
-		urb->hcpriv = NULL;
-	} else if (!urb->dev->dev.power.power_state)
-		mod_timer (&hcd->rh_timer, jiffies + HZ/4);
+	if (urb->dev->state == USB_STATE_CONFIGURED) {
+		length = hcd->driver->hub_status_data (
+					hcd, urb->transfer_buffer);
+		if (length > 0) {
+			hcd->rh_timer.data = 0;
+			urb->actual_length = length;
+			urb->status = 0;
+			urb->hcpriv = NULL;
+		} else
+			mod_timer (&hcd->rh_timer, jiffies + HZ/4);
+	}
 	spin_unlock (&hcd_data_lock);
 	spin_unlock (&urb->lock);
 
@@ -1103,13 +1105,17 @@
 	spin_lock_irqsave (&hcd_data_lock, flags);
 	if (unlikely (urb->reject))
 		status = -EPERM;
-	else if (HCD_IS_RUNNING (hcd->state) &&
-			hcd->state != USB_STATE_QUIESCING) {
+	else switch (hcd->state) {
+	case USB_STATE_RUNNING:
+	case USB_STATE_RESUMING:
 		usb_get_dev (urb->dev);
 		list_add_tail (&urb->urb_list, &dev->urb_list);
 		status = 0;
-	} else
+		break;
+	default:
 		status = -ESHUTDOWN;
+		break;
+	}
 	spin_unlock_irqrestore (&hcd_data_lock, flags);
 	if (status) {
 		INIT_LIST_HEAD (&urb->urb_list);
--- a/drivers/usb/core/hub.c	2004-11-07 14:31:07 -08:00
+++ b/drivers/usb/core/hub.c	2004-11-07 14:31:07 -08:00
@@ -1373,7 +1387,10 @@
 			status = hub_port_wait_reset(hdev, port, udev, delay);
 
 		/* return on disconnect or reset */
-		if (status == -ENOTCONN || status == 0) {
+		switch (status) {
+		case 0:
+		case -ENOTCONN:
+		case -ENODEV:
 			clear_port_feature(hdev,
 				port + 1, USB_PORT_FEAT_C_RESET);
 			/* FIXME need disconnect() for NOTATTACHED device */
@@ -1524,7 +1556,7 @@
 	if (port < 0)
 		return port;
 
-	if (udev->dev.power.power_state
+	if (udev->state == USB_STATE_SUSPENDED
 			|| udev->state == USB_STATE_NOTATTACHED) {
 		return 0;
 	}
@@ -1595,16 +1627,16 @@
 	 */
 	if (!udev->parent) {
 		struct usb_bus	*bus = udev->bus;
-		if (bus && bus->op->hub_suspend)
+		if (bus && bus->op->hub_suspend) {
 			status = bus->op->hub_suspend (bus);
-		else
+			if (status == 0)
+				usb_set_device_state(udev,
+						USB_STATE_SUSPENDED);
+		} else
 			status = -EOPNOTSUPP;
 	} else
 		status = hub_port_suspend(udev->parent, port);
 
-	if (status == 0)
-		udev->dev.power.power_state = PM_SUSPEND_MEM;
-	up(&udev->serialize);
 	return status;
 }
 EXPORT_SYMBOL(__usb_suspend_device);
@@ -1652,7 +1684,6 @@
 
 	/* caller owns the udev device lock */
 	dev_dbg(&udev->dev, "usb resume\n");
-	udev->dev.power.power_state = PM_SUSPEND_ON;
 
 	/* usb ch9 identifies four variants of SUSPENDED, based on what
 	 * state the device resumes to.  Linux currently won't see the
@@ -1809,14 +1840,15 @@
 	 */
 	if (!udev->parent) {
 		struct usb_bus	*bus = udev->bus;
-		if (bus && bus->op->hub_resume)
+		if (bus && bus->op->hub_resume) {
 			status = bus->op->hub_resume (bus);
-		else
+		} else
 			status = -EOPNOTSUPP;
 		if (status == 0) {
 			/* TRSMRCY = 10 msec */
 			msleep(10);
-			status = hub_resume (bus->root_hub
+			usb_set_device_state (udev, USB_STATE_CONFIGURED);
+			status = hub_resume (udev
 					->actconfig->interface[0]);
 		}
 	} else if (udev->state == USB_STATE_SUSPENDED) {
@@ -1824,7 +1856,6 @@
 		status = hub_port_resume(udev->parent, port);
 	} else {
 		status = 0;
-		udev->dev.power.power_state = PM_SUSPEND_ON;
 	}
 	if (status < 0) {
 		dev_dbg(&udev->dev, "can't resume, status %d\n",
--- a/drivers/usb/core/usb.c	2004-11-07 14:31:07 -08:00
+++ b/drivers/usb/core/usb.c	2004-11-07 14:31:07 -08:00
@@ -1395,10 +1395,6 @@
 	struct usb_interface *intf;
 	struct usb_driver *driver;
 
-	/* there's only one USB suspend state */
-	if (dev->power.power_state)
-		return 0;
-
 	if (dev->driver == &usb_generic_driver)
 		return usb_suspend_device (to_usb_device(dev), state);
 
@@ -1408,6 +1404,10 @@
 
 	intf = to_usb_interface(dev);
 	driver = to_usb_driver(dev->driver);
+
+	/* there's only one USB suspend state */
+	if (intf->dev.power.power_state)
+		return 0;
 
 	if (driver->suspend)
 		return driver->suspend(intf, state);

Reply via email to