# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.598   -> 1.599  
#	  drivers/usb/uhci.h	1.9     -> 1.10   
#	  drivers/usb/uhci.c	1.29    -> 1.30   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/08/20	greg@kroah.com	1.599
# USB: uhci incorrect bit operations and FSBR timeout fixes.
# --------------------------------------------
#
diff -Nru a/drivers/usb/uhci.c b/drivers/usb/uhci.c
--- a/drivers/usb/uhci.c	Wed Aug 21 11:47:11 2002
+++ b/drivers/usb/uhci.c	Wed Aug 21 11:47:11 2002
@@ -100,6 +100,11 @@
 #define IDLE_TIMEOUT	(HZ / 20)	/* 50 ms */
 #define FSBR_DELAY	(HZ / 20)	/* 50 ms */
 
+/* When we timeout an idle transfer for FSBR, we'll switch it over to */
+/* depth first traversal. We'll do it in groups of this number of TD's */
+/* to make sure it doesn't hog all of the bandwidth */
+#define DEPTH_INTERVAL	5
+
 #define MAX_URB_LOOP	2048		/* Maximum number of linked URB's */
 
 /*
@@ -115,12 +120,20 @@
 	return 0;
 }
 
+/*
+ * Technically, updating td->status here is a race, but it's not really a
+ * problem. The worst that can happen is that we set the IOC bit again
+ * generating a spurios interrupt. We could fix this by creating another
+ * QH and leaving the IOC bit always set, but then we would have to play
+ * games with the FSBR code to make sure we get the correct order in all
+ * the cases. I don't think it's worth the effort
+ */
 static inline void uhci_set_next_interrupt(struct uhci *uhci)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	set_bit(TD_CTRL_IOC_BIT, &uhci->skel_term_td->status);
+	uhci->skel_term_td->status |= TD_CTRL_IOC;
 	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
@@ -129,7 +142,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	clear_bit(TD_CTRL_IOC_BIT, &uhci->skel_term_td->status);
+	uhci->skel_term_td->status &= ~TD_CTRL_IOC;
 	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
@@ -474,9 +487,9 @@
 		tmp = tmp->next;
 
 		if (toggle)
-			set_bit(TD_TOKEN_TOGGLE, &td->info);
+			td->info |= TD_TOKEN_TOGGLE;
 		else
-			clear_bit(TD_TOKEN_TOGGLE, &td->info);
+			td->info &= ~TD_TOKEN_TOGGLE;
 
 		toggle ^= 1;
 	}
@@ -857,7 +870,7 @@
 			return -ENOMEM;
 
 		/* Alternate Data0/1 (start with Data1) */
-		destination ^= 1 << TD_TOKEN_TOGGLE;
+		destination ^= TD_TOKEN_TOGGLE;
 	
 		uhci_add_td_to_urb(urb, td);
 		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),
@@ -884,7 +897,7 @@
 	else
 		destination |= USB_PID_OUT;
 
-	destination |= 1 << TD_TOKEN_TOGGLE;		/* End in Data1 */
+	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */
 
 	status &= ~TD_CTRL_SPD;
 
@@ -953,14 +966,6 @@
 
 		tmp = tmp->next;
 
-		if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) &&
-		    !(td->status & TD_CTRL_ACTIVE)) {
-			uhci_inc_fsbr(urb->dev->bus->hcpriv, urb);
-			urbp->fsbr_timeout = 0;
-			urbp->fsbrtime = jiffies;
-			clear_bit(TD_CTRL_IOC_BIT, &td->status);
-		}
-
 		status = uhci_status_bits(td->status);
 		if (status & TD_CTRL_ACTIVE)
 			return -EINPROGRESS;
@@ -1097,7 +1102,7 @@
 	if (!td)
 		return -ENOMEM;
 
-	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
+	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
 	destination |= ((urb->transfer_buffer_length - 1) << 21);
 
 	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
@@ -1127,14 +1132,6 @@
 
 		tmp = tmp->next;
 
-		if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) &&
-		    !(td->status & TD_CTRL_ACTIVE)) {
-			uhci_inc_fsbr(urb->dev->bus->hcpriv, urb);
-			urbp->fsbr_timeout = 0;
-			urbp->fsbrtime = jiffies;
-			clear_bit(TD_CTRL_IOC_BIT, &td->status);
-		}
-
 		status = uhci_status_bits(td->status);
 		if (status & TD_CTRL_ACTIVE)
 			return -EINPROGRESS;
@@ -1198,8 +1195,8 @@
 	td = list_entry(urbp->td_list.next, struct uhci_td, list);
 
 	td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
-	td->info &= ~(1 << TD_TOKEN_TOGGLE);
-	td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
+	td->info &= ~TD_TOKEN_TOGGLE;
+	td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
 	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 
 out:
@@ -1255,7 +1252,7 @@
 		uhci_fill_td(td, status, destination |
 			(((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) |
 			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
 			data);
 
 		data += pktsze;
@@ -1283,7 +1280,7 @@
 		uhci_fill_td(td, status, destination |
 			(UHCI_NULL_DATA_SIZE << 21) |
 			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
 			data);
 
 		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
@@ -1830,11 +1827,18 @@
 {
 	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 	struct list_head *head, *tmp;
+	int count = 0;
 
 	uhci_dec_fsbr(uhci, urb);
 
 	urbp->fsbr_timeout = 1;
 
+	/*
+	 * Ideally we would want to fix qh->element as well, but it's
+	 * read/write by the HC, so that can introduce a race. It's not
+	 * really worth the hassle
+	 */
+
 	head = &urbp->td_list;
 	tmp = head->next;
 	while (tmp != head) {
@@ -1842,10 +1846,15 @@
 
 		tmp = tmp->next;
 
-		if (td->status & TD_CTRL_ACTIVE) {
-			set_bit(TD_CTRL_IOC_BIT, &td->status);
-			break;
-		}
+		/*
+		 * Make sure we don't do the last one (since it'll have the
+		 * TERM bit set) as well as we skip every so many TD's to
+		 * make sure it doesn't hog the bandwidth
+		 */
+		if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
+			td->link |= UHCI_PTR_DEPTH;
+
+		count++;
 	}
 
 	return 0;
diff -Nru a/drivers/usb/uhci.h b/drivers/usb/uhci.h
--- a/drivers/usb/uhci.h	Wed Aug 21 11:47:11 2002
+++ b/drivers/usb/uhci.h	Wed Aug 21 11:47:11 2002
@@ -100,7 +100,6 @@
 #define TD_CTRL_C_ERR_SHIFT	27
 #define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
 #define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
-#define TD_CTRL_IOC_BIT		24
 #define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
 #define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
 #define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
@@ -120,13 +119,14 @@
 /*
  * for TD <info>: (a.k.a. Token)
  */
-#define TD_TOKEN_TOGGLE		19
+#define TD_TOKEN_TOGGLE_SHIFT	19
+#define TD_TOKEN_TOGGLE		(1 << 19)
 #define TD_TOKEN_PID_MASK	0xFF
 #define TD_TOKEN_EXPLEN_MASK	0x7FF		/* expected length, encoded as n - 1 */
 
 #define uhci_maxlen(token)	((token) >> 21)
 #define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */
-#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE) & 1)
+#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
 #define uhci_endpoint(token)	(((token) >> 15) & 0xf)
 #define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
 #define uhci_devep(token)	(((token) >> 8) & 0x7ff)
