I've updated my patch for the uhci.c driver. Here's a list of changes
since the last patch:

- Use David Brownell's pci_pool implementation now
- Use pci_map_single and pci_unmap_single for URB buffers
- Merge with ACPI suspend patch and fix a bug
- Some other proc fixes for debugging

This patch hasn't received tons of testing yet, but it atleast enumerates
devices all of the time for me.

I still have one nagging bug I'm still trying to track down which will
stall queued bulk transactions.

JE

diff -urN -X dontdiff linux-2.4.3-pre8.orig/drivers/usb/uhci-debug.h 
linux-2.4.3-pre8/drivers/usb/uhci-debug.h
--- linux-2.4.3-pre8.orig/drivers/usb/uhci-debug.h      Wed Feb 21 16:11:50 2001
+++ linux-2.4.3-pre8/drivers/usb/uhci-debug.h   Mon Mar 26 15:39:35 2001
@@ -6,20 +6,65 @@
  * visible pointers are surrounded in ()'s
  *
  * (C) Copyright 1999 Linus Torvalds
- * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999-2001 Johannes Erdfelt
  */
 
 #include <linux/kernel.h>
+#include <linux/proc_fs.h>
 #include <asm/io.h>
 
 #include "uhci.h"
 
-void uhci_show_td(struct uhci_td *td)
+/* Handle REALLY large printk's so we don't overflow buffers */
+static void inline lprintk(char *buf)
 {
+       char *p;
+
+       /* Just write one line at a time */
+       while (buf) {
+               p = strchr(buf, '\n');
+               if (p)
+                       *p = 0;
+               printk("%s", buf);
+               buf = p;
+               if (buf)
+                       buf++;
+       }
+}
+
+static int inline uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td)
+{
+       int i;
+
+       for (i = 0; i < UHCI_NUM_SKELTD; i++)
+               if (td == uhci->skeltd[i])
+                       return 1;
+
+       return 0;
+}
+
+static int inline uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
+{
+       int i;
+
+       for (i = 0; i < UHCI_NUM_SKELQH; i++)
+               if (qh == uhci->skelqh[i])
+                       return 1;
+
+       return 0;
+}
+
+static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
+{
+       char *out = buf;
        char *spid;
 
-       printk("%08x ", td->link);
-       printk("e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
+       /* Try to make sure there's enough memory */
+       if (len < 160)
+               return 0;
+
+       out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, td->link);
+       out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
                ((td->status >> 27) & 3),
                (td->status & TD_CTRL_SPD) ?      "SPD " : "",
                (td->status & TD_CTRL_LS) ?       "LS " : "",
@@ -48,19 +93,27 @@
                        break;
        }
 
-       printk("MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
+       out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
                td->info >> 21,
                ((td->info >> 19) & 1),
                (td->info >> 15) & 15,
                (td->info >> 8) & 127,
                (td->info & 0xff),
                spid);
-       printk("(buf=%08x)\n", td->buffer);
+       out += sprintf(out, "(buf=%08x)\n", td->buffer);
+
+       return out - buf;
 }
 
