patch-2.3.51 linux/drivers/usb/uhci.c

Next file: linux/drivers/usb/uhci.h
Previous file: linux/drivers/usb/serial/whiteheat.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.50/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
@@ -21,7 +21,6 @@
  *  - working around the horridness of the rest
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
@@ -34,15 +33,14 @@
 #include <linux/unistd.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
+#define DEBUG
+#include <linux/usb.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/system.h>
 
-#define DEBUG
-#include "usb.h"
-
 #include "uhci.h"
 #include "uhci-debug.h"
 
@@ -54,18 +52,21 @@
 
 static kmem_cache_t *uhci_td_cachep;
 static kmem_cache_t *uhci_qh_cachep;
+static kmem_cache_t *uhci_up_cachep;	/* urb_priv */
 
 static LIST_HEAD(uhci_list);
 
 static int rh_submit_urb(urb_t *urb);
 static int rh_unlink_urb(urb_t *urb);
 static int uhci_get_current_frame_number(struct usb_device *dev);
-static void uhci_stop_hc_schedule(struct uhci *uhci);
-static void uhci_start_hc_schedule(struct uhci *uhci);
+static int uhci_unlink_generic(urb_t *urb);
 static int uhci_unlink_urb(urb_t *urb);
 
 #define min(a,b) (((a)<(b))?(a):(b))
 
+/* If a transfer is still active after this much time, turn off FSBR */
+#define IDLE_TIMEOUT	(HZ / 20)	/* 50 ms */
+
 /*
  * Only the USB core should call uhci_alloc_dev and uhci_free_dev
  */
@@ -78,7 +79,7 @@
 {
 	urb_t *u;
 	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
-	struct list_head *tmp, *head = &uhci->urb_list;
+	struct list_head *tmp, *next, *head = &uhci->urb_list;
 	unsigned long flags;
 
 	/* Walk through the entire URB list and forcefully remove any */
@@ -88,38 +89,18 @@
 	while (tmp != head) {
 		u = list_entry(tmp, urb_t, urb_list);
 
+		next = tmp->next;
+
 		if (u->dev == dev)
 			uhci_unlink_urb(u);
+
+		tmp = next;
 	}
 	nested_unlock(&uhci->urblist_lock, flags);
 
 	return 0;
 }
 
-/*
- * UHCI interrupt list operations..
- */
-static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td)
-{
-	unsigned long flags;
-
-	nested_lock(&uhci->irqlist_lock, flags);
-	list_add(&td->irq_list, &uhci->interrupt_list);
-	nested_unlock(&uhci->irqlist_lock, flags);
-}
-
-static void uhci_remove_irq_list(struct uhci *uhci, struct uhci_td *td)
-{
-	unsigned long flags;
-
-	nested_lock(&uhci->irqlist_lock, flags);
-	if (td->irq_list.next != &td->irq_list) {
-		list_del(&td->irq_list);
-		INIT_LIST_HEAD(&td->irq_list);
-	}
-	nested_unlock(&uhci->irqlist_lock, flags);
-}
-
 static void uhci_add_urb_list(struct uhci *uhci, struct urb *urb)
 {
 	unsigned long flags;
@@ -141,6 +122,54 @@
 	nested_unlock(&uhci->urblist_lock, flags);
 }
 
+static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
+{
+	struct uhci_td *td;
+
+	td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
+	if (!td)
+		return NULL;
+
+	td->link = UHCI_PTR_TERM;
+	td->buffer = 0;
+
+	td->frameptr = NULL;
+	td->nexttd = td->prevtd = NULL;
+	td->list.next = td->list.prev = NULL;
+	td->dev = dev;
+
+	usb_inc_dev_use(dev);
+
+	return td;
+}
+
+static void inline uhci_fill_td(struct uhci_td *td, __u32 status,
+		__u32 info, __u32 buffer)
+{
+	td->status = status;
+	td->info = info;
+	td->buffer = buffer;
+}
+
+static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
+
+	/* Fix the linked list pointers */
+	td->nexttd = skeltd->nexttd;
+	td->prevtd = skeltd;
+	if (skeltd->nexttd)
+		skeltd->nexttd->prevtd = td;
+	skeltd->nexttd = td;
+
+	td->link = skeltd->link;
+	skeltd->link = virt_to_bus(td);
+
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+}
+
 /*
  * We insert Isochronous transfers directly into the frame list at the
  * beginning
@@ -157,6 +186,7 @@
 	framenum %= UHCI_NUMFRAMES;
 
 	spin_lock_irqsave(&uhci->framelist_lock, flags);
+
 	td->frameptr = &uhci->fl->frame[framenum];
 	td->link = uhci->fl->frame[framenum];
 	if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) {
@@ -166,6 +196,7 @@
 		nexttd->frameptr = NULL;
 	}
 	uhci->fl->frame[framenum] = virt_to_bus(td);
+
 	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
 
@@ -173,6 +204,10 @@
 {
 	unsigned long flags;
 
+	/* If it's not inserted, don't remove it */
+	if (!td->frameptr && !td->prevtd && !td->nexttd)
+		return;
+
 	spin_lock_irqsave(&uhci->framelist_lock, flags);
 	if (td->frameptr) {
 		*(td->frameptr) = td->link;
@@ -195,77 +230,41 @@
 	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
 
-static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->framelist_lock, flags);
-
-	/* Fix the linked list pointers */
-	td->nexttd = skeltd->nexttd;
-	td->prevtd = skeltd;
-	if (skeltd->nexttd)
-		skeltd->nexttd->prevtd = td;
-	skeltd->nexttd = td;
-
-	td->link = skeltd->link;
-	skeltd->link = virt_to_bus(td);
-
-	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
-}
-
 /*
  * Inserts a td into qh list at the top.
  */
