USB: EHCI: change deschedule logic for interrupt QHs

This patch (as1281) changes the way ehci-hcd deschedules interrupt
QHs, copying the approach used for async QHs.  The caller is no longer
responsible for rescheduling the QH if its queue is non-empty; instead
the reschedule is done directly by intr_deschedule(), after calling
qh_completions().  This is exactly the same as how end_unlink_async()
works.

ehci_urb_dequeue() and intr_deschedule() now correctly handle the case
where they are called while another interrupt URB for the same QH is
being given back.  This was a surprisingly large blind spot.  And
scan_periodic() now respects the new needs_rescan flag.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: David Brownell <david-b@pacbell.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index d7e85b6..4f89d7f 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -934,8 +934,9 @@
 			break;
 		switch (qh->qh_state) {
 		case QH_STATE_LINKED:
+		case QH_STATE_COMPLETING:
 			intr_deschedule (ehci, qh);
-			/* FALL THROUGH */
+			break;
 		case QH_STATE_IDLE:
 			qh_completions (ehci, qh);
 			break;
@@ -944,23 +945,6 @@
 					qh, qh->qh_state);
 			goto done;
 		}
-
-		/* reschedule QH iff another request is queued */
-		if (!list_empty (&qh->qtd_list)
-				&& HC_IS_RUNNING (hcd->state)) {
-			rc = qh_schedule(ehci, qh);
-
-			/* An error here likely indicates handshake failure
-			 * or no space left in the schedule.  Neither fault
-			 * should happen often ...
-			 *
-			 * FIXME kill the now-dysfunctional queued urbs
-			 */
-			if (rc != 0)
-				ehci_err(ehci,
-					"can't reschedule qh %p, err %d",
-					qh, rc);
-		}
 		break;
 
 	case PIPE_ISOCHRONOUS:
@@ -1079,12 +1063,10 @@
 			 * while the QH is active.  Unlink it now;
 			 * re-linking will call qh_refresh().
 			 */
-			if (eptype == USB_ENDPOINT_XFER_BULK) {
+			if (eptype == USB_ENDPOINT_XFER_BULK)
 				unlink_async(ehci, qh);
-			} else {
+			else
 				intr_deschedule(ehci, qh);
-				(void) qh_schedule(ehci, qh);
-			}
 		}
 	}
 	spin_unlock_irqrestore(&ehci->lock, flags);