-static void uhci_show_sc(int port, unsigned short status)
+static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
 {
-       printk("  stat%d     =     %04x   %s%s%s%s%s%s%s%s\n",
+       char *out = buf;
+
+       /* Try to make sure there's enough memory */
+       if (len < 80)
+               return 0;
+
+       out += sprintf(out, "  stat%d     =     %04x   %s%s%s%s%s%s%s%s\n",
                port,
                status,
                (status & USBPORTSC_SUSP) ? "PortSuspend " : "",
@@ -71,16 +124,23 @@
                (status & USBPORTSC_PE) ?   "PortEnabled " : "",
                (status & USBPORTSC_CSC) ?  "ConnectChange " : "",
                (status & USBPORTSC_CCS) ?  "PortConnected " : "");
+
+       return out - buf;
 }
 
-void uhci_show_status(struct uhci *uhci)
+static int uhci_show_status(struct uhci *uhci, char *buf, int len)
 {
+       char *out = buf;
        unsigned int io_addr = uhci->io_addr;
        unsigned short usbcmd, usbstat, usbint, usbfrnum;
        unsigned int flbaseadd;
        unsigned char sof;
        unsigned short portsc1, portsc2;
 
+       /* Try to make sure there's enough memory */
+       if (len < 80 * 6)
+               return 0;
+
        usbcmd    = inw(io_addr + 0);
        usbstat   = inw(io_addr + 2);
        usbint    = inw(io_addr + 4);
@@ -90,7 +150,7 @@
        portsc1   = inw(io_addr + 16);
        portsc2   = inw(io_addr + 18);
 
-       printk("  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
+       out += sprintf(out, "  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
                usbcmd,
                (usbcmd & USBCMD_MAXP) ?    "Maxp64 " : "Maxp32 ",
                (usbcmd & USBCMD_CF) ?      "CF " : "",
@@ -101,7 +161,7 @@
                (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
                (usbcmd & USBCMD_RS) ?      "RS " : "");
 
-       printk("  usbstat   =     %04x   %s%s%s%s%s%s\n",
+       out += sprintf(out, "  usbstat   =     %04x   %s%s%s%s%s%s\n",
                usbstat,
                (usbstat & USBSTS_HCH) ?    "HCHalted " : "",
                (usbstat & USBSTS_HCPE) ?   "HostControllerProcessError " : "",
@@ -110,53 +170,80 @@
                (usbstat & USBSTS_ERROR) ?  "USBError " : "",
                (usbstat & USBSTS_USBINT) ? "USBINT " : "");
 
-       printk("  usbint    =     %04x\n", usbint);
-       printk("  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1,
+       out += sprintf(out, "  usbint    =     %04x\n", usbint);
+       out += sprintf(out, "  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1,
                0xfff & (4*(unsigned int)usbfrnum));
-       printk("  flbaseadd = %08x\n", flbaseadd);
-       printk("  sof       =       %02x\n", sof);
-       uhci_show_sc(1, portsc1);
-       uhci_show_sc(2, portsc2);
-}
-
-#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x))
+       out += sprintf(out, "  flbaseadd = %08x\n", flbaseadd);
+       out += sprintf(out, "  sof       =       %02x\n", sof);
+       out += uhci_show_sc(1, portsc1, out, len - (out - buf));
+       out += uhci_show_sc(2, portsc2, out, len - (out - buf));
 
-struct uhci_td *uhci_link_to_td(unsigned int link)
-{
-       if (link & UHCI_PTR_TERM)
-               return NULL;
-
-       return bus_to_virt(link & ~UHCI_PTR_BITS);
+       return out - buf;
 }
 
-void uhci_show_urb_queue(struct urb *urb)
+static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
 {
-       struct urb_priv *urbp = urb->hcpriv;
+       char *out = buf;
+       struct urb_priv *urbp;
        struct list_head *head, *tmp;
-       int i, checked = 0, prevactive = 0;
+       struct uhci_td *td;
+       int i = 0, checked = 0, prevactive = 0;
+
+       /* Try to make sure there's enough memory */
+       if (len < 80 * 6)
+               return 0;
 
-       printk("  URB [%p] urbp [%p]\n", urb, urbp);
+       out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
+                               qh, qh->link, qh->element);
+
+       if (qh->element & UHCI_PTR_QH)
+               out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");
 
-       if (urbp->qh)
-               printk("    QH [%p]\n", urbp->qh);
-       else
-               printk("    QH [%p] element (%08x) link (%08x)\n", urbp->qh,
-                       urbp->qh->element, urbp->qh->link);
+       if (qh->element & UHCI_PTR_DEPTH)
+               out += sprintf(out, "%*s  Depth traverse\n", space, "");
 
-       i = 0;
+       if (qh->element & 8)
+               out += sprintf(out, "%*s  Bit 3 set (bug?)\n", space, "");
 
-       head = &urbp->list;
+       if (!(qh->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;
+       }
+
+       urbp = qh->urbp;
+
+if (urbp) {
+out += sprintf(out, "Element history\n");
+       for (i = (urbp->element_entry + (sizeof(urbp->elements) / 
+sizeof(urbp->elements[0])) - 1) % (sizeof(urbp->elements) / 
+sizeof(urbp->elements[0])); i > 0; i--)
+               out += sprintf(out, "%08x %08x\n", urbp->elements[i], 
+urbp->elements_time[i]);
+
+       for (i = (sizeof(urbp->elements) / sizeof(urbp->elements[0])) - 1; i > 
+urbp->element_entry; i--)
+               if (urbp->elements[i])
+                       out += sprintf(out, "%08x %08x\n", urbp->elements[i], 
+urbp->elements_time[i]);
+}
+
+       head = &urbp->td_list;
        tmp = head->next;
+
+       td = list_entry(tmp, struct uhci_td, list);
+
+       if (td->dma_handle != (qh->element & ~UHCI_PTR_BITS))
+               out += sprintf(out, "%*s Element != First TD\n", space, "");
+
+i = 0;
        while (tmp != head) {
                struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
                tmp = tmp->next;
 
-               printk("      td %d: [%p]\n", i++, td);
-               printk("      ");
-               uhci_show_td(td);
+               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) {
+               if (i > 10 && !checked && prevactive && tmp != head &&
+                   debug <= 2) {
                        struct list_head *ntmp = tmp;
                        struct uhci_td *ntd = td;
                        int active = 1, ni = i;
@@ -174,7 +261,7 @@
                        }
 
                        if (active && ni > i) {
-                               printk("      [skipped %d active TD's]\n", ni - i);
+                               out += sprintf(out, "%*s[skipped %d active TD's]\n", 
+space, "", ni - i);
                                tmp = ntmp;
                                td = ntd;
                                i = ni;
@@ -183,134 +270,317 @@
 
                prevactive = td->status & TD_CTRL_ACTIVE;
        }
-}
 
-void uhci_show_queue(struct uhci_qh *qh)
-{
-       struct uhci_td *td, *first;
-       int i = 0, count = 1000;
+       if (list_empty(&urbp->queue_list) || urbp->queued)
+               goto out;
 
-       if (qh->element & UHCI_PTR_QH)
-               printk("      Element points to QH (bug?)\n");
+       out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
 
-       if (qh->element & UHCI_PTR_DEPTH)
-               printk("      Depth traverse\n");
+       head = &urbp->queue_list;
+       tmp = head->next;
 
-       if (qh->element & UHCI_PTR_TERM)
-               printk("      Terminate\n");
+       while (tmp != head) {
+               struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
+                                               queue_list);
+               tmp = tmp->next;
 
-       if (!(qh->element & ~UHCI_PTR_BITS)) {
-               printk("      td 0: [NULL]\n");
-               return;
+               out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
        }
 
-       first = uhci_link_to_td(qh->element);
+out:
+       return out - buf;
+}
+
+static const char *td_names[] = {"skel_int1_td", "skel_int2_td",
+                                "skel_int4_td", "skel_int8_td",
+                                "skel_int16_td", "skel_int32_td",
+                                "skel_int64_td", "skel_int128_td",
+                                "skel_int256_td", "skel_term_td" };
+static const char *qh_names[] = { "skel_ls_control_qh", "skel_hs_control_qh",
+                                 "skel_bulk_qh", "skel_term_qh" };
+
+#define show_frame_num()       \
+       if (!shown) {           \
+         shown = 1;            \
+         out += sprintf(out, "- Frame %d\n", i); \
+       }
 
-       /* Make sure it doesn't runaway */
-       for (td = first; td && count > 0; 
-            td = uhci_link_to_td(td->link), --count) {
-               printk("      td %d: [%p]\n", i++, td);
-               printk("      ");
-               uhci_show_td(td);
+#define show_td_name()         \
+       if (!shown) {           \
+         shown = 1;            \
+         out += sprintf(out, "- %s\n", td_names[i]);   \
+       }
 
-               if (td == uhci_link_to_td(td->link)) {
-                       printk(KERN_ERR "td links to itself!\n");
-                       break;
-               }
+#define show_qh_name()         \
+       if (!shown) {           \
+         shown = 1;            \
+         out += sprintf(out, "- %s\n", qh_names[i]);   \
        }
-}
 
-static int uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td)
+static int uhci_sprint_schedule(struct uhci *uhci, char *buf, int len)
 {
-       int j;
+       char *out = buf;
+       int i;
+       struct uhci_qh *qh;
+       struct uhci_td *td;
+       struct list_head *tmp, *head;
 
-       for (j = 0; j < UHCI_NUM_SKELTD; j++)
-               if (td == uhci->skeltd + j)
-                       return 1;
+       out += sprintf(out, "HC status\n");
+       out += uhci_show_status(uhci, out, len - (out - buf));
 
-       return 0;
-}
+       out += sprintf(out, "Frame List\n");
+       for (i = 0; i < UHCI_NUMFRAMES; ++i) {
+               int shown = 0;
 
-static int uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
-{
-       int j;
+               td = uhci->fl->frame_cpu[i];
+               if (!td)
+                       continue;
+
+               if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
+                       show_frame_num();
+                       out += sprintf(out, "    frame list does not match 
+td->dma_handle!\n");
+               }
 
-       for (j = 0; j < UHCI_NUM_SKELQH; j++)
-               if (qh == uhci->skelqh + j)
-                       return 1;
+               if (uhci_is_skeleton_td(uhci, td))
+                       continue;
 
-       return 0;
-}
+               show_frame_num();
 
-static const char *td_names[] = {"interrupt1", "interrupt2", "interrupt4",
-                                "interrupt8", "interrupt16", "interrupt32",
-                                "interrupt64", "interrupt128", "interrupt256" };
-static const char *qh_names[] = { "control", "bulk" };
+               head = &td->fl_list;
+               tmp = head;
 
-void uhci_show_queues(struct uhci *uhci)
-{
-       int i, isqh = 0;
-       struct uhci_qh *qh;
-       struct uhci_td *td;
+               do {
+                       td = list_entry(tmp, struct uhci_td, fl_list);
 
-       for (i = 0; i < UHCI_NUMFRAMES; ++i) {
+                       tmp = tmp->next;
+
+                       out += uhci_show_td(td, out, len - (out - buf), 4);
+               } while (tmp != head);
+       }
+
+       out += sprintf(out, "Skeleton TD's\n");
+       for (i = UHCI_NUM_SKELTD - 1; i >= 0; i--) {
                int shown = 0;
 
-               td = uhci_link_to_td(uhci->fl->frame[i]);
-               if (td)
-                       isqh = uhci->fl->frame[i] & UHCI_PTR_QH;
-               while (td && !isqh) {
-                       if (uhci_is_skeleton_td(uhci, td))
-                               break;
-
-                       if (!shown) {
-                               printk("   Frame %d\n", i);
-                               shown = 1;
+               td = uhci->skeltd[i];
+
+               if (debug > 1) {
+                       show_td_name();
+                       out += uhci_show_td(td, out, len - (out - buf), 4);
+               }
+
+               if (list_empty(&td->fl_list)) {
+                       /* TD 0 is the int1 TD and links to control_ls_qh */
+                       if (!i) {
+                               if (td->link !=
+                                   (uhci->skel_ls_control_qh->dma_handle | 
+UHCI_PTR_QH)) {
+                                       show_td_name();
+                                       out += sprintf(out, "    skeleton TD not 
+linked to ls_control QH!\n");
+                               }
+                       } else if (i < 9) {
+                               if (td->link != uhci->skeltd[i - 1]->dma_handle) {
+                                       show_td_name();
+                                       out += sprintf(out, "    skeleton TD not 
+linked to next skeleton TD!\n");
+                               }
+                       } else {
+                               show_td_name();
+
+                               if (td->link != td->dma_handle)
+                                       out += sprintf(out, "    skel_term_td does not 
+link to self\n");
+
+                               out += uhci_show_td(td, out, len - (out - buf), 4);
                        }
 
-                       printk("[%p] ", td);
+                       continue;
+               }
+
+               show_td_name();
+
+               head = &td->fl_list;
+               tmp = head->next;
+
+               while (tmp != head) {
+                       td = list_entry(tmp, struct uhci_td, fl_list);
 
-                       uhci_show_td(td);
-                       td = uhci_link_to_td(td->link);
-                       if (td)
-                               isqh = td->link & UHCI_PTR_QH;
-               }
-       }
-       for (i = 0; i < UHCI_NUM_SKELTD; ++i) {
-               printk("  %s: [%p] (%08x)\n", td_names[i],
-                       &uhci->skeltd[i],
-                       uhci->skeltd[i].link);
-
-               td = uhci_link_to_td(uhci->skeltd[i].link);
-               if (td)
-                       isqh = uhci->skeltd[i].link & UHCI_PTR_QH;
-               while (td && !isqh) {
-                       if (uhci_is_skeleton_td(uhci, td))
-                               break;
-
-                       printk("[%p] ", td);
-
-                       uhci_show_td(td);
-                       td = uhci_link_to_td(td->link);
-                       if (td)
-                               isqh = td->link & UHCI_PTR_QH;
+                       tmp = tmp->next;
+
+                       out += uhci_show_td(td, out, len - (out - buf), 4);
+               }
+
+               if (!i) {
+                       if (td->link !=
+                           (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH))
+                               out += sprintf(out, "    last TD not linked to 
+ls_control QH!\n");
+               } else if (i < 9) {
+                       if (td->link != uhci->skeltd[i - 1]->dma_handle)
+                               out += sprintf(out, "    last TD not linked to next 
+skeleton!\n");
                }
        }
+
+       out += sprintf(out, "Skeleton QH's\n");
        for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
-               printk("  %s: [%p] (%08x) (%08x)\n", qh_names[i],
-                       &uhci->skelqh[i],
-                       uhci->skelqh[i].link, uhci->skelqh[i].element);
-
-               qh = uhci_link_to_qh(uhci->skelqh[i].link);
-               for (; qh; qh = uhci_link_to_qh(qh->link)) {
-                       if (uhci_is_skeleton_qh(uhci, qh))
-                               break;
+               int shown = 0;
 
-                       printk("    [%p] (%08x) (%08x)\n",
-                               qh, qh->link, qh->element);
+               qh = uhci->skelqh[i];
+
+               if (debug > 1) {
+                       show_qh_name();
+                       out += uhci_show_qh(qh, out, len - (out - buf), 4);
+               }
+
+               /* QH 3 is the Terminating QH, it's different */
+               if (i == 3) {
+                       if (qh->link != UHCI_PTR_TERM) {
+                               show_qh_name();
+                               out += sprintf(out, "    bandwidth reclamation on!\n");
+                       }
+
+                       if (qh->element != uhci->skel_term_td->dma_handle) {
+                               show_qh_name();
+                               out += sprintf(out, "    skel_term_qh element is not 
+set to skel_term_td\n");
+                       }
+               }
+
+               if (list_empty(&qh->list)) {
+                       if (i < 3) {
+                               if (qh->link !=
+                                   (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH)) {
+                                       show_qh_name();
+                                       out += sprintf(out, "    skeleton QH not 
+linked to next skeleton QH!\n");
+                               }
+                       }
+
+                       continue;
+               }
 
-                       uhci_show_queue(qh);
+               show_qh_name();
+
+               head = &qh->list;
+               tmp = head->next;
+
+               while (tmp != head) {
+                       qh = list_entry(tmp, struct uhci_qh, list);
+
+                       tmp = tmp->next;
+
+                       out += uhci_show_qh(qh, out, len - (out - buf), 4);
                }
+
+               if (i < 3) {
+                       if (qh->link !=
+                           (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH))
+                               out += sprintf(out, "    last QH not linked to next 
+skeleton!\n");
+               }
+       }
+
+       return out - buf;
+}
+
+#ifdef CONFIG_PROC_FS
+#define MAX_OUTPUT     (PAGE_SIZE * 8)
+
+static struct proc_dir_entry *uhci_proc_root = NULL;
+
+struct uhci_proc {
+       int size;
+       char *data;
+       struct uhci *uhci;
+};
+
+static int uhci_proc_open(struct inode *inode, struct file *file)
+{
+       const struct proc_dir_entry *dp = inode->u.generic_ip;
+       struct uhci *uhci = dp->data;
+       struct uhci_proc *up;
+       unsigned long flags;
+       int ret = -ENOMEM;
+
+       lock_kernel();
+       up = kmalloc(sizeof(*up), GFP_KERNEL);
+       if (!up)
+               goto out;
+
+       up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
+       if (!up->data) {
+               kfree(up);
+               goto out;
+       }
+
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+       up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+
+       file->private_data = up;
+
+       ret = 0;
+out:
+       unlock_kernel();
+       return ret;
+}
+
+static loff_t uhci_proc_lseek(struct file *file, loff_t off, int whence)
+{
+       struct uhci_proc *up = file->private_data;
+       loff_t new;
+
+       switch (whence) {
+       case 0:
+               new = off;
+               break;
+       case 1:
+               new = file->f_pos + off;
+               break;
+       case 2:
+       default:
+               return -EINVAL;
        }
+       if (new < 0 || new > up->size)
+               return -EINVAL;
+       return (file->f_pos = new);
 }
+
+static ssize_t uhci_proc_read(struct file *file, char *buf, size_t nbytes,
+                       loff_t *ppos)
+{
+       struct uhci_proc *up = file->private_data;
+       unsigned int pos;
+       unsigned int size;
+
+       pos = *ppos;
+       size = up->size;
+       if (pos >= size)
+               return 0;
+       if (nbytes >= size)
+               nbytes = size;
+       if (pos + nbytes > size)
+               nbytes = size - pos;
+
+       if (!access_ok(VERIFY_WRITE, buf, nbytes))
+               return -EINVAL;
+
+       copy_to_user(buf, up->data + pos, nbytes);
+
+       *ppos += nbytes;
+
+       return nbytes;
+}
+
+static int uhci_proc_release(struct inode *inode, struct file *file)
+{
+       struct uhci_proc *up = file->private_data;
+
+       kfree(up->data);
+       kfree(up);
+
+       return 0;
+}
+
+static struct file_operations uhci_proc_operations = {
+       open:           uhci_proc_open,
+       llseek:         uhci_proc_lseek,
+       read:           uhci_proc_read,
+//     write:          uhci_proc_write,
+       release:        uhci_proc_release,
+};
+#endif
 
diff -urN -X dontdiff linux-2.4.3-pre8.orig/drivers/usb/uhci.c 
linux-2.4.3-pre8/drivers/usb/uhci.c
--- linux-2.4.3-pre8.orig/drivers/usb/uhci.c    Mon Mar 26 11:21:55 2001
+++ linux-2.4.3-pre8/drivers/usb/uhci.c Mon Mar 26 15:39:10 2001
@@ -1,8 +1,10 @@
 /*
  * Universal Host Controller Interface driver for USB.
  *
+ * Maintainer: Johannes Erdfelt <[EMAIL PROTECTED]>
+ *
  * (C) Copyright 1999 Linus Torvalds
- * (C) Copyright 1999-2000 Johannes Erdfelt, [EMAIL PROTECTED]
+ * (C) Copyright 1999-2001 Johannes Erdfelt, [EMAIL PROTECTED]
  * (C) Copyright 1999 Randy Dunlap
  * (C) Copyright 1999 Georg Acher, [EMAIL PROTECTED]
  * (C) Copyright 1999 Deti Fliegl, [EMAIL PROTECTED]
@@ -12,7 +14,6 @@
  *               support from usb-ohci.c by Adam Richter, [EMAIL PROTECTED]).
  * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
  *
- *
  * Intel documents this fairly well, and as far as I know there
  * are no royalties or anything like that, but even so there are
  * people who decided that they want to do the same thing in a
@@ -39,7 +40,12 @@
 #include <linux/unistd.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#ifdef CONFIG_USB_DEBUG
 #define DEBUG
+#else
+#undef DEBUG
+#endif
 #include <linux/usb.h>
 
 #include <asm/uaccess.h>
@@ -48,23 +54,36 @@
 #include <asm/system.h>
 
 #include "uhci.h"
-#include "uhci-debug.h"
 
 #include <linux/pm.h>
 
+/*
+ * debug = 0, no debugging messages
+ * debug = 1, dump failed URB's except for stalls
+ * debug = 2, dump all failed URB's (including stalls)
+ *            show all queues in /proc/uhci/hc*
+ * debug = 3, show all TD's in URB's when dumping
+ */
+#ifdef DEBUG
 static int debug = 1;
+#else
+static int debug = 0;
+#endif
 MODULE_PARM(debug, "i");
 MODULE_PARM_DESC(debug, "Debug level");
+static char *errbuf;
+#define ERRBUF_LEN    (PAGE_SIZE * 8)
+
+#include "uhci-debug.h"
 
-static kmem_cache_t *uhci_td_cachep;
-static kmem_cache_t *uhci_qh_cachep;
 static kmem_cache_t *uhci_up_cachep;   /* urb_priv */
 
 static int rh_submit_urb(struct urb *urb);
 static int rh_unlink_urb(struct urb *urb);
 static int uhci_get_current_frame_number(struct usb_device *dev);
-static int uhci_unlink_generic(struct urb *urb);
 static int uhci_unlink_urb(struct urb *urb);
+static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb);
+static void uhci_call_completion(struct urb *urb);
 
 static int  ports_active(struct uhci *uhci);
 static void suspend_hc(struct uhci *uhci);
@@ -75,6 +94,8 @@
 /* If a transfer is still active after this much time, turn off FSBR */
 #define IDLE_TIMEOUT   (HZ / 20)       /* 50 ms */
 
+#define MAX_URB_LOOP   2048            /* Maximum number of linked URB's */
+
 /*
  * Only the USB core should call uhci_alloc_dev and uhci_free_dev
  */
@@ -86,80 +107,95 @@
 static int uhci_free_dev(struct usb_device *dev)
 {
        struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
-       struct list_head *tmp, *head = &uhci->urb_list;
+       struct list_head list, *tmp, *head;
        unsigned long flags;
 
        /* Walk through the entire URB list and forcefully remove any */
        /*  URBs that are still active for that device */
-       nested_lock(&uhci->urblist_lock, flags);
+
+       /* Two stage unlink so we don't deadlock on urb_list_lock */
+       INIT_LIST_HEAD(&list);
+
+       spin_lock_irqsave(&uhci->urb_list_lock, flags);
+       head = &uhci->urb_list;
        tmp = head->next;
        while (tmp != head) {
-               struct urb *u = list_entry(tmp, struct urb, urb_list);
+               struct urb *urb = list_entry(tmp, struct urb, urb_list);
 
                tmp = tmp->next;
 
-               if (u->dev == dev)
-                       uhci_unlink_urb(u);
+               if (urb->dev == dev) {
+                       list_del(&urb->urb_list);
+                       list_add(&urb->urb_list, &list);
+               }
        }
-       nested_unlock(&uhci->urblist_lock, flags);
+       spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
-       return 0;
-}
+       head = &list;
+       tmp = head->next;
+       while (tmp != head) {
+               struct urb *urb = list_entry(tmp, struct urb, urb_list);
+
+               tmp = tmp->next;
+
+               /* Make sure we block waiting on these to die */
+               urb->transfer_flags &= ~USB_ASYNC_UNLINK;
+
+               /* uhci_unlink_urb will unlink from the temp list */
+               uhci_unlink_urb(urb);
+       }
 
-static void uhci_add_urb_list(struct uhci *uhci, struct urb *urb)
-{
-       unsigned long flags;
 
-       nested_lock(&uhci->urblist_lock, flags);
-       list_add(&urb->urb_list, &uhci->urb_list);
-       nested_unlock(&uhci->urblist_lock, flags);
+       return 0;
 }
 
-static void uhci_remove_urb_list(struct uhci *uhci, struct urb *urb)
+static inline void uhci_set_next_interrupt(struct uhci *uhci)
 {
        unsigned long flags;
 
-       nested_lock(&uhci->urblist_lock, flags);
-       if (!list_empty(&urb->urb_list)) {
-               list_del(&urb->urb_list);
-               INIT_LIST_HEAD(&urb->urb_list);
-       }
-       nested_unlock(&uhci->urblist_lock, flags);
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+       uhci->skel_term_td->status |= TD_CTRL_IOC;
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
-void uhci_set_next_interrupt(struct uhci *uhci)
+static inline void uhci_clear_next_interrupt(struct uhci *uhci)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
-       uhci->skel_term_td.status |= TD_CTRL_IOC;
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+       uhci->skel_term_td->status &= ~TD_CTRL_IOC;
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
-void uhci_clear_next_interrupt(struct uhci *uhci)
+static inline void uhci_add_complete(struct urb *urb)
 {
+       struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
        unsigned long flags;
 
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
-       uhci->skel_term_td.status &= ~TD_CTRL_IOC;
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+       spin_lock_irqsave(&uhci->complete_list_lock, flags);
+       list_add(&urbp->complete_list, &uhci->complete_list);
+       spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
 }
 
-static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
+static struct uhci_td *uhci_alloc_td(struct uhci *uhci, struct usb_device *dev)
 {
+       dma_addr_t dma_handle;
        struct uhci_td *td;
 
-       td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : 
SLAB_KERNEL);
+       td = pci_pool_alloc(uhci->td_pool, GFP_DMA, &dma_handle);
        if (!td)
                return NULL;
 
        td->link = UHCI_PTR_TERM;
        td->buffer = 0;
 
-       td->frameptr = NULL;
-       td->nexttd = td->prevtd = NULL;
+       td->dma_handle = dma_handle;
+       td->frame = -1;
        td->dev = dev;
+
        INIT_LIST_HEAD(&td->list);
+       INIT_LIST_HEAD(&td->fl_list);
 
        usb_inc_dev_use(dev);
 
@@ -177,20 +213,19 @@
 static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td 
*td)
 {
        unsigned long flags;
+       struct uhci_td *ltd;
+
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
+       ltd = list_entry(skeltd->fl_list.prev, struct uhci_td, fl_list);
 
-       /* Fix the linked list pointers */
-       td->nexttd = skeltd->nexttd;
-       td->prevtd = skeltd;
-       if (skeltd->nexttd)
-               skeltd->nexttd->prevtd = td;
-       skeltd->nexttd = td;
+       td->link = ltd->link;
+       mb();
+       ltd->link = td->dma_handle;
 
-       td->link = skeltd->link;
-       skeltd->link = virt_to_bus(td);
+       list_add_tail(&td->fl_list, &skeltd->fl_list);
 
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
 /*
@@ -200,27 +235,36 @@
  * frame list pointer -> iso td's (if any) ->
  * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh
  */
-
 static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned 
framenum)
 {
        unsigned long flags;
-       struct uhci_td *nexttd;
 
        framenum %= UHCI_NUMFRAMES;
 
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+       td->frame = framenum;
+
+       /* Is there a TD already mapped there? */
+       if (uhci->fl->frame_cpu[framenum]) {
+               struct uhci_td *ftd, *ltd;
+
+               ftd = uhci->fl->frame_cpu[framenum];
+               ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
 
-       td->frameptr = &uhci->fl->frame[framenum];
-       td->link = uhci->fl->frame[framenum];
-       if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) {
-               nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link);
-               td->nexttd = nexttd;
-               nexttd->prevtd = td;
-               nexttd->frameptr = NULL;
+               list_add_tail(&td->fl_list, &ftd->fl_list);
+
+               td->link = ltd->link;
+               mb();
+               ltd->link = td->dma_handle;
+       } else {
+               td->link = uhci->fl->frame[framenum];
+               mb();
+               uhci->fl->frame[framenum] = td->dma_handle;
+               uhci->fl->frame_cpu[framenum] = td;
        }
-       uhci->fl->frame[framenum] = virt_to_bus(td);
 
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
 static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
@@ -228,29 +272,36 @@
        unsigned long flags;
 
        /* If it's not inserted, don't remove it */
-       if (!td->frameptr && !td->prevtd && !td->nexttd)
+       if (td->frame == -1 && list_empty(&td->fl_list))
                return;
 
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
-       if (td->frameptr) {
-               *(td->frameptr) = td->link;
-               if (td->nexttd) {
-                       td->nexttd->frameptr = td->frameptr;
-                       td->nexttd->prevtd = NULL;
-                       td->nexttd = NULL;
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+       if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
+               if (list_empty(&td->fl_list)) {
+                       uhci->fl->frame[td->frame] = td->link;
+                       uhci->fl->frame_cpu[td->frame] = NULL;
+               } else {
+                       struct uhci_td *ntd;
+
+                       ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
+                       uhci->fl->frame[td->frame] = ntd->dma_handle;
+                       uhci->fl->frame_cpu[td->frame] = ntd;
                }
-               td->frameptr = NULL;
        } else {
-               if (td->prevtd) {
-                       td->prevtd->nexttd = td->nexttd;
-                       td->prevtd->link = td->link;
-               }
-               if (td->nexttd)
-                       td->nexttd->prevtd = td->prevtd;
-               td->prevtd = td->nexttd = NULL;
+               struct uhci_td *ptd;
+
+               ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
+               ptd->link = td->link;
        }
+
+       mb();
        td->link = UHCI_PTR_TERM;
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+
+       list_del(&td->fl_list);
+       INIT_LIST_HEAD(&td->fl_list);
+       td->frame = -1;
+
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
 /*
@@ -260,22 +311,21 @@
 {
        struct list_head *tmp, *head;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-       struct uhci_td *td, *prevtd;
-
-       if (!urbp)
-               return;
+       struct uhci_td *td, *ptd;
 
-       head = &urbp->list;
+       head = &urbp->td_list;
        tmp = head->next;
        if (head == tmp)
                return;
 
+       /* Ordering isn't important here yet since the QH hasn't been */
+       /*  inserted into the schedule yet */
        td = list_entry(tmp, struct uhci_td, list);
 
        /* Add the first TD to the QH element pointer */
-       qh->element = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
+       qh->element = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH);
 
-       prevtd = td;
+       ptd = td;
 
        /* Then link the rest of the TD's */
        tmp = tmp->next;
@@ -284,39 +334,41 @@
 
                tmp = tmp->next;
 
-               prevtd->link = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
+               ptd->link = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH);
 
-               prevtd = td;
+               ptd = td;
        }
 
-       prevtd->link = UHCI_PTR_TERM;
+       ptd->link = UHCI_PTR_TERM;
 }
 
-static void uhci_free_td(struct uhci_td *td)
+static void uhci_free_td(struct uhci *uhci, struct uhci_td *td)
 {
-       if (!list_empty(&td->list))
+       if (!list_empty(&td->list) || !list_empty(&td->fl_list))
                dbg("td is still in URB list!");
 
        if (td->dev)
                usb_dec_dev_use(td->dev);
 
-       kmem_cache_free(uhci_td_cachep, td);
+       pci_pool_free(uhci->td_pool, td, td->dma_handle);
 }
 
-static struct uhci_qh *uhci_alloc_qh(struct usb_device *dev)
+static struct uhci_qh *uhci_alloc_qh(struct uhci *uhci, struct usb_device *dev)
 {
+       dma_addr_t dma_handle;
        struct uhci_qh *qh;
 
-       qh = kmem_cache_alloc(uhci_qh_cachep, in_interrupt() ? SLAB_ATOMIC : 
SLAB_KERNEL);
+       qh = pci_pool_alloc(uhci->qh_pool, GFP_DMA, &dma_handle);
        if (!qh)
                return NULL;
 
        qh->element = UHCI_PTR_TERM;
        qh->link = UHCI_PTR_TERM;
 
+       qh->dma_handle = dma_handle;
        qh->dev = dev;
-       qh->prevqh = qh->nextqh = NULL;
 
+       INIT_LIST_HEAD(&qh->list);
        INIT_LIST_HEAD(&qh->remove_list);
 
        usb_inc_dev_use(dev);
@@ -324,181 +376,271 @@
        return qh;
 }
 
-static void uhci_free_qh(struct uhci_qh *qh)
+static void uhci_free_qh(struct uhci *uhci, struct uhci_qh *qh)
 {
+       if (!list_empty(&qh->list))
+               dbg("qh list not empty!");
+       if (!list_empty(&qh->remove_list))
+               dbg("qh still in remove_list!\n");
+
        if (qh->dev)
                usb_dec_dev_use(qh->dev);
 
-       kmem_cache_free(uhci_qh_cachep, qh);
+       pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
 }
 
-static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhci_qh 
*qh)
+static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhci_qh 
+*qh)
 {
-       unsigned long flags;
+       struct uhci_qh *lqh;
 
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
+       /* Grab the last QH */
+       lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
 
-       /* Fix the linked list pointers */
-       qh->nextqh = skelqh->nextqh;
-       qh->prevqh = skelqh;
-       if (skelqh->nextqh)
-               skelqh->nextqh->prevqh = qh;
-       skelqh->nextqh = qh;
+       qh->link = lqh->link;
+       mb();                           /* Ordering is important */
+       lqh->link = qh->dma_handle | UHCI_PTR_QH;
 
-       qh->link = skelqh->link;
-       skelqh->link = virt_to_bus(qh) | UHCI_PTR_QH;
+       list_add_tail(&qh->list, &skelqh->list);
+}
 
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhci_qh 
+*qh)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+       _uhci_insert_qh(uhci, skelqh, qh);
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
 static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
 {
        unsigned long flags;
-       int delayed;
+       struct uhci_qh *prevqh;
+
+       /* Only go through the hoops if it's actually linked in */
+       if (list_empty(&qh->list)) {
+               uhci_free_qh(uhci, qh);
+               return;
+       }
+
+       qh->urbp = NULL;
+
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
-       /* If the QH isn't queued, then we don't need to delay unlink it */
-       delayed = (qh->prevqh || qh->nextqh);
+       prevqh = list_entry(qh->list.prev, struct uhci_qh, list);
 
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
-       if (qh->prevqh) {
-               qh->prevqh->nextqh = qh->nextqh;
-               qh->prevqh->link = qh->link;
-       }
-       if (qh->nextqh)
-               qh->nextqh->prevqh = qh->prevqh;
-       qh->prevqh = qh->nextqh = NULL;
+       prevqh->link = qh->link;
+       mb();
        qh->element = qh->link = UHCI_PTR_TERM;
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 
-       if (delayed) {
-               spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+       list_del(&qh->list);
+       INIT_LIST_HEAD(&qh->list);
 
-               /* Check to see if the remove list is empty */
-               /* Set the IOC bit to force an interrupt so we can remove the QH */
-               if (list_empty(&uhci->qh_remove_list))
-                       uhci_set_next_interrupt(uhci);
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 
-               /* Add it */
-               list_add(&qh->remove_list, &uhci->qh_remove_list);
+       spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
 
-               spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
-       } else
-               uhci_free_qh(qh);
+       /* Check to see if the remove list is empty. Set the IOC bit */
+       /* to force an interrupt so we can remove the QH */
+       if (list_empty(&uhci->qh_remove_list))
+               uhci_set_next_interrupt(uhci);
+
+       list_add(&qh->remove_list, &uhci->qh_remove_list);
+
+       spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
 }
 
-static spinlock_t uhci_append_urb_lock = SPIN_LOCK_UNLOCKED;
+static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct list_head *head, *tmp;
+
+       head = &urbp->td_list;
+       tmp = head->next;
+       while (head != tmp) {
+               struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+               tmp = tmp->next;
+
+               td->info &= ~(1 << TD_TOKEN_TOGGLE);
+               if (toggle)
+                       td->info |= (1 << TD_TOKEN_TOGGLE);
+
+               toggle ^= 1;
+       }
+
+       return toggle;
+}
 
 /* This function will append one URB's QH to another URB's QH. This is for */
-/*  USB_QUEUE_BULK support */
+/*  USB_QUEUE_BULK support for bulk transfers and implicitily for control */
+/*  transfers */
 static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb 
*urb)
 {
+       /* eurb = existing, urb = new, furb = first, lurb = last */
        struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
        struct list_head *tmp;
-       struct uhci_td *td, *ltd;
+       struct uhci_td *ftd, *lltd;
        unsigned long flags;
 
        eurbp = eurb->hcpriv;
        urbp = urb->hcpriv;
 
-       spin_lock_irqsave(&uhci_append_urb_lock, flags);
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
-       /* Find the beginning URB in the queue */
+       /* Find the first URB in the queue */
        if (eurbp->queued) {
-               struct list_head *head = &eurbp->urb_queue_list;
+               struct list_head *head = &eurbp->queue_list;
 
                tmp = head->next;
                while (tmp != head) {
                        struct urb_priv *turbp =
-                               list_entry(tmp, struct urb_priv, urb_queue_list);
-
-                       tmp = tmp->next;
+                               list_entry(tmp, struct urb_priv, queue_list);
 
                        if (!turbp->queued)
                                break;
+
+                       tmp = tmp->next;
                }
        } else
-               tmp = &eurbp->urb_queue_list;
+               tmp = &eurbp->queue_list;
 
-       furbp = list_entry(tmp, struct urb_priv, urb_queue_list);
+       furbp = list_entry(tmp, struct urb_priv, queue_list);
+       lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
 
-       tmp = furbp->urb_queue_list.prev;
-       lurbp = list_entry(tmp, struct urb_priv, urb_queue_list);
+       lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
+       ftd = list_entry(urbp->td_list.next, struct uhci_td, list);
 
-       /* Add this one to the end */
-       list_add_tail(&urbp->urb_queue_list, &furbp->urb_queue_list);
+       uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1);
 
-       /* Grab the last TD from the last URB */
-       ltd = list_entry(lurbp->list.prev, struct uhci_td, list);
+       /* No depth since this will only be called for bulk transfers */
+       lltd->link = ftd->dma_handle;
 
-       /* Grab the first TD from the first URB */
-       td = list_entry(urbp->list.next, struct uhci_td, list);
+       list_add_tail(&urbp->queue_list, &furbp->queue_list);
 
-       /* No breadth since this will only be called for bulk transfers */
-       ltd->link = virt_to_bus(td);
+       urbp->queued = 1;
 
-       spin_unlock_irqrestore(&uhci_append_urb_lock, flags);
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
 static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb)
 {
        struct urb_priv *urbp, *nurbp;
+       struct list_head *head, *tmp;
+       struct urb_priv *purbp;
+       struct uhci_td *pltd;
+       unsigned int toggle;
        unsigned long flags;
 
        urbp = urb->hcpriv;
 
-       spin_lock_irqsave(&uhci_append_urb_lock, flags);
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
-       nurbp = list_entry(urbp->urb_queue_list.next, struct urb_priv,
-                       urb_queue_list);
+       if (list_empty(&urbp->queue_list))
+               goto out;
+
+       nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
+
+       /* Fix up the toggle for the next URB's */
+       if (!urbp->queued)
+               toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
+usb_pipeout(urb->pipe));
+       else {
+               purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+                               queue_list);
+
+               pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+
+               toggle = uhci_toggle(pltd->info) ^ 1;
+       }
+       
+       head = &urbp->queue_list;
+       tmp = head->next;
+       while (head != tmp) {
+               struct urb_priv *turbp;
+
+               turbp = list_entry(tmp, struct urb_priv, queue_list);
+
+               tmp = tmp->next;
+
+               if (!turbp->queued)
+                       break;
+
+               toggle = uhci_fixup_toggle(turbp->urb, toggle);
+       }
+
+       usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+               usb_pipeout(urb->pipe), toggle);
 
        if (!urbp->queued) {
                /* We're the head, so just insert the QH for the next URB */
-               uhci_insert_qh(uhci, &uhci->skel_bulk_qh, nurbp->qh);
+               _uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->qh);
                nurbp->queued = 0;
        } else {
-               struct urb_priv *purbp;
-               struct uhci_td *ptd;
-
                /* We're somewhere in the middle (or end). A bit trickier */
                /*  than the head scenario */
-               purbp = list_entry(urbp->urb_queue_list.prev, struct urb_priv,
-                               urb_queue_list);
+               purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+                               queue_list);
+
+               pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+               if (nurbp->queued) {
+                       struct uhci_td *nftd;
 
-               ptd = list_entry(purbp->list.prev, struct uhci_td, list);
-               if (nurbp->queued)
                        /* Close the gap between the two */
-                       ptd->link = virt_to_bus(list_entry(nurbp->list.next,
-                                       struct uhci_td, list));
-               else
+                       nftd = list_entry(nurbp->td_list.next, struct uhci_td,
+                                       list);
+                       pltd->link = nftd->dma_handle;
+               } else
                        /* The next URB happens to be the beggining, so */
                        /*  we're the last, end the chain */
-                       ptd->link = UHCI_PTR_TERM;
-               
+                       pltd->link = UHCI_PTR_TERM;
        }
 
-       list_del(&urbp->urb_queue_list);
+       list_del(&urbp->queue_list);
+       INIT_LIST_HEAD(&urbp->queue_list);
 
-       spin_unlock_irqrestore(&uhci_append_urb_lock, flags);
+out:
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
-struct urb_priv *uhci_alloc_urb_priv(struct urb *urb)
+static struct urb_priv *uhci_alloc_urb_priv(struct urb *urb, struct uhci *uhci)
 {
        struct urb_priv *urbp;
 
        urbp = kmem_cache_alloc(uhci_up_cachep, in_interrupt() ? SLAB_ATOMIC : 
SLAB_KERNEL);
-       if (!urbp)
+       if (!urbp) {
+               err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
                return NULL;
+       }
 
        memset((void *)urbp, 0, sizeof(*urbp));
 
        urbp->inserttime = jiffies;
        urbp->urb = urb;
+       urbp->dev = urb->dev;
        
-       INIT_LIST_HEAD(&urbp->list);
-       INIT_LIST_HEAD(&urbp->urb_queue_list);
+       INIT_LIST_HEAD(&urbp->td_list);
+       INIT_LIST_HEAD(&urbp->queue_list);
+       INIT_LIST_HEAD(&urbp->complete_list);
 
        urb->hcpriv = urbp;
 
+       if (urb->transfer_buffer_length) {
+               urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev,
+                       urb->transfer_buffer, urb->transfer_buffer_length,
+                       usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE :
+                       PCI_DMA_TODEVICE);
+               if (!urbp->transfer_buffer_dma_handle)
+                       return NULL;
+       }
+
+       if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) {
+               urbp->setup_packet_dma_handle = pci_map_single(uhci->dev,
+                       urb->setup_packet, sizeof(devrequest),
+                       PCI_DMA_TODEVICE);
+               if (!urbp->setup_packet_dma_handle)
+                       return NULL;
+       }
+
        return urbp;
 }
 