-static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *begin)
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
 {
-	struct uhci_td *td, *prevtd;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td, *prevtd = NULL;
+
+	if (!urbp)
+		return;
 
-	if (!begin)		/* Nothing to do */
+	td = urbp->list.begin;
+	if (!td)
 		return;
 
-	/* Grab the first TD and add it to the QH */
-	td = begin;
-	qh->element = virt_to_bus(td) | UHCI_PTR_DEPTH;
+	/* Add the first TD to the QH element pointer */
+	qh->element = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
 
-	/* Go through the rest of the TD's, link them together */
 	prevtd = td;
-	td = td->next;
-	while (td) {
-		prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH;
+
+	/* Then link the rest of the TD's */
+	for (td = td->list.next; td; td = td->list.next) {
+		prevtd->link = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
 
 		prevtd = td;
-		td = td->next;
 	}
 
 	prevtd->link = UHCI_PTR_TERM;
 }
 
-static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
-{
-	struct uhci_td *td;
-
-	td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
-	if (!td)
-		return NULL;
-
-	td->link = UHCI_PTR_TERM;
-	td->buffer = 0;
-
-	td->frameptr = NULL;
-	td->nexttd = td->prevtd = NULL;
-	td->next = NULL;
-	td->dev = dev;
-	INIT_LIST_HEAD(&td->irq_list);
-	INIT_LIST_HEAD(&td->list);
-
-	usb_inc_dev_use(dev);
-
-	return td;
-}
-
 static void uhci_free_td(struct uhci_td *td)
 {
+	if (td->list.next || td->list.prev)
+		dbg("td is still in URB list!");
+
 	kmem_cache_free(uhci_td_cachep, td);
 
 	if (td->dev)
@@ -286,7 +285,7 @@
 	qh->dev = dev;
 	qh->prevqh = qh->nextqh = NULL;
 
-	INIT_LIST_HEAD(&qh->list);
+	INIT_LIST_HEAD(&qh->remove_list);
 
 	usb_inc_dev_use(dev);
 
@@ -333,14 +332,31 @@
 		qh->nextqh->prevqh = qh->prevqh;
 	qh->prevqh = qh->nextqh = NULL;
 	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+
+	spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+	list_add(&qh->remove_list, &uhci->qh_remove_list);
+	spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
 }
 
-static void inline uhci_fill_td(struct uhci_td *td, __u32 status,
-		__u32 info, __u32 buffer)
+struct urb_priv *uhci_alloc_urb_priv(struct urb *urb)
 {
-	td->status = status;
-	td->info = info;
-	td->buffer = buffer;
+	struct urb_priv *urbp;
+
+	urbp = kmem_cache_alloc(uhci_up_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
+	if (!urbp)
+		return NULL;
+
+	memset((void *)urbp, 0, sizeof(*urbp));
+
+	urbp->list.begin = urbp->list.end = NULL;
+
+	urb->hcpriv = urbp;
+
+	urbp->inserttime = jiffies;
+
+	usb_inc_dev_use(urb->dev);
+
+	return urbp;
 }
 
 static void uhci_add_td_to_urb(urb_t *urb, struct uhci_td *td)
@@ -349,35 +365,110 @@
 
 	td->urb = urb;
 
-	if (urbp->end)
-		urbp->end->next = td;
+	if (!urbp->list.begin)
+		urbp->list.begin = td;
+
+	if (urbp->list.end) {
+		urbp->list.end->list.next = td;
+		td->list.prev = urbp->list.end;
+	}
+	urbp->list.end = td;
+}
+
+static void uhci_remove_td_from_urb(urb_t *urb, struct uhci_td *td)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (!urbp->list.begin && !urbp->list.end)
+		return;
+
+	if (td->list.prev)
+		td->list.prev->list.next = td->list.next;
+	else
+		urbp->list.begin = td->list.next;
+
+	if (td->list.next)
+		td->list.next->list.prev = td->list.prev;
+	else
+		urbp->list.end = td->list.prev;
+
+	td->list.next = td->list.prev = NULL;
+	td->urb = NULL;
+}
+
+static void uhci_destroy_urb_priv(urb_t *urb)
+{
+	struct urb_priv *urbp;
+	struct uhci *uhci;
+	struct uhci_td *td, *nexttd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+	if (!urbp)
+		return;
+
+	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
+		return;
 
-	urbp->end = td;
+	uhci = urb->dev->bus->hcpriv;
+
+	td = urbp->list.begin;
+	while (td) {
+		nexttd = td->list.next;
+
+		uhci_remove_td_from_urb(urb, td);
+
+		uhci_remove_td(uhci, td);
+
+		uhci_free_td(td);
+		
+		td = nexttd;
+	}
+
+	urb->hcpriv = NULL;
+	kmem_cache_free(uhci_up_cachep, urbp);
 
-	if (!urbp->begin)
-		urbp->begin = td;
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	usb_dec_dev_use(urb->dev);
 }
 
