ChangeSet 1.1276.1.70, 2003/09/02 11:09:09-07:00, david-b@pacbell.net

[PATCH] USB: usb hcd states

AFAICT this is ready for your next merge with Linus.

Ben's ohci stuff was not cooked yet, seemed like the
pm stuff wasn't yet supporting the hook(s) he needed.
RMK had similar issues w.r.t. PM on ARM too.


This patch includes:

   - updates from Benjamin Herrenschmidt to make usbcore
     behave a bit better during PM suspend (setting and
     checking hcd state).

   - related updates from me, making more paths into hcds
     fail when the driver is suspended.

   - updates based on some feedback from Alan Stern,
     notably including getting rid of a state we don't
     really need (most of the patch, by volume).

   - an experiment that tries to give a warning in the
     sadly common case of ACPI or APIC (etc) settings
     that need to change before USB works.

Net effect is that some of the PM issues start to get
resolved, maybe IRQ problems will be diagnosed quicker,
and some overdue cleanup gets started.


 drivers/usb/core/hcd-pci.c  |    7 +++++--
 drivers/usb/core/hcd.c      |   22 ++++++++++++++++++++++
 drivers/usb/core/hcd.h      |    3 +--
 drivers/usb/host/ehci-hcd.c |    5 ++---
 drivers/usb/host/ohci-hcd.c |    2 +-
 drivers/usb/host/ohci-pci.c |    2 +-
 drivers/usb/host/uhci-hcd.c |    4 ++--
 7 files changed, 34 insertions(+), 11 deletions(-)


diff -Nru a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
--- a/drivers/usb/core/hcd-pci.c	Tue Sep  2 12:42:10 2003
+++ b/drivers/usb/core/hcd-pci.c	Tue Sep  2 12:42:10 2003
@@ -139,6 +139,7 @@
 			return retval;
 		}
 	}
+	// hcd zeroed everything
 	hcd->regs = base;
 	hcd->region = region;
 
@@ -165,6 +166,7 @@
 		dev_err (hcd->controller, "can't reset\n");
 		goto clean_3;
 	}
+	hcd->state = USB_STATE_HALT;
 
 	pci_set_master (dev);
 #ifndef __sparc__
@@ -230,7 +232,8 @@
 		BUG ();
 
 	hub = hcd->self.root_hub;
-	hcd->state = USB_STATE_QUIESCING;
+	if (HCD_IS_RUNNING (hcd->state))
+		hcd->state = USB_STATE_QUIESCING;
 
 	dev_dbg (hcd->controller, "roothub graceful disconnect\n");
 	usb_disconnect (&hub);
@@ -287,8 +290,8 @@
 		pci_save_state (dev, hcd->pci_state);
 
 		/* driver may want to disable DMA etc */
+		hcd->state = USB_STATE_QUIESCING;
 		retval = hcd->driver->suspend (hcd, state);
-		hcd->state = USB_STATE_SUSPENDED;
 	}
 
  	pci_set_power_state (dev, state);
diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
--- a/drivers/usb/core/hcd.c	Tue Sep  2 12:42:10 2003
+++ b/drivers/usb/core/hcd.c	Tue Sep  2 12:42:10 2003
@@ -1099,6 +1099,8 @@
 static int hcd_get_frame_number (struct usb_device *udev)
 {
 	struct usb_hcd	*hcd = (struct usb_hcd *)udev->bus->hcpriv;
+	if (!HCD_IS_RUNNING (hcd->state))
+		return -ESHUTDOWN;
 	return hcd->driver->get_frame_number (hcd);
 }
 
@@ -1195,6 +1197,12 @@
 		goto done;
 	}
 
+	/* running ~= hc unlink handshake works (irq, timer, etc)
+	 * halted ~= no unlink handshake is needed
+	 * suspended, resuming == should never happen
+	 */
+	WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);
+
 	if (!urb->hcpriv) {
 		retval = -EINVAL;
 		goto done;
@@ -1210,6 +1218,17 @@
 		goto done;
 	}
 
+	/* PCI IRQ setup can easily be broken so that USB controllers
+	 * never get completion IRQs ... maybe even the ones we need to
+	 * finish unlinking the initial failed usb_set_address().
+	 */
+	if (!hcd->saw_irq) {
+		dev_warn (hcd->controller, "Unlink after no-IRQ?  "
+			"Different ACPI or APIC settings may help."
+			"\n");
+		hcd->saw_irq = 1;
+	}
+
 	/* maybe set up to block until the urb's completion fires.  the
 	 * lower level hcd code is always async, locking on urb->status
 	 * updates; an intercepted completion unblocks us.
@@ -1289,6 +1308,8 @@
 	dev = udev->hcpriv;
 	hcd = udev->bus->hcpriv;
 
+	WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);
+
 	local_irq_disable ();
 
 rescan:
@@ -1485,6 +1506,7 @@
 	if (unlikely (hcd->state == USB_STATE_HALT))	/* irq sharing? */
 		return IRQ_NONE;
 