@@ -508,13 +650,11 @@
 
        td->urb = urb;
 
-       list_add_tail(&td->list, &urbp->list);
+       list_add_tail(&td->list, &urbp->td_list);
 }
 
-static void uhci_remove_td_from_urb(struct urb *urb, struct uhci_td *td)
+static void uhci_remove_td_from_urb(struct uhci_td *td)
 {
-       urb = NULL;     /* No warnings */
-
        if (list_empty(&td->list))
                return;
 
@@ -526,41 +666,55 @@
 
 static void uhci_destroy_urb_priv(struct urb *urb)
 {
-       struct list_head *tmp, *head;
+       struct list_head *head, *tmp;
        struct urb_priv *urbp;
        struct uhci *uhci;
-       struct uhci_td *td;
        unsigned long flags;
 
        spin_lock_irqsave(&urb->lock, flags);
 
        urbp = (struct urb_priv *)urb->hcpriv;
        if (!urbp)
-               goto unlock;
+               goto out;
 
-       if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
-               goto unlock;
+       if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) {
+               warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or 
+bus?", urb);
+               goto out;
+       }
 
-       uhci = urb->dev->bus->hcpriv;
+       if (!list_empty(&urb->urb_list))
+               warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or 
+uhci->remove_list", urb);
 
-       head = &urbp->list;
+       if (!list_empty(&urbp->complete_list))
+               warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", 
+urb);
+
+       uhci = urbp->dev->bus->hcpriv;
+
+       head = &urbp->td_list;
        tmp = head->next;
        while (tmp != head) {
-               td = list_entry(tmp, struct uhci_td, list);
+               struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
                tmp = tmp->next;
 
-               uhci_remove_td_from_urb(urb, td);
-
+               uhci_remove_td_from_urb(td);
                uhci_remove_td(uhci, td);
-
-               uhci_free_td(td);
+               uhci_free_td(uhci, td);
        }
 