-void uhci_inc_fsbr(struct uhci *uhci)
+static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
 {
 	unsigned long flags;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (!urbp)
+		return;
 
 	spin_lock_irqsave(&uhci->framelist_lock, flags);
 
-	if (!uhci->fsbr++)
-		uhci->skel_term_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
+	if (!urbp->fsbr) {
+		urbp->fsbr = 1;
+		if (!uhci->fsbr++)
+			uhci->skel_term_td.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
+	}
 
 	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
 
-void uhci_dec_fsbr(struct uhci *uhci)
+static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb)
 {
 	unsigned long flags;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (!urbp)
+		return;
 
 	spin_lock_irqsave(&uhci->framelist_lock, flags);
 
-	if (!--uhci->fsbr)
-		uhci->skel_term_qh.link = UHCI_PTR_TERM;
+	if (urbp->fsbr) {
+		urbp->fsbr = 0;
+		if (!--uhci->fsbr)
+			uhci->skel_term_td.link = UHCI_PTR_TERM;
+	}
 
 	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
@@ -419,14 +510,14 @@
  */
 static int uhci_submit_control(urb_t *urb)
 {
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
 	struct uhci_td *td;
 	struct uhci_qh *qh;
 	unsigned long destination, status;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
 	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
 	int len = urb->transfer_buffer_length;
 	unsigned char *data = urb->transfer_buffer;
-	struct urb_priv *urbp;
 
 	/* The "pipe" thing contains the destination in bits 8--18 */
 	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
@@ -434,14 +525,6 @@
 	/* 3 errors */
 	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27);
 
-	urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-	if (!urbp)
-		return -ENOMEM;
-
-	urbp->begin = urbp->end = NULL;
-
-	urb->hcpriv = urbp;
-
 	/*
 	 * Build the TD for the control request
 	 */
@@ -465,18 +548,16 @@
 	/*
 	 * Build the DATA TD's
 	 */
-	td = uhci_alloc_td(urb->dev);
-	if (!td) {
-		/* FIXME: Free the TD's */
-		return -ENOMEM;
-	}
-
 	while (len > 0) {
 		int pktsze = len;
 
 		if (pktsze > maxsze)
 			pktsze = maxsze;
 
+		td = uhci_alloc_td(urb->dev);
+		if (!td)
+			return -ENOMEM;
+
 		/* Alternate Data0/1 (start with Data1) */
 		destination ^= 1 << TD_TOKEN_TOGGLE;
 	
@@ -486,16 +567,16 @@
 
 		data += pktsze;
 		len -= pktsze;
-
-		td = uhci_alloc_td(urb->dev);
-		if (!td)
-			/* FIXME: Free all of the previously allocated td's */
-			return -ENOMEM;
 	}
 
 	/*
 	 * Build the final TD for control status 
-	 *
+	 */
+	td = uhci_alloc_td(urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	/*
 	 * It's IN if the pipe is an output pipe or we're not expecting
 	 * data back.
 	 */
@@ -513,20 +594,19 @@
 	uhci_fill_td(td, status | TD_CTRL_IOC,
 		destination | (UHCI_NULL_DATA_SIZE << 21), 0);
 
-	uhci_add_irq_list(uhci, td);
-
 	qh = uhci_alloc_qh(urb->dev);
-	if (!qh) {
-		/* FIXME: Free all of the TD's */
+	if (!qh)
 		return -ENOMEM;
-	}
-	uhci_insert_tds_in_qh(qh, urbp->begin);
 
-	if (!(urb->pipe & TD_CTRL_LS)) {
-		uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh);
-		uhci_inc_fsbr(uhci);
-	} else
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->pipe & TD_CTRL_LS) {
+		uhci_insert_tds_in_qh(qh, urb, 0);
 		uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, qh);
+	} else {
+		uhci_insert_tds_in_qh(qh, urb, 1);
+		uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh);
+		uhci_inc_fsbr(uhci, urb);
+	}
 
 	urbp->qh = qh;
 
@@ -537,66 +617,22 @@
 	return -EINPROGRESS;
 }
 
-/* This is also the uhci_unlink_bulk function */
-static int uhci_unlink_control(urb_t *urb)
+static int uhci_result_control(urb_t *urb)
 {
 	struct urb_priv *urbp = urb->hcpriv;
 	struct uhci_td *td;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	int notfinished;
+	unsigned int status;
 
 	if (!urbp)
 		return -EINVAL;
 
-	notfinished = (urb->status == -EINPROGRESS);
-
-	if (notfinished)
-		uhci_stop_hc_schedule(uhci);
-
-	if (!(urb->pipe & TD_CTRL_LS))
-		uhci_dec_fsbr(uhci);
-
-	uhci_remove_qh(uhci, urbp->qh);
-	uhci_free_qh(urbp->qh);
-
-	/* Go through the rest of the TD's, deleting them, then scheduling */
-	/*  their deletion */
-	td = urbp->begin;
-	while (td) {
-		struct uhci_td *next = td->next;
-
-		if (td->status & TD_CTRL_IOC)
-			uhci_remove_irq_list(uhci, td);
-
-		uhci_free_td(td);
-
-		td = next;
-	}
-
-	if (notfinished)
-		uhci_start_hc_schedule(uhci);
-
-	kfree(urbp);
-	urb->hcpriv = NULL;
-
-	uhci_remove_urb_list(uhci, urb);
-
-	return 0;
-}
-
-static int uhci_result_control(urb_t *urb)
-{
-	struct urb_priv *urbp = urb->hcpriv;
-	struct uhci_td *td;
-	unsigned int status;
-
-	td = urbp->begin;
-	if (!td)		/* Nothing to do */
+	td = urbp->list.begin;
+	if (!td)
 		return -EINVAL;
 
 	/* The first TD is the SETUP phase, check the status, but skip */
 	/*  the count */
-	status =  uhci_status_bits(td->status);
+	status = uhci_status_bits(td->status);
 	if (status & TD_CTRL_ACTIVE)
 		return -EINPROGRESS;
 
@@ -605,9 +641,10 @@
 
 	urb->actual_length = 0;
 
+	td = td->list.next;
+
 	/* The rest of the TD's (but the last) are data */
-	td = td->next;
-	while (td && td->next) {
+	while (td && td->list.next) {
 		status = uhci_status_bits(td->status);
 		if (status & TD_CTRL_ACTIVE)
 			return -EINPROGRESS;
@@ -617,13 +654,14 @@
 		/* If SPD is set then we received a short packet */
 		/*  There will be no status phase at the end */
 		/* FIXME: Re-setup the queue to run the STATUS phase? */
-		if (td->status & TD_CTRL_SPD && (uhci_actual_length(td->status) < uhci_expected_length(td->info)))
+		if ((td->status & TD_CTRL_SPD) &&
+		    (uhci_actual_length(td->status) < uhci_expected_length(td->info)))
 			return 0;
 
 		if (status)
 			goto td_error;
 
-		td = td->next;
+		td = td->list.next;
 	}
 
 	/* Control status phase */
@@ -648,18 +686,16 @@
 td_error:
 	/* Some debugging code */
 	if (debug) {
-		dbg("uhci_result_control() failed with status %x",
-			status);
+		dbg("uhci_result_control() failed with status %x", status);
 
 		/* Print the chain for debugging purposes */
 		uhci_show_queue(urbp->qh);
 	}
 
-	if (status & TD_CTRL_STALLED) {
+	if (status & TD_CTRL_STALLED)
 		/* endpoint has stalled - mark it halted */
 		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
 	    			uhci_packetout(td->info));
-	}
 
 	return uhci_map_status(status, uhci_packetout(td->info));
 }
