[PATCH] UHCI: use one QH per endpoint, not per URB

This patch (as623) changes the uhci-hcd driver to make it use one QH per
device endpoint, instead of a QH per URB as it does now.  Numerous areas
of the code are affected by this.  For example, the distinction between
"queued" URBs and non-"queued" URBs no longer exists; all URBs belong to
a queue and some just happen to be at the queue's head.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index 5832953..3faccbd 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -90,13 +90,60 @@
 	return out - buf;
 }
 
+static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
+{
+	char *out = buf;
+	struct uhci_td *td;
+	int i, nactive, ninactive;
+
+	if (len < 200)
+		return 0;
+
+	out += sprintf(out, "urb_priv [%p] ", urbp);
+	out += sprintf(out, "urb [%p] ", urbp->urb);
+	out += sprintf(out, "qh [%p] ", urbp->qh);
+	out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
+	out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe),
+			(usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
+
+	switch (usb_pipetype(urbp->urb->pipe)) {
+	case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO"); break;
+	case PIPE_INTERRUPT: out += sprintf(out, "INT"); break;
+	case PIPE_BULK: out += sprintf(out, "BLK"); break;
+	case PIPE_CONTROL: out += sprintf(out, "CTL"); break;
+	}
+
+	out += sprintf(out, "%s", (urbp->fsbr ? " FSBR" : ""));
+	out += sprintf(out, "%s", (urbp->fsbr_timeout ? " FSBR_TO" : ""));
+
+	if (urbp->urb->status != -EINPROGRESS)
+		out += sprintf(out, " Status=%d", urbp->urb->status);
+	out += sprintf(out, "\n");
+
+	i = nactive = ninactive = 0;
+	list_for_each_entry(td, &urbp->td_list, list) {
+		if (++i <= 10 || debug > 2) {
+			out += sprintf(out, "%*s%d: ", space + 2, "", i);
+			out += uhci_show_td(td, out, len - (out - buf), 0);
+		} else {
+			if (td_status(td) & TD_CTRL_ACTIVE)
+				++nactive;
+			else
+				++ninactive;
+		}
+	}
+	if (nactive + ninactive > 0)
+		out += sprintf(out, "%*s[skipped %d inactive and %d active "
+				"TDs]\n",
+				space, "", ninactive, nactive);
+
+	return out - buf;
+}
+
 static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
 {
 	char *out = buf;
-	struct urb_priv *urbp;
-	struct list_head *head, *tmp;
-	struct uhci_td *td;
-	int i = 0, checked = 0, prevactive = 0;
+	int i, nurbs;
 	__le32 element = qh_element(qh);
 
 	/* Try to make sure there's enough memory */
@@ -118,86 +165,36 @@
 	if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
 		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");
 
-	if (!qh->urbp) {
-		out += sprintf(out, "%*s  urbp == NULL\n", space, "");
-		goto out;
-	}
+	if (list_empty(&qh->queue)) {
+		out += sprintf(out, "%*s  queue is empty\n", space, "");
+	} else {
+		struct urb_priv *urbp = list_entry(qh->queue.next,
+				struct urb_priv, node);
+		struct uhci_td *td = list_entry(urbp->td_list.next,
+				struct uhci_td, list);
 
-	urbp = qh->urbp;
-
-	head = &urbp->td_list;
-	tmp = head->next;
-
-	td = list_entry(tmp, struct uhci_td, list);
-
-	if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
-		out += sprintf(out, "%*s Element != First TD\n", space, "");
-
-	while (tmp != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		out += sprintf(out, "%*s%d: ", space + 2, "", i++);
-		out += uhci_show_td(td, out, len - (out - buf), 0);
-
-		if (i > 10 && !checked && prevactive && tmp != head &&
-		    debug <= 2) {
-			struct list_head *ntmp = tmp;
-			struct uhci_td *ntd = td;
-			int active = 1, ni = i;
-
-			checked = 1;
-
-			while (ntmp != head && ntmp->next != head && active) {
-				ntd = list_entry(ntmp, struct uhci_td, list);
-
-				ntmp = ntmp->next;
-
-				active = td_status(ntd) & TD_CTRL_ACTIVE;
-
-				ni++;
-			}
-
-			if (active && ni > i) {
-				out += sprintf(out, "%*s[skipped %d active TDs]\n", space, "", ni - i);
-				tmp = ntmp;
-				td = ntd;
-				i = ni;
-			}
+		if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
+			out += sprintf(out, "%*s Element != First TD\n",
+					space, "");
+		i = nurbs = 0;
+		list_for_each_entry(urbp, &qh->queue, node) {
+			if (++i <= 10)
+				out += uhci_show_urbp(urbp, out,
+						len - (out - buf), space + 2);
+			else
+				++nurbs;
 		}
-
-		prevactive = td_status(td) & TD_CTRL_ACTIVE;
+		if (nurbs > 0)
+			out += sprintf(out, "%*s Skipped %d URBs\n",
+					space, "", nurbs);
 	}
 
-	if (list_empty(&urbp->queue_list) || urbp->queued)
-		goto out;
-
-	out += sprintf(out, "%*sQueued QHs:\n", -space, "--");
-
-	head = &urbp->queue_list;
-	tmp = head->next;
-
-	while (tmp != head) {
-		struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
-						queue_list);
-		tmp = tmp->next;
-
-		out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
-	}
-
-out:
 	return out - buf;
 }
 
-#define show_frame_num()	\
-	if (!shown) {		\
-	  shown = 1;		\
-	  out += sprintf(out, "- Frame %d\n", i); \
-	}
-
 #ifdef CONFIG_PROC_FS
 static const char * const qh_names[] = {
+  "skel_unlink_qh", "skel_iso_qh",
   "skel_int128_qh", "skel_int64_qh",
   "skel_int32_qh", "skel_int16_qh",
   "skel_int8_qh", "skel_int4_qh",
@@ -206,12 +203,6 @@
   "skel_bulk_qh", "skel_term_qh"
 };
 
-#define show_qh_name()		\
-	if (!shown) {		\
-	  shown = 1;		\
-	  out += sprintf(out, "- %s\n", qh_names[i]); \
-	}
-
 static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
 {
 	char *out = buf;
@@ -321,139 +312,29 @@
 	return out - buf;
 }
 
-static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *buf, int len)
-{
-	struct list_head *tmp;
-	char *out = buf;
-	int count = 0;
-
-	if (len < 200)
-		return 0;
-
-	out += sprintf(out, "urb_priv [%p] ", urbp);
-	out += sprintf(out, "urb [%p] ", urbp->urb);
-	out += sprintf(out, "qh [%p] ", urbp->qh);
-	out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
-	out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
-
-	switch (usb_pipetype(urbp->urb->pipe)) {
-	case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO "); break;
-	case PIPE_INTERRUPT: out += sprintf(out, "INT "); break;
-	case PIPE_BULK: out += sprintf(out, "BLK "); break;
-	case PIPE_CONTROL: out += sprintf(out, "CTL "); break;
-	}
-
-	out += sprintf(out, "%s", (urbp->fsbr ? "FSBR " : ""));
-	out += sprintf(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : ""));
-
-	if (urbp->urb->status != -EINPROGRESS)
-		out += sprintf(out, "Status=%d ", urbp->urb->status);
-	//out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime);
-
-	count = 0;
-	list_for_each(tmp, &urbp->td_list)
-		count++;
-	out += sprintf(out, "TDs=%d ",count);
-
-	if (urbp->queued)
-		out += sprintf(out, "queued\n");
-	else {
-		count = 0;
-		list_for_each(tmp, &urbp->queue_list)
-			count++;
-		out += sprintf(out, "queued URBs=%d\n", count);
-	}
-
-	return out - buf;
-}
-
-static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
-{
-	char *out = buf;
-	struct list_head *head, *tmp;
-	int count;
-
-	out += sprintf(out, "Main list URBs:");
-	if (list_empty(&uhci->urb_list))
-		out += sprintf(out, " Empty\n");
-	else {
-		out += sprintf(out, "\n");
-		count = 0;
-		head = &uhci->urb_list;
-		tmp = head->next;
-		while (tmp != head) {
-			struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
-
-			out += sprintf(out, "  %d: ", ++count);
-			out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
-			tmp = tmp->next;
-		}
-	}
-
-	out += sprintf(out, "Remove list URBs:");
-	if (list_empty(&uhci->urb_remove_list))
-		out += sprintf(out, " Empty\n");
-	else {
-		out += sprintf(out, "\n");
-		count = 0;
-		head = &uhci->urb_remove_list;
-		tmp = head->next;
-		while (tmp != head) {
-			struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
-
-			out += sprintf(out, "  %d: ", ++count);
-			out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
-			tmp = tmp->next;
-		}
-	}
-
-	out += sprintf(out, "Complete list URBs:");
-	if (list_empty(&uhci->complete_list))
-		out += sprintf(out, " Empty\n");
-	else {
-		out += sprintf(out, "\n");
-		count = 0;
-		head = &uhci->complete_list;
-		tmp = head->next;
-		while (tmp != head) {
-			struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
-
-			out += sprintf(out, "  %d: ", ++count);
-			out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
-			tmp = tmp->next;
-		}
-	}
-
-	return out - buf;
-}
-
 static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
 {
-	unsigned long flags;
 	char *out = buf;
 	int i, j;
 	struct uhci_qh *qh;
 	struct uhci_td *td;
 	struct list_head *tmp, *head;
 
-	spin_lock_irqsave(&uhci->lock, flags);
-
 	out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
 	out += sprintf(out, "HC status\n");
 	out += uhci_show_status(uhci, out, len - (out - buf));
+	if (debug <= 1)
+		return out - buf;
 
 	out += sprintf(out, "Frame List\n");
 	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
-		int shown = 0;
 		td = uhci->frame_cpu[i];
 		if (!td)
 			continue;
 
-		if (td->dma_handle != (dma_addr_t)uhci->frame[i]) {
-			show_frame_num();
+		out += sprintf(out, "- Frame %d\n", i); \
+		if (td->dma_handle != (dma_addr_t)uhci->frame[i])
 			out += sprintf(out, "    frame list does not match td->dma_handle!\n");
-		}
-		show_frame_num();
 
 		head = &td->fl_list;
 		tmp = head;
@@ -467,14 +348,11 @@
 	out += sprintf(out, "Skeleton QHs\n");
 
 	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
-		int shown = 0;
+		int cnt = 0;
 
 		qh = uhci->skelqh[i];
-
-		if (debug > 1) {
-			show_qh_name();
-			out += uhci_show_qh(qh, out, len - (out - buf), 4);
-		}
+		out += sprintf(out, "- %s\n", qh_names[i]); \
+		out += uhci_show_qh(qh, out, len - (out - buf), 4);
 
 		/* Last QH is the Terminating QH, it's different */
 		if (i == UHCI_NUM_SKELQH - 1) {
@@ -487,44 +365,27 @@
 			continue;
 		}
 
-		j = (i < 7) ? 7 : i+1;		/* Next skeleton */
-		if (list_empty(&qh->list)) {
-			if (i < UHCI_NUM_SKELQH - 1) {
-				if (qh->link !=
-				    (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) {
-					show_qh_name();
-					out += sprintf(out, "    skeleton QH not linked to next skeleton QH!\n");
-				}
-			}
-
-			continue;
-		}
-
-		show_qh_name();
-
-		head = &qh->list;
+		j = (i < 9) ? 9 : i+1;		/* Next skeleton */
+		head = &qh->node;
 		tmp = head->next;
 
 		while (tmp != head) {
-			qh = list_entry(tmp, struct uhci_qh, list);
-
+			qh = list_entry(tmp, struct uhci_qh, node);
 			tmp = tmp->next;
-
-			out += uhci_show_qh(qh, out, len - (out - buf), 4);
+			if (++cnt <= 10)
+				out += uhci_show_qh(qh, out,
+						len - (out - buf), 4);
 		}
+		if ((cnt -= 10) > 0)
+			out += sprintf(out, "    Skipped %d QHs\n", cnt);
 
-		if (i < UHCI_NUM_SKELQH - 1) {
+		if (i > 1 && i < UHCI_NUM_SKELQH - 1) {
 			if (qh->link !=
 			    (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
 				out += sprintf(out, "    last QH not linked to next skeleton!\n");
 		}
 	}
 
-	if (debug > 2)
-		out += uhci_show_lists(uhci, out, len - (out - buf));
-
-	spin_unlock_irqrestore(&uhci->lock, flags);
-
 	return out - buf;
 }
 
@@ -541,6 +402,7 @@
 	struct uhci_hcd *uhci = inode->u.generic_ip;
 	struct uhci_debug *up;
 	int ret = -ENOMEM;
+	unsigned long flags;
 
 	lock_kernel();
 	up = kmalloc(sizeof(*up), GFP_KERNEL);
@@ -553,7 +415,9 @@
 		goto out;
 	}
 
+	spin_lock_irqsave(&uhci->lock, flags);
 	up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
+	spin_unlock_irqrestore(&uhci->lock, flags);
 
 	file->private_data = up;