+       if (urb->setup_packet)
+               pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle,
+                       sizeof(devrequest), PCI_DMA_TODEVICE);
+
+       if (urb->transfer_buffer_length)
+               pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle,
+                       urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
+                       PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+
        urb->hcpriv = NULL;
        kmem_cache_free(uhci_up_cachep, urbp);
 
-unlock:
+out:
        spin_unlock_irqrestore(&urb->lock, flags);
 }
 
@@ -569,18 +723,15 @@
        unsigned long flags;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
-       if (!urbp)
-               return;
-
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
-       if ((!(urb->transfer_flags & USB_NO_FSBR)) && (!urbp->fsbr)) {
+       if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) {
                urbp->fsbr = 1;
                if (!uhci->fsbr++)
-                       uhci->skel_term_qh.link = 
virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
+                       uhci->skel_term_qh->link = 
+uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH;
        }
 
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
 static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb)
@@ -588,18 +739,15 @@
        unsigned long flags;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
-       if (!urbp)
-               return;
-
-       spin_lock_irqsave(&uhci->framelist_lock, flags);
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
        if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) {
                urbp->fsbr = 0;
                if (!--uhci->fsbr)
-                       uhci->skel_term_qh.link = UHCI_PTR_TERM;
+                       uhci->skel_term_qh->link = UHCI_PTR_TERM;
        }
 
-       spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
 /*
@@ -637,7 +785,7 @@
 /*
  * Control transfers
  */
-static int uhci_submit_control(struct urb *urb)
+static int uhci_submit_control(struct urb *urb, struct urb *eurb)
 {
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -646,7 +794,7 @@
        unsigned long destination, status;
        int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
        int len = urb->transfer_buffer_length;
-       unsigned char *data = urb->transfer_buffer;
+       dma_addr_t data = urbp->transfer_buffer_dma_handle;
 
        /* The "pipe" thing contains the destination in bits 8--18 */
        destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
@@ -657,13 +805,13 @@
        /*
         * Build the TD for the control request
         */
-       td = uhci_alloc_td(urb->dev);
+       td = uhci_alloc_td(uhci, urb->dev);
        if (!td)
                return -ENOMEM;
 
        uhci_add_td_to_urb(urb, td);
        uhci_fill_td(td, status, destination | (7 << 21),
-               virt_to_bus(urb->setup_packet));
+               urbp->setup_packet_dma_handle);
 
        /*
         * If direction is "send", change the frame from SETUP (0x2D)
@@ -683,7 +831,7 @@
                if (pktsze > maxsze)
                        pktsze = maxsze;
 
-               td = uhci_alloc_td(urb->dev);
+               td = uhci_alloc_td(uhci, urb->dev);
                if (!td)
                        return -ENOMEM;
 
@@ -692,7 +840,7 @@
        
                uhci_add_td_to_urb(urb, td);
                uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),
-                       virt_to_bus(data));
+                       data);
 
                data += pktsze;
                len -= pktsze;
@@ -701,7 +849,7 @@
        /*
         * Build the final TD for control status 
         */
-       td = uhci_alloc_td(urb->dev);
+       td = uhci_alloc_td(uhci, urb->dev);
        if (!td)
                return -ENOMEM;
 
@@ -723,23 +871,22 @@
        uhci_fill_td(td, status | TD_CTRL_IOC,
                destination | (UHCI_NULL_DATA_SIZE << 21), 0);
 
-       qh = uhci_alloc_qh(urb->dev);
+       qh = uhci_alloc_qh(uhci, urb->dev);
        if (!qh)
                return -ENOMEM;
 
        /* 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);
+               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_insert_qh(uhci, uhci->skel_hs_control_qh, qh);
                uhci_inc_fsbr(uhci, urb);
        }
 
        urbp->qh = qh;
-
-       uhci_add_urb_list(uhci, urb);
+       qh->urbp = urbp;
 
        return -EINPROGRESS;
 }
@@ -754,12 +901,10 @@
        unsigned int status;
        int ret = 0;
 
-       if (!urbp)
+       if (list_empty(&urbp->td_list))
                return -EINVAL;
 
-       head = &urbp->list;
-       if (head->next == head)
-               return -EINVAL;
+       head = &urbp->td_list;
 
        if (urbp->short_control_packet) {
                tmp = head->prev;
@@ -849,12 +994,16 @@
                                uhci_packetout(td->info));
 
 err:
-       if (debug && ret != -EPIPE) {
+       if ((debug == 1 && ret != -EPIPE) || debug > 1) {
                /* Some debugging code */
                dbg("uhci_result_control() failed with status %x", status);
 
-               /* Print the chain for debugging purposes */
-               uhci_show_urb_queue(urb);
+               if (errbuf) {
+                       /* Print the chain for debugging purposes */
+                       uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+                       lprintk(errbuf);
+               }
        }
 
        return ret;
@@ -872,34 +1021,34 @@
        uhci_remove_qh(uhci, urbp->qh);
 
        /* Delete all of the TD's except for the status TD at the end */
-       head = &urbp->list;
+       head = &urbp->td_list;
        tmp = head->next;
        while (tmp != head && tmp->next != head) {
                struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
                tmp = tmp->next;
 
-               uhci_remove_td_from_urb(urb, td);
-
+               uhci_remove_td_from_urb(td);
                uhci_remove_td(uhci, td);
-
-               uhci_free_td(td);
+               uhci_free_td(uhci, td);
        }
 
-       urbp->qh = uhci_alloc_qh(urb->dev);
+       urbp->qh = uhci_alloc_qh(uhci, urb->dev);
        if (!urbp->qh) {
                err("unable to allocate new QH for control retrigger");
                return -ENOMEM;
        }
 
+       urbp->qh->urbp = urbp;
+
        /* One TD, who cares about Breadth first? */
        uhci_insert_tds_in_qh(urbp->qh, urb, 0);
 
        /* Low speed or small transfers gets a different queue and treatment */
        if (urb->pipe & TD_CTRL_LS)
-               uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+               uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urbp->qh);
        else
-               uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+               uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urbp->qh);
 
        return -EINPROGRESS;
 }
@@ -912,6 +1061,7 @@
        struct uhci_td *td;
        unsigned long destination, status;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
        if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, 
usb_pipeout(urb->pipe)))
                return -EINVAL;
@@ -921,7 +1071,7 @@
 
        status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
 
-       td = uhci_alloc_td(urb->dev);
+       td = uhci_alloc_td(uhci, urb->dev);
        if (!td)
                return -ENOMEM;
 
@@ -931,12 +1081,9 @@
        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_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle);
 
-       uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td);
-
-       uhci_add_urb_list(uhci, urb);
+       uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td);
 
        return -EINPROGRESS;
 }
@@ -949,12 +1096,20 @@
        unsigned int status;
        int ret = 0;
 
-       if (!urbp)
-               return -EINVAL;
+       {
+               struct uhci_qh *qh = urbp->qh;
+
+               if (qh && urbp->elements[urbp->element_entry] != qh->element) {
+                       urbp->elements_time[urbp->element_entry] = jiffies;
+                       urbp->elements[urbp->element_entry++] = qh->element;
+                       if (urbp->element_entry >= (sizeof(urbp->elements) / 
+sizeof(urbp->elements[0])))
+                               urbp->element_entry = 0;
+               }
+       }
 
        urb->actual_length = 0;
 
-       head = &urbp->list;
+       head = &urbp->td_list;
        tmp = head->next;
        while (tmp != head) {
                td = list_entry(tmp, struct uhci_td, list);
@@ -969,8 +1124,18 @@
                }
 
                status = uhci_status_bits(td->status);
-               if (status & TD_CTRL_ACTIVE)
+               if (status & TD_CTRL_ACTIVE) {
+/*
+                       struct uhci_qh *qh = urbp->qh;
+
+                       if (qh && qh->element != (td->dma_handle | (qh->element & 
+UHCI_PTR_BITS)))
+{
+// printk("fixing up stalled QH %p\n", qh);
+                               qh->element = td->dma_handle | (qh->element & 
+UHCI_PTR_BITS);
+}
+*/
                        return -EINPROGRESS;
+               }
 
                urb->actual_length += uhci_actual_length(td->status);
 
@@ -1000,16 +1165,20 @@
                                uhci_packetout(td->info));
 
 err:
-       if (debug && ret != -EPIPE) {
+       if ((debug == 1 && ret != -EPIPE) || debug > 1) {
                /* Some debugging code */
                dbg("uhci_result_interrupt/bulk() failed with status %x",
                        status);
 
-               /* Print the chain for debugging purposes */
-               if (urbp->qh)
-                       uhci_show_urb_queue(urb);
-               else
-                       uhci_show_td(td);
+               if (errbuf) {
+                       /* Print the chain for debugging purposes */
+                       if (urbp->qh)
+                               uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+                       else
+                               uhci_show_td(td, errbuf, ERRBUF_LEN, 0);
+
+                       lprintk(errbuf);
+               }
        }
 
        return ret;
@@ -1017,24 +1186,28 @@
 
 static void uhci_reset_interrupt(struct urb *urb)
 {
-       struct list_head *tmp;
+       struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
        struct uhci_td *td;
+       unsigned long flags;
 
-       if (!urbp)
-               return;
+       spin_lock_irqsave(&urb->lock, flags);
 
-       tmp = urbp->list.next;
-       td = list_entry(tmp, struct uhci_td, list);
-       if (!td)
-               return;
+       /* Root hub is special */
+       if (urb->dev == uhci->rh.dev)
+               goto out;
+
+       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);
        usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 
+out:
        urb->status = -EINPROGRESS;
+
+       spin_unlock_irqrestore(&urb->lock, flags);
 }
 
 /*
@@ -1048,8 +1221,8 @@
        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 = (struct urb_priv *)urb->hcpriv;
+       dma_addr_t data = urbp->transfer_buffer_dma_handle;
 
        if (len < 0)
                return -EINVAL;
@@ -1076,7 +1249,7 @@
                if (pktsze > maxsze)
                        pktsze = maxsze;
 
-               td = uhci_alloc_td(urb->dev);
+               td = uhci_alloc_td(uhci, urb->dev);
                if (!td)
                        return -ENOMEM;
 
@@ -1084,7 +1257,7 @@
                uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) |
                        (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
                         usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
-                       virt_to_bus(data));
+                       data);
 
                data += pktsze;
                len -= maxsze;
@@ -1096,22 +1269,20 @@
                        usb_pipeout(urb->pipe));
        } while (len > 0);
 
-       qh = uhci_alloc_qh(urb->dev);
+       qh = uhci_alloc_qh(uhci, urb->dev);
        if (!qh)
                return -ENOMEM;
 
        urbp->qh = qh;
+       qh->urbp = urbp;
 
-       /* Always assume depth first */
+       /* Always assume breadth first */
        uhci_insert_tds_in_qh(qh, urb, 1);
 
-       if (urb->transfer_flags & USB_QUEUE_BULK && eurb) {
-               urbp->queued = 1;
+       if (urb->transfer_flags & USB_QUEUE_BULK && eurb)
                uhci_append_queued_urb(uhci, eurb, urb);
-       } else
-               uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);
-
-       uhci_add_urb_list(uhci, urb);
+       else
+               uhci_insert_qh(uhci, uhci->skel_bulk_qh, qh);
 
        uhci_inc_fsbr(uhci, urb);
 
@@ -1128,11 +1299,12 @@
 {
        struct urb *last_urb = NULL;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-       struct list_head *tmp, *head = &uhci->urb_list;
+       struct list_head *tmp, *head;
        int ret = 0;
        unsigned long flags;
 
-       nested_lock(&uhci->urblist_lock, flags);
+       spin_lock_irqsave(&uhci->urb_list_lock, flags);
+       head = &uhci->urb_list;
        tmp = head->next;
        while (tmp != head) {
                struct urb *u = list_entry(tmp, struct urb, urb_list);
@@ -1154,7 +1326,7 @@
        } else
                ret = -1;       /* no previous urb found */
 
-       nested_unlock(&uhci->urblist_lock, flags);
+       spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
        return ret;
 }
@@ -1185,12 +1357,16 @@
        return 0;
 }
 
+/*
+ * Isochronous transfers
+ */
 static int uhci_submit_isochronous(struct urb *urb)
 {
        struct uhci_td *td;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
        int i, ret, framenum;
        int status, destination;
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
        status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
        destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
@@ -1204,13 +1380,13 @@
                if (!urb->iso_frame_desc[i].length)
                        continue;
 
-               td = uhci_alloc_td(urb->dev);
+               td = uhci_alloc_td(uhci, urb->dev);
                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));
+                       urbp->transfer_buffer_dma_handle + 
+urb->iso_frame_desc[i].offset);
 
                if (i + 1 >= urb->number_of_packets)
                        td->status |= TD_CTRL_IOC;
@@ -1218,8 +1394,6 @@
                uhci_insert_td_frame_list(uhci, td, framenum);
        }
 
-       uhci_add_urb_list(uhci, urb);
-
        return -EINPROGRESS;
 }
 
@@ -1230,13 +1404,10 @@
        int status;
        int i, ret = 0;
 
-       if (!urbp)
-               return -EINVAL;
-
        urb->actual_length = 0;
 
        i = 0;
-       head = &urbp->list;
+       head = &urbp->td_list;
        tmp = head->next;
        while (tmp != head) {
                struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
@@ -1253,7 +1424,7 @@
 
                status = uhci_map_status(uhci_status_bits(td->status), 
usb_pipeout(urb->pipe));
                urb->iso_frame_desc[i].status = status;
-               if (status != 0) {
+               if (status) {
                        urb->error_count++;
                        ret = status;
                }
@@ -1266,28 +1437,30 @@
 
 static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
 {
-       struct list_head *tmp, *head = &uhci->urb_list;
+       struct list_head *tmp, *head;
        unsigned long flags;
        struct urb *u = NULL;
 
+       /* We don't match Isoc transfers since they are special */
        if (usb_pipeisoc(urb->pipe))
                return NULL;
 
-       nested_lock(&uhci->urblist_lock, flags);
+       spin_lock_irqsave(&uhci->urb_list_lock, flags);
+       head = &uhci->urb_list;
        tmp = head->next;
        while (tmp != head) {
                u = list_entry(tmp, struct urb, urb_list);
 
                tmp = tmp->next;
 
-               if (u->dev == urb->dev &&
-                   u->pipe == urb->pipe)
-                       goto found;
+               if (u->dev == urb->dev && u->pipe == urb->pipe &&
+                   u->status == -EINPROGRESS)
+                       goto out;
        }
        u = NULL;
 
-found:
-       nested_unlock(&uhci->urblist_lock, flags);
+out:
+       spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
        return u;
 }
@@ -1297,38 +1470,58 @@
        int ret = -EINVAL;
        struct uhci *uhci;
        unsigned long flags;
-       struct urb *u;
+       struct urb *eurb;
        int bustime;
 
        if (!urb)
                return -EINVAL;
 
-       if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
+       if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) {
+               warn("uhci_submit_urb: urb %p belongs to disconnected device or bus?", 
+urb);
                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);
-
-       u = uhci_find_urb_ep(uhci, urb);
-       if (u && !(urb->transfer_flags & USB_QUEUE_BULK))
-               return -ENXIO;
+       INIT_LIST_HEAD(&urb->urb_list);
 
        usb_inc_dev_use(urb->dev);
+
        spin_lock_irqsave(&urb->lock, flags);
 
-       if (!uhci_alloc_urb_priv(urb)) {
+       if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
+           urb->status == -ECONNABORTED) {
+               dbg("uhci_submit_urb: urb not available to submit (status = %d)", 
+urb->status);
+               /* Since we can have problems on the out path */
                spin_unlock_irqrestore(&urb->lock, flags);
                usb_dec_dev_use(urb->dev);
 
-               return -ENOMEM;
+               return ret;
+       }
+
+       if (!uhci_alloc_urb_priv(urb, uhci)) {
+               ret = -ENOMEM;
+
+               goto out;
+       }
+
+       eurb = uhci_find_urb_ep(uhci, urb);
+       if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
+               ret = -ENXIO;
+
+               goto out;
+       }
+
+       /* Short circuit the virtual root hub */
+       if (urb->dev == uhci->rh.dev) {
+               ret = rh_submit_urb(urb);
+
+               goto out;
        }
 
        switch (usb_pipetype(urb->pipe)) {
        case PIPE_CONTROL:
-               ret = uhci_submit_control(urb);
+               ret = uhci_submit_control(urb, eurb);
                break;
        case PIPE_INTERRUPT:
                if (urb->bandwidth == 0) {      /* not yet checked/allocated */
@@ -1344,7 +1537,7 @@
                        ret = uhci_submit_interrupt(urb);
                break;
        case PIPE_BULK:
-               ret = uhci_submit_bulk(urb, u);
+               ret = uhci_submit_bulk(urb, eurb);
                break;
        case PIPE_ISOCHRONOUS:
                if (urb->bandwidth == 0) {      /* not yet checked/allocated */
@@ -1366,35 +1559,53 @@
                break;
        }
 
+out:
        urb->status = ret;
 
        spin_unlock_irqrestore(&urb->lock, flags);
 
-       if (ret == -EINPROGRESS)
-               ret = 0;
-       else {
-               uhci_unlink_generic(urb);
-               usb_dec_dev_use(urb->dev);
+       if (ret == -EINPROGRESS) {
+               spin_lock_irqsave(&uhci->urb_list_lock, flags);
+               /* We use _tail to make find_urb_ep more efficient */
+               list_add_tail(&urb->urb_list, &uhci->urb_list);
+               spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+               return 0;
        }
 
+       uhci_unlink_generic(uhci, urb);
+       uhci_destroy_urb_priv(urb);
+
+       usb_dec_dev_use(urb->dev);
+
        return ret;
 }
 
 /*
  * Return the result of a transfer
  *
- * Must be called with urblist_lock acquired
+ * Must be called with urb_list_lock acquired
  */
-static void uhci_transfer_result(struct urb *urb)
+static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
 {
-       struct usb_device *dev = urb->dev;
-       struct urb *turb;
-       int proceed = 0, is_ring = 0;
        int ret = -EINVAL;
        unsigned long flags;
+       struct urb_priv *urbp;
+
+       /* The root hub is special */
+       if (urb->dev == uhci->rh.dev)
+               return;
 
        spin_lock_irqsave(&urb->lock, flags);
 
+       urbp = (struct urb_priv *)urb->hcpriv;
+
+       if (urb->status != -EINPROGRESS) {
+               info("uhci_transfer_result: called for URB %p not in flight?", urb);
+               spin_unlock_irqrestore(&urb->lock, flags);
+               return;
+       }
+
        switch (usb_pipetype(urb->pipe)) {
        case PIPE_CONTROL:
                ret = uhci_result_control(urb);
@@ -1410,7 +1621,7 @@
                break;
        }
 
-       urb->status = ret;
+       urbp->status = ret;
 
        spin_unlock_irqrestore(&urb->lock, flags);
 
@@ -1425,106 +1636,65 @@
                /* Spinlock needed ? */
                if (urb->bandwidth)
                        usb_release_bandwidth(urb->dev, urb, 1);
-               uhci_unlink_generic(urb);
+               uhci_unlink_generic(uhci, urb);
                break;
        case PIPE_INTERRUPT:
                /* Interrupts are an exception */
                if (urb->interval) {
-                       urb->complete(urb);
-                       uhci_reset_interrupt(urb);
-                       return;
+                       uhci_add_complete(urb);
+                       return;         /* <-- note return */
                }
 
                /* Release bandwidth for Interrupt or Isoc. transfers */
                /* Spinlock needed ? */
                if (urb->bandwidth)
                        usb_release_bandwidth(urb->dev, urb, 0);
-               uhci_unlink_generic(urb);
+               uhci_unlink_generic(uhci, urb);
                break;
+       default:
+               info("uhci_transfer_result: unknown pipe type %d for urb %p\n",
+                       usb_pipetype(urb->pipe), urb);
        }
 
-       if (urb->next) {
-               turb = urb->next;
-               do {
-                       if (turb->status != -EINPROGRESS) {
-                               proceed = 1;
-                               break;
-                       }
-
-                       turb = turb->next;
-               } while (turb && turb != urb && turb != urb->next);
-
-               if (turb == urb || turb == urb->next)
-                       is_ring = 1;
-       }
-
-       if (urb->complete && !proceed) {
-               urb->complete(urb);
-               if (!proceed && is_ring)
-                       uhci_submit_urb(urb);
-       }
-
-       if (proceed && urb->next) {
-               turb = urb->next;
-               do {
-                       if (turb->status != -EINPROGRESS &&
-                           uhci_submit_urb(turb) != 0)
-
-                       turb = turb->next;
-               } while (turb && turb != urb->next);
-
-               if (urb->complete)
-                       urb->complete(urb);
-       }
+       list_del(&urb->urb_list);
+       INIT_LIST_HEAD(&urb->urb_list);
 
-       /* We decrement the usage count after we're done with everything */
-       usb_dec_dev_use(dev);
+       uhci_add_complete(urb);
 }
 
-static int uhci_unlink_generic(struct urb *urb)
+static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
 {
        struct urb_priv *urbp = urb->hcpriv;
-       struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
+       /* We can get called when urbp allocation fails, so check */
        if (!urbp)
-               return -EINVAL;
+               return;
 
        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);
 
-       if (!list_empty(&urbp->urb_queue_list))
-               uhci_delete_queued_urb(uhci, urb);
-
-       uhci_destroy_urb_priv(urb);
-
-       urb->dev = NULL;
-
-       return 0;
+       uhci_delete_queued_urb(uhci, urb);      /* It checks */
 }
 
+/* FIXME: If we forcefully unlink an urb, we should reset the toggle for */
+/*  that pipe to match what actually completed */
 static int uhci_unlink_urb(struct urb *urb)
 {
        struct uhci *uhci;
-       int ret = 0;
        unsigned long flags;
+       struct urb_priv *urbp = urb->hcpriv;
 
        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_unlink_urb(urb);
-
        /* Release bandwidth for Interrupt or Isoc. transfers */
        /* Spinlock needed ? */
        if (urb->bandwidth) {
@@ -1540,13 +1710,28 @@
                }
        }
 