@@ -672,7 +708,6 @@
 	struct uhci_td *td;
 	unsigned long destination, status;
 	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct urb_priv *urbp;
 
 	if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))
 		return -EINVAL;
@@ -680,16 +715,7 @@
 	/* The "pipe" thing contains the destination in bits 8--18 */
 	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
 
-	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD |
-			TD_CTRL_IOC;
-
-	urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-	if (!urbp)
-		return -ENOMEM;
-
-	urbp->begin = urbp->end = NULL;
-
-	urb->hcpriv = urbp;
+	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
 
 	td = uhci_alloc_td(urb->dev);
 	if (!td)
@@ -698,72 +724,70 @@
 	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
 	destination |= ((urb->transfer_buffer_length - 1) << 21);
 
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
 	uhci_add_td_to_urb(urb, td);
 	uhci_fill_td(td, status, destination,
 		virt_to_bus(urb->transfer_buffer));
 
-	uhci_add_irq_list(uhci, td);
-
 	uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td);
 
 	uhci_add_urb_list(uhci, urb);
 
-	usb_inc_dev_use(urb->dev);
-
 	return -EINPROGRESS;
 }
 
-static int uhci_unlink_interrupt(urb_t *urb)
+static int uhci_result_interrupt(urb_t *urb)
 {
 	struct urb_priv *urbp = urb->hcpriv;
 	struct uhci_td *td;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	int notfinished;
+	unsigned int status;
 
 	if (!urbp)
 		return -EINVAL;
 
-	notfinished = (urb->status == -EINPROGRESS);
+	urb->actual_length = 0;
 
-	if (notfinished)
-		uhci_stop_hc_schedule(uhci);
+	for (td = urbp->list.begin; td; td = td->list.next) {
+		status = uhci_status_bits(td->status);
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
 
-	td = urbp->begin;
-	uhci_remove_td(uhci, td);
-	if (td->status & TD_CTRL_IOC)
-		uhci_remove_irq_list(uhci, td);
-	uhci_free_td(td);
+		urb->actual_length += uhci_actual_length(td->status);
 
-	if (notfinished)
-		uhci_start_hc_schedule(uhci);
+		/* If SPD is set then we received a short packet */
+		if ((td->status & TD_CTRL_SPD) &&
+		    (uhci_actual_length(td->status) < uhci_expected_length(td->info))) {
+			usb_settoggle(urb->dev, uhci_endpoint(td->info),
+				uhci_packetout(td->info),
+				uhci_toggle(td->info) ^ 1);
 
-	kfree(urbp);
-	urb->hcpriv = NULL;
+			return 0;
+		}
 
-	uhci_remove_urb_list(uhci, urb);
+		if (status)
+			goto td_error;
+	}
 
 	return 0;
-}
-
-static int uhci_result_interrupt(urb_t *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td;
-	int status;
 
-	if (!urbp)
-		return -EINVAL;
-
-	td = urbp->begin;
-	if (!td)
-		return -EINVAL;
+td_error:
+	/* Some debugging code */
+	if (debug) {
+		dbg("uhci_result_interrupt/bulk() failed with status %x",
+			status);
 
-	status =  uhci_status_bits(td->status);
-	if (status & TD_CTRL_ACTIVE)
-		return -EINPROGRESS;
+		/* Print the chain for debugging purposes */
+		if (urbp->qh)
+			uhci_show_queue(urbp->qh);
+		else
+			uhci_show_td(td);
+	}
 
-	if (!status)
-		urb->actual_length = uhci_actual_length(td->status);
+	if (status & TD_CTRL_STALLED)
+		/* endpoint has stalled - mark it halted */
+		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
+	    			uhci_packetout(td->info));
 
 	return uhci_map_status(status, uhci_packetout(td->info));
 }
@@ -773,12 +797,17 @@
 	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 	struct uhci_td *td;
 
-	td = urbp->begin;
+	if (!urbp)
+		return;
+
+	td = urbp->list.begin;
+	if (!td)
+		return;
 
-	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 	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);
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 
 	urb->status = -EINPROGRESS;
 }