+	hcd->saw_irq = 1;
 	hcd->driver->irq (hcd, r);
 	if (hcd->state != start && hcd->state == USB_STATE_HALT)
 		usb_hc_died (hcd);
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h	Tue Sep  2 12:42:10 2003
+++ b/drivers/usb/core/hcd.h	Tue Sep  2 12:42:10 2003
@@ -73,6 +73,7 @@
 	 * hardware info/state
 	 */
 	struct hc_driver	*driver;	/* hw-specific hooks */
+	unsigned		saw_irq : 1;
 	int			irq;		/* irq allocated */
 	void			*regs;		/* device memory/io */
 	struct device		*controller;	/* handle to hardware */
@@ -89,13 +90,11 @@
 
 	int			state;
 #	define	__ACTIVE		0x01
-#	define	__SLEEPY		0x02
 #	define	__SUSPEND		0x04
 #	define	__TRANSIENT		0x80
 
 #	define	USB_STATE_HALT		0
 #	define	USB_STATE_RUNNING	(__ACTIVE)
-#	define	USB_STATE_READY		(__ACTIVE|__SLEEPY)
 #	define	USB_STATE_QUIESCING	(__SUSPEND|__TRANSIENT|__ACTIVE)
 #	define	USB_STATE_RESUMING	(__SUSPEND|__TRANSIENT)
 #	define	USB_STATE_SUSPENDED	(__SUSPEND)
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Tue Sep  2 12:42:10 2003
+++ b/drivers/usb/host/ehci-hcd.c	Tue Sep  2 12:42:10 2003
@@ -231,7 +231,6 @@
 		ehci->hcd.state = USB_STATE_HALT;
 		return;
 	}
-	ehci->hcd.state = USB_STATE_READY;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -481,7 +480,7 @@
 	ehci->reboot_notifier.notifier_call = ehci_reboot;
 	register_reboot_notifier (&ehci->reboot_notifier);
 
-	ehci->hcd.state = USB_STATE_READY;
+	ehci->hcd.state = USB_STATE_RUNNING;
 	writel (FLAG_CF, &ehci->regs->configured_flag);
 	readl (&ehci->regs->command);	/* unblock posted write */
 
@@ -625,7 +624,7 @@
 	/* resume HC and each port */
 // restore pci FLADJ value
 	// khubd and drivers will set HC running, if needed;
-	hcd->state = USB_STATE_READY;
+	hcd->state = USB_STATE_RUNNING;
 	// FIXME Philips/Intel/... etc don't really have a "READY"
 	// state ... turn on CMD_RUN too
 	for (i = 0; i < ports; i++) {
diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c	Tue Sep  2 12:42:10 2003
+++ b/drivers/usb/host/ohci-hcd.c	Tue Sep  2 12:42:10 2003
@@ -529,7 +529,7 @@
 	/* connect the virtual root hub */
 	bus = hcd_to_bus (&ohci->hcd);
 	bus->root_hub = udev = usb_alloc_dev (NULL, bus);
-	ohci->hcd.state = USB_STATE_READY;
+	ohci->hcd.state = USB_STATE_RUNNING;
 	if (!udev) {
 		disable (ohci);
 		ohci->hc_control &= ~OHCI_CTRL_HCFS;
diff -Nru a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
--- a/drivers/usb/host/ohci-pci.c	Tue Sep  2 12:42:10 2003
+++ b/drivers/usb/host/ohci-pci.c	Tue Sep  2 12:42:10 2003
@@ -267,7 +267,7 @@
 			if (ohci->ed_bulktail)
 				ohci->hc_control |= OHCI_CTRL_BLE;
 		}
-		hcd->state = USB_STATE_READY;
+		hcd->state = USB_STATE_RUNNING;
 		writel (ohci->hc_control, &ohci->regs->control);
 
 		/* trigger a start-frame interrupt (why?) */
diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c	Tue Sep  2 12:42:10 2003
+++ b/drivers/usb/host/uhci-hcd.c	Tue Sep  2 12:42:10 2003
@@ -2099,7 +2099,7 @@
 	uhci->state_end = jiffies + HZ;
 	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
 
-        uhci->hcd.state = USB_STATE_READY;
+        uhci->hcd.state = USB_STATE_RUNNING;
 }
 
 /*
@@ -2452,7 +2452,7 @@
 		reset_hc(uhci);
 		start_hc(uhci);
 	}
-	uhci->hcd.state = USB_STATE_READY;
+	uhci->hcd.state = USB_STATE_RUNNING;
 	return 0;
 }
 #endif