-       if (urb->status == -EINPROGRESS) {
-               uhci_unlink_generic(urb);
+       if (urb->status != -EINPROGRESS)
+               return 0;
+
+       spin_lock_irqsave(&uhci->urb_list_lock, flags);
+       list_del(&urb->urb_list);
+       INIT_LIST_HEAD(&urb->urb_list);
+       spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
+       uhci_unlink_generic(uhci, urb);
+
+       /* Short circuit the virtual root hub */
+       if (urb->dev == uhci->rh.dev) {
+               rh_unlink_urb(urb);
+               uhci_call_completion(urb);
+       } else {
                if (urb->transfer_flags & USB_ASYNC_UNLINK) {
-                       urb->status = -ECONNABORTED;
+                       /* urb_list is available now since we called */
+                       /*  uhci_unlink_generic already */
+
+                       urbp->status = urb->status = -ECONNABORTED;
 
-                       spin_lock_irqsave(&uhci->urb_remove_lock, flags);
+                       spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
 
                        /* Check to see if the remove list is empty */
                        if (list_empty(&uhci->urb_remove_list))
@@ -1554,7 +1739,7 @@
                        
                        list_add(&urb->urb_list, &uhci->urb_remove_list);
 
-                       spin_unlock_irqrestore(&uhci->urb_remove_lock, flags);
+                       spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
                } else {
                        urb->status = -ENOENT;
 
@@ -1567,12 +1752,11 @@
                        } else
                                schedule_timeout(1+1*HZ/1000); 
 
-                       if (urb->complete)
-                               urb->complete(urb);
+                       uhci_call_completion(urb);
                }
        }
 
-       return ret;
+       return 0;
 }
 
 static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb)
@@ -1588,7 +1772,7 @@
        /*  and we'd be turning on FSBR next frame anyway, so it's a wash */
        urbp->fsbr_timeout = 1;
 
-       head = &urbp->list;
+       head = &urbp->td_list;
        tmp = head->next;
        while (tmp != head) {
                struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
@@ -1624,9 +1808,7 @@
        uhci_unlink_urb
 };
 
-/* -------------------------------------------------------------------
-   Virtual Root Hub
-   ------------------------------------------------------------------- */
+/* Virtual Root Hub */
 
 static __u8 root_hub_dev_des[] =
 {
@@ -1700,7 +1882,6 @@
        0xff                    /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
 };
 
-/*-------------------------------------------------------------------------*/
 /* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
 static int rh_send_irq(struct urb *urb)
 {
@@ -1708,6 +1889,7 @@
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
        unsigned int io_addr = uhci->io_addr;
        __u16 data = 0;
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
        for (i = 0; i < uhci->rh.numports; i++) {
                data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 
1)) : 0);
@@ -1715,19 +1897,23 @@
        }
 
        *(__u16 *) urb->transfer_buffer = cpu_to_le16(data);
-       urb->actual_length = len;
-       urb->status = USB_ST_NOERROR;
 
        if ((data > 0) && (uhci->rh.send != 0)) {
                dbg("root-hub INT complete: port1: %x port2: %x data: %x",
                        inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data);
-               urb->complete(urb);
+
+               if (uhci->is_suspended)
+                       wakeup_hc(uhci);
+
+               urb->actual_length = len;
+               urbp->status = 0;
+
+               uhci_call_completion(urb);
        }
 
-       return USB_ST_NOERROR;
+       return 0;
 }
 
-/*-------------------------------------------------------------------------*/
 /* Virtual Root Hub INTs are polled by this timer every "interval" ms */
 static int rh_init_int_timer(struct urb *urb);
 
@@ -1735,41 +1921,46 @@
 {
        struct urb *urb = (struct urb *)ptr;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-       struct list_head *tmp, *head = &uhci->urb_list;
-       struct urb_priv *urbp;
-       int len;
+       struct list_head list, *tmp, *head;
        unsigned long flags;
 
-       if (uhci->rh.send) {
-               len = rh_send_irq(urb);
-               if (len > 0) {
-                       urb->actual_length = len;
-                       if (urb->complete)
-                               urb->complete(urb);
+       if (uhci->rh.send)
+               rh_send_irq(urb);
+
+       INIT_LIST_HEAD(&list);
+
+       spin_lock_irqsave(&uhci->urb_list_lock, flags);
+       head = &uhci->urb_list;
+
+       tmp = head->next;
+       while (tmp != head) {
+               struct urb *u = list_entry(tmp, struct urb, urb_list);
+               struct urb_priv *urbp = (struct urb_priv *)u->hcpriv;
+
+               tmp = tmp->next;
+
+               /* Check if the FSBR timed out */
+               if (urbp->fsbr && time_after_eq(jiffies, urbp->inserttime + 
+IDLE_TIMEOUT))
+                       uhci_fsbr_timeout(uhci, u);
+  
+               /* Check if the URB timed out */
+               if (u->timeout && time_after_eq(jiffies, u->timeout)) {
+                       list_del(&u->urb_list);
+                       list_add_tail(&u->urb_list, &list);
                }
        }
+       spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
-       nested_lock(&uhci->urblist_lock, flags);
+       head = &list;
        tmp = head->next;
        while (tmp != head) {
-               struct urb *u = list_entry(tmp, urb_t, urb_list);
+               struct urb *u = list_entry(tmp, struct urb, urb_list);
 
                tmp = tmp->next;
 
-               urbp = (struct urb_priv *)u->hcpriv;
-               if (urbp) {
-                       /* Check if the FSBR timed out */
-                       if (urbp->fsbr && time_after_eq(jiffies, urbp->inserttime + 
IDLE_TIMEOUT))
-                               uhci_fsbr_timeout(uhci, u);
-
-                       /* Check if the URB timed out */
-                       if (u->timeout && time_after_eq(jiffies, u->timeout)) {
-                               u->transfer_flags |= USB_ASYNC_UNLINK | 
USB_TIMEOUT_KILLED;
-                               uhci_unlink_urb(u);
-                       }
-               }
+               u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
+               uhci_unlink_urb(u);
        }
-       nested_unlock(&uhci->urblist_lock, flags);
 
        /* enter global suspend if nothing connected */
        if (!uhci->is_suspended && !ports_active(uhci))
@@ -1778,7 +1969,6 @@
        rh_init_int_timer(urb);
 }
 
-/*-------------------------------------------------------------------------*/
 /* Root Hub INTs are polled by this timer */
 static int rh_init_int_timer(struct urb *urb)
 {
@@ -1794,7 +1984,6 @@
        return 0;
 }
 
-/*-------------------------------------------------------------------------*/
 #define OK(x)                  len = (x); break
 
 #define CLR_RH_PORTSTAT(x) \
@@ -1808,10 +1997,7 @@
        outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
 
 
-/*-------------------------------------------------------------------------*/
-/*************************
- ** Root Hub Control Pipe
- *************************/
+/* Root Hub Control Pipe */
 
 static int rh_submit_urb(struct urb *urb)
 {
@@ -1822,7 +2008,7 @@
        int leni = urb->transfer_buffer_length;
        int len = 0;
        int status = 0;
-       int stat = USB_ST_NOERROR;
+       int stat = 0;
        int i;
        unsigned int io_addr = uhci->io_addr;
        __u16 cstatus;
@@ -1837,7 +2023,7 @@
                uhci->rh.interval = urb->interval;
                rh_init_int_timer(urb);
 
-               return USB_ST_NOERROR;
+               return -EINPROGRESS;
        }
 
        bmRType_bReq = cmd->requesttype | cmd->request << 8;
@@ -1899,6 +2085,7 @@
        case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
                switch (wValue) {
                case RH_PORT_ENABLE:
+printk("clearing enable on root port\n");
                        CLR_RH_PORTSTAT(USBPORTSC_PE);
                        OK(0);
                case RH_PORT_SUSPEND:
@@ -1907,6 +2094,7 @@
                case RH_PORT_POWER:
                        OK(0);  /* port power */
                case RH_C_PORT_CONNECTION:
+printk("clearing port_connection on root port\n");
                        SET_RH_PORTSTAT(USBPORTSC_CSC);
                        OK(0);
                case RH_C_PORT_ENABLE:
@@ -1945,7 +2133,6 @@
                }
                break;
        case RH_SET_ADDRESS:
-               uhci->rh.devnum = wValue;
                OK(0);
        case RH_GET_DESCRIPTOR:
                switch ((wValue & 0xff00) >> 8) {
@@ -1955,14 +2142,14 @@
                        OK(len);
                case 0x02:      /* configuration descriptor */
                        len = min(leni, min(sizeof(root_hub_config_des), wLength));
-                       memcpy (data, root_hub_config_des, len);
+                       memcpy(data, root_hub_config_des, len);
                        OK(len);
                case 0x03:      /* string descriptors */
-                       len = usb_root_hub_string (wValue & 0xff,
+                       len = usb_root_hub_string(wValue & 0xff,
                                uhci->io_addr, "UHCI-alt",
                                data, wLength);
                        if (len > 0) {
-                               OK (min (leni, len));
+                               OK(min (leni, len));
                        } else 
                                stat = -EPIPE;
                }
@@ -1987,33 +2174,29 @@
        }
 
        urb->actual_length = len;
-       urb->status = stat;
-       if (urb->complete)
-               urb->complete(urb);
 
-       return USB_ST_NOERROR;
+       return stat;
 }
-/*-------------------------------------------------------------------------*/
 
 static int rh_unlink_urb(struct urb *urb)
 {
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
        if (uhci->rh.urb == urb) {
+               urb->status = -ENOENT;
                uhci->rh.send = 0;
+               uhci->rh.urb = NULL;
                del_timer(&uhci->rh.rh_int_timer);
        }
        return 0;
 }
-/*-------------------------------------------------------------------*/
 
-void uhci_free_pending_qhs(struct uhci *uhci)
+static 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);
+       spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
        head = &uhci->qh_remove_list;
        tmp = head->next;
        while (tmp != head) {
@@ -2022,10 +2205,143 @@
                tmp = tmp->next;
 
                list_del(&qh->remove_list);
+               INIT_LIST_HEAD(&qh->remove_list);
 
-               uhci_free_qh(qh);
+               uhci_free_qh(uhci, qh);
        }
-       spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
+       spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
+}
+
+static void uhci_call_completion(struct urb *urb)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct usb_device *dev = urb->dev;
+       int is_ring = 0, killed, resubmit_interrupt, status;
+       struct urb *nurb;
+
+       killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
+                       urb->status == -ECONNRESET);
+       resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
+                       urb->interval && !killed);
+
+       nurb = urb->next;
+       if (nurb && !killed) {
+               /* First loop to check for any other URB's that are killed */
+               int count = 0;
+
+               while (nurb && nurb != urb && count < MAX_URB_LOOP) {
+                       if (nurb->status == -ENOENT ||
+                           nurb->status == -ECONNABORTED ||
+                           nurb->status == -ECONNRESET) {
+                               killed = 1;
+                               break;
+                       }
+
+                       nurb = nurb->next;
+                       count++;
+               }
+
+               if (count == MAX_URB_LOOP)
+                       err("uhci_call_completion: too many linked URB's, loop? (first 
+loop)");
+
+               /* Check to see if chain is a ring */
+               is_ring = (nurb == urb);
+       }
+
+       status = urbp->status;
+       if (!resubmit_interrupt)
+               /* We don't need urb_priv anymore */
+               uhci_destroy_urb_priv(urb);
+
+#if 0
+       nurb = urb->next;
+       if (nurb && !killed) {
+               /* Second loop to submit any other URB's */
+               int count = 0;
+
+               while (nurb && nurb != urb && count < MAX_URB_LOOP) {
+                       if (nurb->status != -EINPROGRESS) {
+                               /* usb_submit_urb since there's no reason */
+                               /*  that this URB doesn't belong to another */
+                               /*  HCD (ohci, ehci, etc) */
+                               if (usb_submit_urb(nurb))
+                                       break;
+                       }
+
+                       nurb = nurb->next;
+                       count++;
+               }
+
+               if (count == MAX_URB_LOOP)
+                       err("uhci_call_completion: too many linked URB's, loop? 
+(second loop)");
+       }
+#endif
+
+       if (!killed)
+               urb->status = status;
+
+       urb->dev = NULL;
+       if (urb->complete)
+               urb->complete(urb);
+
+       if (resubmit_interrupt) {
+               urb->dev = dev;
+               uhci_reset_interrupt(urb);
+       } else {
+               if (is_ring && !killed) {
+                       urb->dev = dev;
+                       uhci_submit_urb(urb);
+               } else {
+                       /* We decrement the usage count after we're done */
+                       /*  with everything */
+                       usb_dec_dev_use(dev);
+               }
+       }
+}
+
+static void uhci_finish_completion(struct uhci *uhci)
+{
+       struct list_head *tmp, *head;
+       unsigned long flags;
+
+       spin_lock_irqsave(&uhci->complete_list_lock, flags);
+       head = &uhci->complete_list;
+       tmp = head->next;
+       while (tmp != head) {
+               struct urb_priv *urbp = list_entry(tmp, struct urb_priv, 
+complete_list);
+               struct urb *urb = urbp->urb;
+
+               tmp = tmp->next;
+
+               list_del(&urbp->complete_list);
+               INIT_LIST_HEAD(&urbp->complete_list);
+
+               uhci_call_completion(urb);
+       }
+       spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
+}
+
+static void uhci_remove_pending_qhs(struct uhci *uhci)
+{
+       struct list_head *tmp, *head;
+       unsigned long flags;
+
+       spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
+       head = &uhci->urb_remove_list;
+       tmp = head->next;
+       while (tmp != head) {
+               struct urb *urb = list_entry(tmp, struct urb, urb_list);
+               struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+               tmp = tmp->next;
+
+               list_del(&urb->urb_list);
+               INIT_LIST_HEAD(&urb->urb_list);
+
+               urbp->status = urb->status = -ECONNRESET;
+               uhci_call_completion(urb);
+       }
+       spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
 }
 
 static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
@@ -2033,7 +2349,6 @@
        struct uhci *uhci = __uhci;
        unsigned int io_addr = uhci->io_addr;
        unsigned short status;
-       unsigned long flags;
        struct list_head *tmp, *head;
 
        /*
@@ -2043,7 +2358,7 @@
        status = inw(io_addr + USBSTS);
        if (!status)    /* shared interrupt, not mine */
                return;