@@ -795,27 +824,23 @@
 	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
 	int len = urb->transfer_buffer_length;
 	unsigned char *data = urb->transfer_buffer;
-	struct urb_priv *urbp;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
 	if (len < 0)
 		return -EINVAL;
 
+	/* Can't have low speed bulk transfers */
+	if (urb->pipe & TD_CTRL_LS)
+		return -EINVAL;
+
 	/* The "pipe" thing contains the destination in bits 8--18 */
 	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
 
 	/* 3 errors */
-	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27);
+	status = TD_CTRL_ACTIVE | (3 << 27);
 	if (!(urb->transfer_flags & USB_DISABLE_SPD))
 		status |= TD_CTRL_SPD;
 
-	urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-	if (!urbp)
-		return -ENOMEM;
-
-	urbp->begin = urbp->end = NULL;
-
-	urb->hcpriv = urbp;
-
 	/*
 	 * Build the DATA TD's
 	 */
@@ -826,10 +851,8 @@
 			pktsze = maxsze;
 
 		td = uhci_alloc_td(urb->dev);
-		if (!td) {
-			/* FIXME: Free the TD's */
+		if (!td)
 			return -ENOMEM;
-		}
 
 		uhci_add_td_to_urb(urb, td);
 		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) |
@@ -840,86 +863,31 @@
 		data += pktsze;
 		len -= maxsze;
 
-		if (len <= 0) {
+		if (len <= 0)
 			td->status |= TD_CTRL_IOC;
-			uhci_add_irq_list(uhci, td);
-		}
 
 		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
 			usb_pipeout(urb->pipe));
 	}
 
 	qh = uhci_alloc_qh(urb->dev);
-	if (!qh) {
-		/* FIXME: Free all of the TD's */
+	if (!qh)
 		return -ENOMEM;
-	}
-	uhci_insert_tds_in_qh(qh, urbp->begin);
+
+	uhci_insert_tds_in_qh(qh, urb, 1);
 
 	uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);
 	urbp->qh = qh;
 
 	uhci_add_urb_list(uhci, urb);
 
-	usb_inc_dev_use(urb->dev);
-
-	uhci_inc_fsbr(uhci);
+	uhci_inc_fsbr(uhci, urb);
 
 	return -EINPROGRESS;
 }
 
-/* We can use the control unlink since they're identical */
-#define uhci_unlink_bulk uhci_unlink_control
-
-static int uhci_result_bulk(urb_t *urb)
-{
-	struct urb_priv *urbp = urb->hcpriv;
-	struct uhci_td *td;
-	unsigned int status;
-
-	urb->actual_length = 0;
-
-	/* The rest of the TD's (but the last) are data */
-	for (td = urbp->begin; td; td = td->next) {
-		status = uhci_status_bits(td->status);
-		if (status & TD_CTRL_ACTIVE)
-			return -EINPROGRESS;
-
-		urb->actual_length += uhci_actual_length(td->status);
-
-		/* If SPD is set then we received a short packet */
-		if (td->status & TD_CTRL_SPD && (uhci_actual_length(td->status) < uhci_expected_length(td->info))) {
-			usb_settoggle(urb->dev, uhci_endpoint(td->info),
-				uhci_packetout(td->info),
-				uhci_toggle(td->info) ^ 1);
-
-			return 0;
-		}
-
-		if (status)
-			goto td_error;
-	}
-
-	return 0;
-
-td_error:
-	/* Some debugging code */
-	if (debug) {
-		dbg("uhci_result_bulk() failed with status %x",
-			status);
-
-		/* Print the chain for debugging purposes */
-		uhci_show_queue(urbp->qh);
-	}
-
-	if (status & TD_CTRL_STALLED) {
-		/* endpoint has stalled - mark it halted */
-		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
-	    			uhci_packetout(td->info));
-	}
-
-	return uhci_map_status(status, uhci_packetout(td->info));
-}
+/* We can use the result interrupt since they're identical */
+#define uhci_result_bulk uhci_result_interrupt
 
 /*
  * Isochronous transfers
@@ -929,6 +897,7 @@
 	urb_t *u, *last_urb = NULL;
 	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
 	struct list_head *tmp, *head = &uhci->urb_list;
+	int ret = 0;
 	unsigned long flags;
 
 	nested_lock(&uhci->urblist_lock, flags);
@@ -945,14 +914,16 @@
 		}
 		tmp = tmp->next;
 	}
-	nested_unlock(&uhci->urblist_lock, flags);
 
 	if (last_urb) {
 		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
-		return 0;
+		ret = 0;
 	} else
-		return -1;	// no previous urb found
+		ret = -1;	/* no previous urb found */
+
+	nested_unlock(&uhci->urblist_lock, flags);
 
+	return ret;
 }
 
 static int isochronous_find_start(urb_t *urb)
@@ -985,7 +956,6 @@
 {
 	struct uhci_td *td;
 	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct urb_priv *urbp;
 	int i, ret, framenum;
 	int status, destination;
 
@@ -996,85 +966,30 @@
 	if (ret)
 		return ret;
 
-	urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-	if (!urbp)
-		return -ENOMEM;
-
-	urbp->begin = urbp->end = NULL;
-
-	urb->hcpriv = urbp;
-
 	framenum = urb->start_frame;
 	for (i = 0; i < urb->number_of_packets; i++, framenum++) {
 		if (!urb->iso_frame_desc[i].length)
 			continue;
 
 		td = uhci_alloc_td(urb->dev);
-		if (!td) {
-			/* FIXME: Free the TD's */
+		if (!td)
 			return -ENOMEM;
-		}
 
 		uhci_add_td_to_urb(urb, td);
 		uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21),
 			virt_to_bus(urb->transfer_buffer + urb->iso_frame_desc[i].offset));
 