-       outw(status, io_addr + USBSTS);
+       outw(status, io_addr + USBSTS);         /* Clear it */
 
        if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
                if (status & USBSTS_HSE)
@@ -2056,31 +2371,17 @@
                }
        }
 
-       if (status & USBSTS_RD) {
+       if (status & USBSTS_RD)
                wakeup_hc(uhci);
-       }
 
        uhci_free_pending_qhs(uhci);
 
-       spin_lock(&uhci->urb_remove_lock);
-       head = &uhci->urb_remove_list;
-       tmp = head->next;
-       while (tmp != head) {
-               struct urb *urb = list_entry(tmp, struct urb, urb_list);
-
-               tmp = tmp->next;
-
-               list_del(&urb->urb_list);
-
-               if (urb->complete)
-                       urb->complete(urb);
-       }
-       spin_unlock(&uhci->urb_remove_lock);
+       uhci_remove_pending_qhs(uhci);
 
        uhci_clear_next_interrupt(uhci);
 
-       /* Walk the list of pending TD's to see which ones completed */
-       nested_lock(&uhci->urblist_lock, flags);
+       /* Walk the list of pending URB's to see which ones completed */
+       spin_lock(&uhci->urb_list_lock);
        head = &uhci->urb_list;
        tmp = head->next;
        while (tmp != head) {
@@ -2089,12 +2390,14 @@
                tmp = tmp->next;
 
                /* Checks the status and does all of the magic necessary */
-               uhci_transfer_result(urb);
+               uhci_transfer_result(uhci, urb);
        }
-       nested_unlock(&uhci->urblist_lock, flags);
+       spin_unlock(&uhci->urb_list_lock);
+
+       uhci_finish_completion(uhci);
 }
 
-static void reset_hc(struct uhci *uhci)
+static void uhci_reset(struct uhci *uhci)
 {
        unsigned int io_addr = uhci->io_addr;
 
@@ -2148,7 +2451,7 @@
        return connection;
 }
 
-static void start_hc(struct uhci *uhci)
+static void uhci_start(struct uhci *uhci)
 {
        unsigned int io_addr = uhci->io_addr;
        int timeout = 1000;
@@ -2173,12 +2476,43 @@
 
        /* Start at frame 0 */
        outw(0, io_addr + USBFRNUM);
-       outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD);
+       outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
 
        /* Run and mark it configured with a 64-byte max packet */
        outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
 }
 
+static int uhci_alloc_root_hub(struct uhci *uhci)
+{
+       struct usb_device *dev;
+
+       dev = usb_alloc_dev(NULL, uhci->bus);
+       if (!dev)
+               return -1;
+
+       uhci->bus->root_hub = dev;
+       uhci->rh.dev = dev;
+
+       return 0;
+}
+
+static int uhci_start_root_hub(struct uhci *uhci)
+{
+       usb_connect(uhci->rh.dev);
+
+       if (usb_new_device(uhci->rh.dev) != 0) {
+               usb_free_dev(uhci->rh.dev);
+
+               return -1;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+static int uhci_num = 0;
+#endif
+
 /*
  * Allocate a frame list, and then setup the skeleton
  *
@@ -2193,8 +2527,9 @@
  *  - 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)
+static struct uhci *uhci_alloc(struct pci_dev *dev, unsigned int io_addr, unsigned 
+int io_size)
 {
+       dma_addr_t dma_handle;
        int i, port;
        struct uhci *uhci;
        struct usb_bus *bus;
@@ -2205,36 +2540,60 @@
 
        memset(uhci, 0, sizeof(*uhci));
 
+       uhci->dev = dev;
        uhci->irq = -1;
        uhci->io_addr = io_addr;
        uhci->io_size = io_size;
 
-       spin_lock_init(&uhci->qh_remove_lock);
+       /* Initialize some lists/spinlocks */
+       spin_lock_init(&uhci->qh_remove_list_lock);
        INIT_LIST_HEAD(&uhci->qh_remove_list);
 
-       spin_lock_init(&uhci->urb_remove_lock);
+       spin_lock_init(&uhci->urb_remove_list_lock);
        INIT_LIST_HEAD(&uhci->urb_remove_list);
 
-       nested_init(&uhci->urblist_lock);
+       spin_lock_init(&uhci->urb_list_lock);
        INIT_LIST_HEAD(&uhci->urb_list);
 
-       spin_lock_init(&uhci->framelist_lock);
+       spin_lock_init(&uhci->complete_list_lock);
+       INIT_LIST_HEAD(&uhci->complete_list);
+
+       spin_lock_init(&uhci->frame_list_lock);
 
        /* We need exactly one page (per UHCI specs), how convenient */
        /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
-       uhci->fl = (void *)__get_free_page(GFP_KERNEL);
-       if (!uhci->fl)
-               goto au_free_uhci;
+       uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle);
+       if (!uhci->fl) {
+               printk(KERN_ERR "Unable to allocate consistent memory for frame 
+list\n");
+               goto free_uhci;
+       }
+
+       memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
+
+       uhci->fl->dma_handle = dma_handle;
+
+       uhci->td_pool = pci_pool_create("uhci_td", uhci->dev,
+               sizeof(struct uhci_td), 16, 0, GFP_DMA);
+       if (!uhci->td_pool) {
+               printk(KERN_ERR "Unable to create td pci_pool\n");
+               goto free_fl;
+       }
+
+       uhci->qh_pool = pci_pool_create("uhci_qh", uhci->dev,
+               sizeof(struct uhci_qh), 16, 0, GFP_DMA);
+       if (!uhci->qh_pool) {
+               printk(KERN_ERR "Unable to create qh pci_pool\n");
+               goto free_td_pool;
+       }
 
        bus = usb_alloc_bus(&uhci_device_operations);
        if (!bus)
-               goto au_free_fl;
+               goto free_qh_pool;
 
        uhci->bus = bus;
        bus->hcpriv = uhci;
 
        /* Initialize the root hub */
-
        /* UHCI specs says devices must have 2 ports, but goes on to say */
        /*  they may have more but give no way to determine how many they */
        /*  have. However, according to the UHCI spec, Bit 7 is always set */
@@ -2258,36 +2617,66 @@
 
        uhci->rh.numports = port;
 
+       if (uhci_alloc_root_hub(uhci)) {
+               err("unable to allocate root hub");
+               goto free_fl;
+       }
+
+       uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh.dev);
+       if (!uhci->skeltd[0]) {
+               err("unable to allocate TD 0");
+               goto free_fl;
+       }
+
        /*
         * 9 Interrupt queues; link int2 to int1, int4 to int2, etc
         * then link int1 to control and control to bulk
         */
        for (i = 1; i < 9; i++) {
-               struct uhci_td *td = &uhci->skeltd[i];
+               struct uhci_td *td;
+
+               td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh.dev);
+               if (!td) {
+                       err("unable to allocate TD %d", i);
+                       goto free_tds;
+               }
 
                uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | 
USB_PID_IN, 0);
-               td->link = virt_to_bus(&uhci->skeltd[i - 1]);
+               td->link = uhci->skeltd[i - 1]->dma_handle;
        }
 
+       uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh.dev);
+       if (!uhci->skel_term_td) {
+               err("unable to allocate TD 0");
+               goto free_fl;
+       }
+
+       for (i = 0; i < UHCI_NUM_SKELQH; i++) {
+               uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh.dev);
+               if (!uhci->skelqh[i]) {
+                       err("unable to allocate QH %d", i);
+                       goto free_qhs;
+               }
+       }
 
-       uhci_fill_td(&uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) 
| USB_PID_IN, 0);
-       uhci->skel_int1_td.link = virt_to_bus(&uhci->skel_ls_control_qh) | UHCI_PTR_QH;
+       uhci_fill_td(uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) 
+| USB_PID_IN, 0);
+       uhci->skel_int1_td->link = uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH;
 
-       uhci->skel_ls_control_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | 
UHCI_PTR_QH;
-       uhci->skel_ls_control_qh.element = UHCI_PTR_TERM;
+       uhci->skel_ls_control_qh->link = uhci->skel_hs_control_qh->dma_handle | 
+UHCI_PTR_QH;
+       uhci->skel_ls_control_qh->element = UHCI_PTR_TERM;
 
-       uhci->skel_hs_control_qh.link = virt_to_bus(&uhci->skel_bulk_qh) | UHCI_PTR_QH;
-       uhci->skel_hs_control_qh.element = UHCI_PTR_TERM;
+       uhci->skel_hs_control_qh->link = uhci->skel_bulk_qh->dma_handle | UHCI_PTR_QH;
+       uhci->skel_hs_control_qh->element = UHCI_PTR_TERM;
 
-       uhci->skel_bulk_qh.link = virt_to_bus(&uhci->skel_term_qh) | UHCI_PTR_QH;
-       uhci->skel_bulk_qh.element = UHCI_PTR_TERM;
+       uhci->skel_bulk_qh->link = uhci->skel_term_qh->dma_handle | 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_fill_td(uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) 
+| USB_PID_IN, 0);
+       uhci->skel_term_td->link = uhci->skel_term_td->dma_handle;
 
-       uhci->skel_term_qh.link = UHCI_PTR_TERM;
-       uhci->skel_term_qh.element = virt_to_bus(&uhci->skel_term_td);
+       uhci->skel_term_qh->link = UHCI_PTR_TERM;
+       uhci->skel_term_qh->element = uhci->skel_term_td->dma_handle;
 
        /*
         * Fill the frame list: make all entries point to
@@ -2297,8 +2686,8 @@
         * scatter the interrupt queues in a way that gives
         * us a reasonable dynamic range for irq latencies.
         */
-       for (i = 0; i < 1024; i++) {
-               struct uhci_td *irq = &uhci->skel_int1_td;
+       for (i = 0; i < UHCI_NUMFRAMES; i++) {
+               int irq = 0;
 
                if (i & 1) {
                        irq++;
@@ -2322,7 +2711,7 @@
                }
 
                /* Only place we don't use the frame list routines */
-               uhci->fl->frame[i] =  virt_to_bus(irq);
+               uhci->fl->frame[i] =  uhci->skeltd[irq]->dma_handle;
        }
 
        return uhci;
@@ -2330,9 +2719,29 @@
 /*
  * error exits:
  */
-au_free_fl:
-       free_page((unsigned long)uhci->fl);
-au_free_uhci:
+free_qhs:
+       for (i = 0; i < UHCI_NUM_SKELQH; i++)
+               if (uhci->skelqh[i]) {
+                       uhci_free_qh(uhci, uhci->skelqh[i]);
+                       uhci->skelqh[i] = NULL;
+               }
+free_tds:
+       for (i = 0; i < UHCI_NUM_SKELTD; i++)
+               if (uhci->skeltd[i]) {
+                       uhci_free_td(uhci, uhci->skeltd[i]);
+                       uhci->skeltd[i] = NULL;
+               }
+
+free_qh_pool:
+       pci_pool_destroy(uhci->qh_pool);
+
+free_td_pool:
+       pci_pool_destroy(uhci->td_pool);
+
+free_fl:
+       pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, 
+uhci->fl->dma_handle);
+
+free_uhci:
        kfree(uhci);
 
        return NULL;
@@ -2341,51 +2750,80 @@
 /*
  * De-allocate all resources..
  */
-static void release_uhci(struct uhci *uhci)
+static void uhci_release(struct uhci *uhci)
 {
+       unsigned long flags;
+       int i;
+#ifdef CONFIG_PROC_FS
+       char buf[8];
+#endif
+
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+       for (i = 0; i < UHCI_NUMFRAMES; i++) {
+               uhci->fl->frame[i] =  UHCI_PTR_TERM;
+               uhci->fl->frame_cpu[i] = NULL;
+       }
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+
+       for (i = 0; i < UHCI_NUM_SKELQH; i++)
+               if (uhci->skelqh[i]) {
+                       uhci_free_qh(uhci, uhci->skelqh[i]);
+                       uhci->skelqh[i] = NULL;
+               }
+
+       for (i = 0; i < UHCI_NUM_SKELTD; i++)
+               if (uhci->skeltd[i]) {
+                       uhci_free_td(uhci, uhci->skeltd[i]);
+                       uhci->skeltd[i] = NULL;
+               }
+
+       release_region(uhci->io_addr, uhci->io_size);
+
        if (uhci->irq >= 0) {
                free_irq(uhci->irq, uhci);
                uhci->irq = -1;
        }
 
-       if (uhci->fl) {
-               free_page((unsigned long)uhci->fl);
-               uhci->fl = NULL;
-       }
+#ifdef CONFIG_PROC_FS
+       sprintf(buf, "hc%d", uhci->num);
 
-       usb_free_bus(uhci->bus);
-       kfree(uhci);
+       remove_proc_entry(buf, uhci_proc_root);
+       uhci->proc_entry = NULL;
+#endif
 }
 
-int uhci_start_root_hub(struct uhci *uhci)
+static void uhci_free(struct uhci *uhci)
 {
-       struct usb_device *dev;
-
-       dev = usb_alloc_dev(NULL, uhci->bus);
-       if (!dev)
-               return -1;
-
-       uhci->bus->root_hub = dev;
-       usb_connect(dev);
+       if (uhci->qh_pool) {
+               pci_pool_destroy(uhci->qh_pool);
+               uhci->qh_pool = NULL;
+       }
 
-       if (usb_new_device(dev) != 0) {
-               usb_free_dev(dev);
+       if (uhci->td_pool) {
+               pci_pool_destroy(uhci->td_pool);
+               uhci->td_pool = NULL;
+       }
 
-               return -1;
+       if (uhci->fl) {
+               pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, 
+uhci->fl->dma_handle);
+               uhci->fl = NULL;
        }
 
-       return 0;
+       usb_free_bus(uhci->bus);
+       kfree(uhci);
 }
 
 /*
- * If we've successfully found a UHCI, now is the time to increment the
- * module usage count, and return success..
+ * If we've successfully found a UHCI, now is the time to return success..
  */
-static int setup_uhci(struct pci_dev *dev, int irq, unsigned int io_addr, unsigned 
int io_size)
+static int uhci_found(struct pci_dev *dev, int irq, unsigned int io_addr, unsigned 
+int io_size)
 {
        int retval;
        struct uhci *uhci;
        char buf[8], *bufp = buf;
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry *ent;
+#endif
 
 #ifndef __sparc__
        sprintf(buf, "%d", irq);
@@ -2395,17 +2833,34 @@
        printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
                io_addr, bufp);
 
-       uhci = alloc_uhci(io_addr, io_size);
+       uhci = uhci_alloc(dev, io_addr, io_size);
        if (!uhci)
                return -ENOMEM;
+
        dev->driver_data = uhci;
 
+#ifdef CONFIG_PROC_FS
+       uhci->num = uhci_num++;
+
+       sprintf(buf, "hc%d", uhci->num);
+
+       ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
+       if (!ent)
+               return -ENOMEM;
+
+       ent->data = uhci;
+       ent->proc_fops = &uhci_proc_operations;
+       ent->size = 0;
+       uhci->proc_entry = ent;
+#endif
+
        request_region(uhci->io_addr, io_size, "usb-uhci");
 
-       reset_hc(uhci);
+       uhci_reset(uhci);
 
        usb_register_bus(uhci->bus);
-       start_hc(uhci);
+
+       uhci_start(uhci);
 
        retval = -EBUSY;
        if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci) == 0) {
@@ -2418,10 +2873,12 @@
        }
 
        /* Couldn't allocate IRQ if we got here */
+       uhci_reset(uhci);
+       uhci_release(uhci);
 
-       reset_hc(uhci);
-       release_region(uhci->io_addr, uhci->io_size);
-       release_uhci(uhci);
+       usb_deregister_bus(uhci->bus);
+
+       uhci_free(uhci);
 
        return retval;
 }
@@ -2430,6 +2887,12 @@
 {
        int i;
 
+       if (!pci_dma_supported(dev, 0xFFFFFFFF)) {
+               err("PCI subsystem doesn't support 32 bit addressing?");
+               return -ENODEV;
+       }
+       dev->dma_mask = 0xFFFFFFFF;
+
        /* disable legacy emulation */
        pci_write_config_word(dev, USBLEGSUP, 0);
 
@@ -2455,7 +2918,8 @@
                        break;
 
                pci_set_master(dev);
-               return setup_uhci(dev, dev->irq, io_addr, io_size);
+
+               return uhci_found(dev, dev->irq, io_addr, io_size);
        }
 
        return -ENODEV;
@@ -2468,31 +2932,32 @@
        if (uhci->bus->root_hub)
                usb_disconnect(&uhci->bus->root_hub);
 
-       usb_deregister_bus(uhci->bus);
+       /* At this point, we're pretty much guaranteed that no new */
+       /*  connects can be made to this bus since there are no more */
+       /*  parents */
+       uhci_free_pending_qhs(uhci);
+       uhci_remove_pending_qhs(uhci);
 
-       reset_hc(uhci);
-       release_region(uhci->io_addr, uhci->io_size);
+       uhci_reset(uhci);
+       uhci_release(uhci);
 
-       uhci_free_pending_qhs(uhci);
+       usb_deregister_bus(uhci->bus);
 
-       release_uhci(uhci);
+       uhci_free(uhci);
 }
 
 static void uhci_pci_suspend(struct pci_dev *dev)
 {
-       reset_hc((struct uhci *) dev->driver_data);
+       uhci_reset((struct uhci *)dev->driver_data);
 }
 
 static void uhci_pci_resume(struct pci_dev *dev)
 {
-       reset_hc((struct uhci *) dev->driver_data);
-       start_hc((struct uhci *) dev->driver_data);
+       uhci_reset((struct uhci *)dev->driver_data);
+       uhci_start((struct uhci *)dev->driver_data);
 }
 
-/*-------------------------------------------------------------------------*/
-
-static const struct pci_device_id __devinitdata uhci_pci_ids [] = { {
-
+static const struct pci_device_id __devinitdata uhci_pci_ids[] = { {
        /* handle any USB UHCI controller */
        class:          ((PCI_CLASS_SERIAL_USB << 8) | 0x00),
        class_mask:     ~0,
@@ -2520,38 +2985,29 @@
        resume:         uhci_pci_resume,
 #endif /* PM */
 };
-
  
 static int __init uhci_hcd_init(void)
 {
-       int retval;
-
-       retval = -ENOMEM;
+       int retval = -ENOMEM;
 
-       /* We throw all of the TD's and QH's into a kmem cache */
-       /* TD's and QH's need to be 16 byte aligned and SLAB_HWCACHE_ALIGN */
-       /*  does this for us */
-       uhci_td_cachep = kmem_cache_create("uhci_td",
-               sizeof(struct uhci_td), 0,
-               SLAB_HWCACHE_ALIGN, NULL, NULL);
-
-       if (!uhci_td_cachep)
-               goto td_failed;
-
-       uhci_qh_cachep = kmem_cache_create("uhci_qh",
-               sizeof(struct uhci_qh), 0,
-               SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (debug) {
+               errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
+               if (!errbuf)
+                       goto errbuf_failed;
+       }
 
-       if (!uhci_qh_cachep)
-               goto qh_failed;
+#ifdef CONFIG_PROC_FS
+       uhci_proc_root = create_proc_entry("uhci", S_IFDIR, 0);
+       if (!uhci_proc_root)
+               goto proc_failed;
+#endif
 
        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 = pci_module_init (&uhci_pci_driver);
+       retval = pci_module_init(&uhci_pci_driver);
        if (retval)
                goto init_failed;
 
@@ -2562,29 +3018,31 @@
                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");
+#ifdef CONFIG_PROC_FS
+       remove_proc_entry("uhci", 0);
 
-qh_failed:
-       if (kmem_cache_destroy(uhci_td_cachep))
-               printk(KERN_INFO "uhci: not all TD's were freed\n");
+proc_failed:
+#endif
+       if (errbuf)
+               kfree(errbuf);
+
+errbuf_failed:
 
-td_failed:
        return retval;
 }
 
 static void __exit uhci_hcd_cleanup (void) 
 {
-       pci_unregister_driver (&uhci_pci_driver);
+       pci_unregister_driver(&uhci_pci_driver);
        
        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");
-
-       if (kmem_cache_destroy(uhci_td_cachep))
-               printk(KERN_INFO "uhci: not all TD's were freed\n");
+#ifdef CONFIG_PROC_FS
+       remove_proc_entry("uhci", 0);
+#endif
+       if (errbuf)
+               kfree(errbuf);
 }
 
 module_init(uhci_hcd_init);
diff -urN -X dontdiff linux-2.4.3-pre8.orig/drivers/usb/uhci.h 
linux-2.4.3-pre8/drivers/usb/uhci.h
--- linux-2.4.3-pre8.orig/drivers/usb/uhci.h    Mon Mar 26 11:21:55 2001
+++ linux-2.4.3-pre8/drivers/usb/uhci.h Mon Mar 26 15:39:26 2001
@@ -5,36 +5,6 @@
 #include <linux/usb.h>
 
 /*
- * This nested spinlock code is courtesy of Davide Libenzi <[EMAIL PROTECTED]>
- */
-struct s_nested_lock {
-       spinlock_t lock;
-       void *uniq;
-       short int count;
-};
-
-#define nested_init(snl) \
-       spin_lock_init(&(snl)->lock); \
-       (snl)->uniq = NULL; \
-       (snl)->count = 0;
-
-#define nested_lock(snl, flags) \
-       if ((snl)->uniq == current) { \
-               (snl)->count++; \
-               flags = 0; /* No warnings */ \
-       } else { \
-               spin_lock_irqsave(&(snl)->lock, flags); \
-               (snl)->count++; \
-               (snl)->uniq = current; \
-       }
-
-#define nested_unlock(snl, flags) \
-       if (!--(snl)->count) { \
-               (snl)->uniq = NULL; \
-               spin_unlock_irqrestore(&(snl)->lock, flags); \
-       }
-
-/*
  * Universal Host Controller Interface data structures and defines
  */
 
@@ -97,24 +67,29 @@
 #define UHCI_MAX_SOF_NUMBER    2047    /* in an SOF packet */
 #define CAN_SCHEDULE_FRAMES    1000    /* how far future frames can be scheduled */
 
-struct uhci_framelist {
+struct uhci_frame_list {
        __u32 frame[UHCI_NUMFRAMES];
-} __attribute__((aligned(4096)));
 
-struct uhci_td;
+       dma_addr_t dma_handle;
+
+       void *frame_cpu[UHCI_NUMFRAMES];
+};
+
+struct urb_priv;
 
 struct uhci_qh {
        /* Hardware fields */
        __u32 link;                             /* Next queue */
-       __u32 element;                          /* Queue element pointer */
+       volatile __u32 element;                 /* Queue element pointer */
 
        /* Software fields */
-       /* Can't use list_head since we want a specific order */
+       dma_addr_t dma_handle;
        struct usb_device *dev;                 /* The owning device */
 
-       struct uhci_qh *prevqh, *nextqh;
+       struct urb_priv *urbp;
 
-       struct list_head remove_list;
+       struct list_head list;                  /* P: uhci->frame_list_lock */
+       struct list_head remove_list;           /* P: uhci->remove_list_lock */
 } __attribute__((aligned(16)));
 
 /*
@@ -141,8 +116,6 @@
 #define uhci_status_bits(ctrl_sts)     (ctrl_sts & 0xFE0000)
 #define uhci_actual_length(ctrl_sts)   ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 
1-based */
 
-#define uhci_ptr_to_virt(x)    bus_to_virt(x & ~UHCI_PTR_BITS)
-
 /*
  * for TD <info>: (a.k.a. Token)
  */
@@ -170,7 +143,8 @@
  * On 64-bit machines we probably want to take advantage of the fact that
  * hw doesn't really care about the size of the sw-only area.
  *
- * Alas, not anymore, we have more than 4 words for software, woops
+ * Alas, not anymore, we have more than 4 words for software, woops.
+ * Everything still works tho, surprise! -jerdfelt
  */
 struct uhci_td {
        /* Hardware fields */
@@ -180,13 +154,14 @@
        __u32 buffer;
 
        /* Software fields */
-       unsigned int *frameptr;         /* Frame list pointer */
-       struct uhci_td *prevtd, *nexttd; /* Previous and next TD in queue */
+       dma_addr_t dma_handle;
+       int frame;
 
        struct usb_device *dev;
        struct urb *urb;                /* URB this TD belongs to */
 
-       struct list_head list;
+       struct list_head list;          /* P: urb->lock */
+       struct list_head fl_list;       /* P: frame_list_lock */
 } __attribute__((aligned(16)));
 
 /*
@@ -289,8 +264,8 @@
 }
 
 struct virt_root_hub {
-       int devnum;             /* Address of Root Hub endpoint */
-       void *urb;
+       struct usb_device *dev;
+       struct urb *urb;
        void *int_addr;
        int send;
        int interval;
@@ -306,6 +281,12 @@
  * a subset of what the full implementation needs.
  */
 struct uhci {
+       struct pci_dev *dev;
+
+       /* procfs */
+       int num;
+       struct proc_dir_entry *proc_entry;
+
        /* Grabbed from PCI */
        int irq;
        unsigned int io_addr;
@@ -313,32 +294,44 @@
 
        struct list_head uhci_list;
 
+       struct pci_pool *qh_pool;
+       struct pci_pool *td_pool;
+
        struct usb_bus *bus;
 
-       struct uhci_td skeltd[UHCI_NUM_SKELTD]; /* Skeleton TD's */
-       struct uhci_qh skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
+       struct uhci_td *skeltd[UHCI_NUM_SKELTD];        /* Skeleton TD's */
+       struct uhci_qh *skelqh[UHCI_NUM_SKELQH];        /* Skeleton QH's */
+
+       spinlock_t frame_list_lock;
+       struct uhci_frame_list *fl;     /* Frame list */
+       int fsbr;                       /* Full speed bandwidth reclamation */
 
-       spinlock_t framelist_lock;
-       struct uhci_framelist *fl;              /* Frame list */
-       int fsbr;                               /* Full speed bandwidth reclamation */
        int is_suspended;
 
-       spinlock_t qh_remove_lock;
+       spinlock_t qh_remove_list_lock;
        struct list_head qh_remove_list;
 
-       spinlock_t urb_remove_lock;
+       spinlock_t urb_remove_list_lock;
        struct list_head urb_remove_list;
 
-       struct s_nested_lock urblist_lock;
+       spinlock_t urb_list_lock;
        struct list_head urb_list;
 
+       spinlock_t complete_list_lock;
+       struct list_head complete_list;
+
        struct virt_root_hub rh;        /* private data of the virtual root hub */
 };
 
 struct urb_priv {
        struct urb *urb;
+       struct usb_device *dev;
+
+       dma_addr_t setup_packet_dma_handle;     /* DMA address */
+       dma_addr_t transfer_buffer_dma_handle;  /* DMA address */
 
        struct uhci_qh *qh;             /* QH for this URB */
+       struct list_head td_list;       /* List of TD's (if !qh) */
 
        int fsbr : 1;                   /* URB turned on FSBR */
        int fsbr_timeout : 1;           /* URB timed out on FSBR */
@@ -347,11 +340,17 @@
                                        /*  a control transfer, retrigger */
                                        /*  the status phase */
 
-       unsigned long inserttime;       /* In jiffies */
+       int status;                     /* Final status */
 
-       struct list_head list;
+       unsigned long inserttime;       /* In jiffies */
 
-       struct list_head urb_queue_list;        /* URB's linked together */
+       /* P: urb->lock */
+       struct list_head queue_list;            /* URB's linked together */
+       struct list_head complete_list;         /* URB's to be completed */
+
+       int element_entry;
+       unsigned long elements[100];
+       unsigned long elements_time[100];
 };
 
 /* -------------------------------------------------------------------------
@@ -408,16 +407,6 @@
 #define RH_ACK                 0x01
 #define RH_REQ_ERR             -1
 #define RH_NACK                        0x00
-
-/* needed for the debugging code */
-struct uhci_td *uhci_link_to_td(unsigned int element);
-
-/* Debugging code */
-void uhci_show_td(struct uhci_td *td);
-void uhci_show_status(struct uhci *uhci);
-void uhci_show_urb_queue(struct urb *urb);
-void uhci_show_queue(struct uhci_qh *qh);
-void uhci_show_queues(struct uhci *uhci);
 
 #endif
 

Reply via email to