-		if (i + 1 >= urb->number_of_packets) {
+		if (i + 1 >= urb->number_of_packets)
 			td->status |= TD_CTRL_IOC;
-			uhci_add_irq_list(uhci, td);
-		}
 
 		uhci_insert_td_frame_list(uhci, td, framenum);
 	}
 
 	uhci_add_urb_list(uhci, urb);
 
-	usb_inc_dev_use(urb->dev);
-
 	return -EINPROGRESS;
 }
 
-static int uhci_unlink_isochronous(urb_t *urb)
-{
-	struct urb_priv *urbp = urb->hcpriv;
-	struct uhci_td *td;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	int notfinished;
-
-	if (!urbp)
-		return -EINVAL;
-
-	notfinished = (urb->status == -EINPROGRESS);
-
-	if (notfinished)
-		uhci_stop_hc_schedule(uhci);
-
-	/* Go through the rest of the TD's, deleting them, then scheduling */
-	/*  their deletion */
-	td = urbp->begin;
-	while (td) {
-		struct uhci_td *next = td->next;
-
-		uhci_remove_td(uhci, td);
-
-		if (td->status & TD_CTRL_IOC)
-			uhci_remove_irq_list(uhci, td);
-		uhci_free_td(td);
-
-		td = next;
-	}
-
-	if (notfinished)
-		uhci_start_hc_schedule(uhci);
-
-	kfree(urbp);
-	urb->hcpriv = NULL;
-
-	uhci_remove_urb_list(uhci, urb);
-
-	return 0;
-}
-
 static int uhci_result_isochronous(urb_t *urb)
 {
 	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
@@ -1082,19 +997,17 @@
 	int status;
 	int i, ret = 0;
 
-	td = urbp->end;
-	if (!td)		/* Nothing to do */
+	if (!urbp)
 		return -EINVAL;
 
-	status = uhci_status_bits(td->status);
-	if (status & TD_CTRL_ACTIVE)
-		return -EINPROGRESS;
-
 	urb->actual_length = 0;
 
-	for (i = 0, td = urbp->begin; td; i++, td = td->next) {
+	for (i = 0, td = urbp->list.begin; td; i++, td = td->list.next) {
 		int actlength;
 
+		if (td->status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
 		actlength = uhci_actual_length(td->status);
 		urb->iso_frame_desc[i].actual_length = actlength;
 		urb->actual_length += actlength;
@@ -1114,17 +1027,26 @@
 {
 	int ret = -EINVAL;
 	struct uhci *uhci;
+	unsigned long flags;
 
 	if (!urb)
 		return -EINVAL;
 
-	if (!urb->dev || !urb->dev->bus)
+	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
 		return -ENODEV;
 
 	uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
+	/* Short circuit the virtual root hub */
 	if (usb_pipedevice(urb->pipe) == uhci->rh.devnum)
-		return rh_submit_urb(urb);	/* Virtual root hub */
+		return rh_submit_urb(urb);
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	if (!uhci_alloc_urb_priv(urb)) {
+		spin_unlock_irqrestore(&urb->lock, flags);
+		return -ENOMEM;
+	}
 
 	switch (usb_pipetype(urb->pipe)) {
 	case PIPE_CONTROL:
@@ -1142,20 +1064,30 @@
 	}
 
 	urb->status = ret;
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+
 	if (ret == -EINPROGRESS)
-		return 0;
+		ret = 0;
+	else
+		uhci_unlink_generic(urb);
 
 	return ret;
 }
 
 /*
  * Return the result of a transfer
+ *
+ * Must be called with urblist_lock acquired
  */
 static void uhci_transfer_result(urb_t *urb)
 {
 	urb_t *turb;
 	int proceed = 0, is_ring = 0;
 	int ret = -EINVAL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb->lock, flags);
 
 	switch (usb_pipetype(urb->pipe)) {
 	case PIPE_CONTROL:
@@ -1173,12 +1105,17 @@
 	}
 
 	urb->status = ret;
-	if (urb->status == -EINPROGRESS)
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	if (ret == -EINPROGRESS)
 		return;
 
 	switch (usb_pipetype(urb->pipe)) {
 	case PIPE_CONTROL:
-		uhci_unlink_control(urb);
+	case PIPE_BULK:
+	case PIPE_ISOCHRONOUS:
+		uhci_unlink_generic(urb);
 		break;
 	case PIPE_INTERRUPT:
 		/* Interrupts are an exception */
@@ -1186,14 +1123,8 @@
 		if (urb->interval)
 			uhci_reset_interrupt(urb);
 		else
-			uhci_unlink_interrupt(urb);
-		return;
-	case PIPE_BULK:
-		uhci_unlink_bulk(urb);
-		break;
-	case PIPE_ISOCHRONOUS:
-		uhci_unlink_isochronous(urb);
-		break;
+			uhci_unlink_generic(urb);
+		return;		/* <-- Note the return */
 	}
 
 	if (urb->next) {
@@ -1231,10 +1162,32 @@
 	}
 }
 
+static int uhci_unlink_generic(urb_t *urb)
+{
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	if (!urbp)
+		return -EINVAL;
+
+	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */
+
+	uhci_remove_urb_list(uhci, urb);
+
+	if (urbp->qh)
+		/* The interrupt loop will reclaim the QH's */
+		uhci_remove_qh(uhci, urbp->qh);
+
+	uhci_destroy_urb_priv(urb);
+
+	return 0;
+}
+
 static int uhci_unlink_urb(urb_t *urb)
 {
 	struct uhci *uhci;
 	int ret = 0;
+	unsigned long flags;
 
 	if (!urb)
 		return -EINVAL;
@@ -1244,40 +1197,34 @@
 
 	uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
+	/* Short circuit the virtual root hub */
 	if (usb_pipedevice(urb->pipe) == uhci->rh.devnum)
 		return rh_unlink_urb(urb);
 
 	if (urb->status == -EINPROGRESS) {
-		switch (usb_pipetype(urb->pipe)) {
-		case PIPE_CONTROL:
-			ret = uhci_unlink_control(urb);
-			break;
-		case PIPE_INTERRUPT:
-			ret = uhci_unlink_interrupt(urb);
-			break;
-		case PIPE_BULK:
-			ret = uhci_unlink_bulk(urb);
-			break;
-		case PIPE_ISOCHRONOUS:
-			ret = uhci_unlink_isochronous(urb);
-			break;
-		}
+		uhci_unlink_generic(urb);
 
-		if (urb->complete)
-			urb->complete(urb);
+		if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+			spin_lock_irqsave(&uhci->urb_remove_lock, flags);
+			list_add(&urb->urb_list, &uhci->urb_remove_list);
+			spin_unlock_irqrestore(&uhci->urb_remove_lock, flags);
+
+			urb->status = -ECONNABORTED;
+		} else {
+			if (in_interrupt()) {	/* wait at least 1 frame */
+				static int errorcount = 10;
+
+				if (errorcount--)
+					dbg("uhci_unlink_urb called from interrupt for urb %p", urb);
+				udelay(1000);
+			} else
+				schedule_timeout(1+1*HZ/1000); 
 
-#ifndef CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
-		if (in_interrupt()) {		/* wait at least 1 frame */
-			int errorcount = 10;
-
-			if (errorcount--)
-				dbg("uhci_unlink_urb called from interrupt for urb %p", urb);
-			udelay(1000);
-		} else
-			schedule_timeout(1+1*HZ/1000); 
-#endif
+			if (urb->complete)
+				urb->complete(urb);
 
-		urb->status = -ENOENT;
+			urb->status = -ENOENT;
+		}
 	}
 
 	return ret;
@@ -1412,9 +1359,12 @@
 
 static void rh_int_timer_do(unsigned long ptr)
 {
-	int len;
-	urb_t *urb = (urb_t *)ptr;
+	urb_t *urb = (urb_t *)ptr, *u;
 	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct list_head *tmp, *head = &uhci->urb_list;
+	struct urb_priv *urbp;
+	int len;
+	unsigned long flags;
 
 	if (uhci->rh.send) {
 		len = rh_send_irq(urb);
@@ -1425,6 +1375,21 @@
 		}
 	}
 
+	nested_lock(&uhci->urblist_lock, flags);
+	tmp = head->next;
+	while (tmp != head) {
+		u = list_entry(tmp, urb_t, urb_list);
+
+		urbp = (struct urb_priv *)u->hcpriv;
+		if (urbp) {
+			if (urbp->fsbr && time_after(jiffies, urbp->inserttime + IDLE_TIMEOUT))
+				uhci_dec_fsbr(uhci, u);
+		}
+
+		tmp = tmp->next;
+	}
+	nested_unlock(&uhci->urblist_lock, flags);
+
 	rh_init_int_timer(urb);
 }
 
@@ -1645,6 +1610,27 @@
 }
 /*-------------------------------------------------------------------*/
 
+void uhci_free_pending_qhs(struct uhci *uhci)
+{
+	struct list_head *tmp, *head;
+	unsigned long flags;
+
+	/* Free any pending QH's */
+	spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+	head = &uhci->qh_remove_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
+
+		tmp = tmp->next;
+
+		list_del(&qh->remove_list);
+
+		uhci_free_qh(qh);
+	}
+	spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
+}
+
 static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
 {
 	struct uhci *uhci = __uhci;
@@ -1652,7 +1638,6 @@
 	unsigned short status;
 	unsigned long flags;
 	struct list_head *tmp, *head;
-	urb_t *urb;
 
 	/*
 	 * Read the interrupt status, and write it back to clear the
@@ -1676,55 +1661,36 @@
 		}
 	}
 
-	/* Walk the list of pending TD's to see which ones completed.. */
-	nested_lock(&uhci->irqlist_lock, flags);
-	head = &uhci->interrupt_list;
+	uhci_free_pending_qhs(uhci);
+
+	spin_lock(&uhci->urb_remove_lock);
+	head = &uhci->urb_remove_list;
 	tmp = head->next;
 	while (tmp != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list);
-
-		urb = td->urb;
+		struct urb *urb = list_entry(tmp, struct urb, urb_list);
 
 		tmp = tmp->next;
 
-		/* Checks the status and does all of the magic necessary */
-		uhci_transfer_result(urb);
-	}
-	nested_unlock(&uhci->irqlist_lock, flags);
-}
-
-static void uhci_stop_hc_schedule(struct uhci *uhci)
-{
-#ifdef CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
-	unsigned int cmdreg, timeout = 1000;
-
-	cmdreg = inw(uhci->io_addr + USBCMD);
-	outw(cmdreg & ~USBCMD_RS, uhci->io_addr + USBCMD);
+		list_del(&urb->urb_list);
 
-	while (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) {
-		if (!--timeout) {
-			printk(KERN_ERR "uhci: stop_hc_schedule failed, HC still running\n");
-			break;
-		}
+		if (urb->complete)
+			urb->complete(urb);
 	}
-#endif
-}
+	spin_unlock(&uhci->urb_remove_lock);
 
-static void uhci_start_hc_schedule(struct uhci *uhci)
-{
-#ifdef CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
-	unsigned int cmdreg, timeout = 1000;
+	/* Walk the list of pending TD's to see which ones completed */
+	nested_lock(&uhci->urblist_lock, flags);
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *urb = list_entry(tmp, struct urb, urb_list);
 
-	cmdreg = inw(uhci->io_addr + USBCMD);
-	outw(cmdreg | USBCMD_RS, uhci->io_addr + USBCMD);
+		tmp = tmp->next;
 
-	while (inw(uhci->io_addr + USBSTS) & USBSTS_HCH) {
-		if (!--timeout) {
-			printk(KERN_ERR "uhci: start_hc_schedule failed, HC still halted\n");
-			break;
-		}
+		/* Checks the status and does all of the magic necessary */
+		uhci_transfer_result(urb);
 	}
-#endif
+	nested_unlock(&uhci->urblist_lock, flags);
 }
 
 static void reset_hc(struct uhci *uhci)
@@ -1780,7 +1746,7 @@
  *    of the queues. We don't do that here, because
  *    we'll create the actual TD entries on demand.
  *  - The first queue is the "interrupt queue".
- *  - The second queue is the "control queue".
+ *  - The second queue is the "control queue", split into low and high speed
  *  - The third queue is "bulk data".
  */
 static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size)
@@ -1799,14 +1765,16 @@
 	uhci->io_addr = io_addr;
 	uhci->io_size = io_size;
 
-	INIT_LIST_HEAD(&uhci->interrupt_list);
-	INIT_LIST_HEAD(&uhci->urb_list);
+	spin_lock_init(&uhci->qh_remove_lock);
+	INIT_LIST_HEAD(&uhci->qh_remove_list);
+
+	spin_lock_init(&uhci->urb_remove_lock);
+	INIT_LIST_HEAD(&uhci->urb_remove_list);
 
-	spin_lock_init(&uhci->framelist_lock);
 	nested_init(&uhci->urblist_lock);
-	nested_init(&uhci->irqlist_lock);
+	INIT_LIST_HEAD(&uhci->urb_list);
 
-	uhci->fsbr = 0;
+	spin_lock_init(&uhci->framelist_lock);
 
 	/* We need exactly one page (per UHCI specs), how convenient */
 	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
@@ -1870,8 +1838,12 @@
 	uhci->skel_bulk_qh.link = virt_to_bus(&uhci->skel_term_qh) | UHCI_PTR_QH;
 	uhci->skel_bulk_qh.element = UHCI_PTR_TERM;
 
+	/* This dummy TD is to work around a bug in Intel PIIX controllers */
+	uhci_fill_td(&uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
+	uhci->skel_term_td.link = UHCI_PTR_TERM;
+
 	uhci->skel_term_qh.link = UHCI_PTR_TERM;
-	uhci->skel_term_qh.element = UHCI_PTR_TERM;
+	uhci->skel_term_qh.element = virt_to_bus(&uhci->skel_term_td);
 
 	/*
 	 * Fill the frame list: make all entries point to
@@ -2022,23 +1994,24 @@
 			dev->resource[i].end - dev->resource[i].start + 1;
 
 		/* IO address? */
-		if (!(dev->resource[i].flags & 1))
+		if (!(dev->resource[i].flags & IORESOURCE_IO))
 			continue;
 
 		/* Is it already in use? */
 		if (check_region(io_addr, io_size))
 			break;
 
-		/* disable legacy emulation */
-		pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
-
-		pci_enable_device(dev);
-
 		if (!dev->irq) {
 			err("found UHCI device with no IRQ assigned. check BIOS settings!");
 			continue;
 		}
 
+		/* disable legacy emulation */
+		pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+		if (pci_enable_device(dev) < 0)
+			continue;
+
 		return setup_uhci(dev, dev->irq, io_addr, io_size);
 	}
 
@@ -2081,6 +2054,12 @@
 	if (!uhci_qh_cachep)
 		goto qh_failed;
 
+	uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
+		sizeof(struct urb_priv), 0, 0, NULL, NULL);
+
+	if (!uhci_up_cachep)
+		goto up_failed;
+
 	retval = -ENODEV;
 	dev = NULL;
 	for (;;) {
@@ -2105,6 +2084,10 @@
 	return 0;
 
 init_failed:
+	if (kmem_cache_destroy(uhci_up_cachep))
+		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
+up_failed:
 	if (kmem_cache_destroy(uhci_qh_cachep))
 		printk(KERN_INFO "uhci: not all QH's were freed\n");
 
@@ -2118,13 +2101,13 @@
 
 void uhci_cleanup(void)
 {
-	struct list_head *next, *tmp, *head = &uhci_list;
+	struct list_head *tmp, *head = &uhci_list;
 
 	tmp = head->next;
 	while (tmp != head) {
 		struct uhci *uhci = list_entry(tmp, struct uhci, uhci_list);
 
-		next = tmp->next;
+		tmp = tmp->next;
 
 		list_del(&uhci->uhci_list);
 		INIT_LIST_HEAD(&uhci->uhci_list);
@@ -2137,10 +2120,13 @@
 		reset_hc(uhci);
 		release_region(uhci->io_addr, uhci->io_size);
 
-		release_uhci(uhci);
+		uhci_free_pending_qhs(uhci);
 
-		tmp = next;
+		release_uhci(uhci);
 	}
+
+	if (kmem_cache_destroy(uhci_up_cachep))
+		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
 
 	if (kmem_cache_destroy(uhci_qh_cachep))
 		printk(KERN_INFO "uhci: not all QH's were freed\n");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)