Another patch:

- Changes all urb_t references to struct urb to make code cleaner
- Removes native linked list routines for tracking TD's in URB's. Uses
  list_head and list_add_tail now
- Use list_empty instead of checking pointers
- Don't delay unlink QH if it wasn't linked in the first place
- Force interrupt next frame if delaying unlinking of QH and/or URB's
  (Sometimes QH's, and more importantly URB's, would sit around for a
  while before being properly unlinked and the callback being made)
- Fix deadlock in uhci_destroy_urb_priv. If a seemingly impossible
  set of conditions are met, then we would exit before releasing the
  spinlock, causing a deadlock next time around. I don't think anyone
  ran into this
- Little bit more error reporting in failure circumstances
- Use some more macros instead of magic numbers (in shifts, etc)
- Allow zero length bulk packets. Dunno if this is useful or not
- Implement USB_QUEUE_BULK support (UNTESTED!)
- Disallow multiple URB's to one endpoint simultaneously unless using
  USB_QUEUE_BULK
- Set USB_TIMEOUT_KILLED on a timeout, to inform the client that it was
  unlinked because of a timeout
- {GET,SET}_INTERFACE support for the virtual root hub (from my userspace
  driver binding patch)

There's a couple of features I haven't tested yet because there are no
drivers for devices I can get my hands on which utilize them.

Such as timeouts for URB's, USB_QUEUE_BULK and USB_ASYNC_UNLINK. If anyone
has any drivers (other than dabusb) which utilizes these features, please
tell me so I can test them out and fix any bugs.

This patch is against 2.3.99-pre4-5.

Randy, please send this to Linus.

JE

--- linux-2.3.99-pre4-5.orig/drivers/usb/uhci.c Mon Apr 10 11:43:44 2000
+++ linux-2.3.99-pre4-5/drivers/usb/uhci.c      Mon Apr 10 13:43:23 2000
@@ -56,11 +56,11 @@
 
 static LIST_HEAD(uhci_list);
 
-static int rh_submit_urb(urb_t *urb);
-static int rh_unlink_urb(urb_t *urb);
+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(urb_t *urb);
-static int uhci_unlink_urb(urb_t *urb);
+static int uhci_unlink_generic(struct urb *urb);
+static int uhci_unlink_urb(struct urb *urb);
 
 #define min(a,b) (((a)<(b))?(a):(b))
 
@@ -77,9 +77,8 @@
 
 static int uhci_free_dev(struct usb_device *dev)
 {
-       urb_t *u;
        struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
-       struct list_head *tmp, *next, *head = &uhci->urb_list;
+       struct list_head *tmp, *head = &uhci->urb_list;
        unsigned long flags;
 
        /* Walk through the entire URB list and forcefully remove any */
@@ -87,14 +86,12 @@
        nested_lock(&uhci->urblist_lock, flags);
        tmp = head->next;
        while (tmp != head) {
-               u = list_entry(tmp, urb_t, urb_list);
+               struct urb *u = list_entry(tmp, struct urb, urb_list);
 
-               next = tmp->next;
+               tmp = tmp->next;
 
                if (u->dev == dev)
                        uhci_unlink_urb(u);
-
-               tmp = next;
        }
        nested_unlock(&uhci->urblist_lock, flags);
 
@@ -115,13 +112,31 @@
        unsigned long flags;
 
        nested_lock(&uhci->urblist_lock, flags);
-       if (urb->urb_list.next != &urb->urb_list) {
+       if (!list_empty(&urb->urb_list)) {
                list_del(&urb->urb_list);
                INIT_LIST_HEAD(&urb->urb_list);
        }
        nested_unlock(&uhci->urblist_lock, flags);
 }
 
+void uhci_set_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);
+}
+
+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);
+}
+
 static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
 {
        struct uhci_td *td;
@@ -135,8 +150,8 @@
 
        td->frameptr = NULL;
        td->nexttd = td->prevtd = NULL;
-       td->list.next = td->list.prev = NULL;
        td->dev = dev;
+       INIT_LIST_HEAD(&td->list);
 
        usb_inc_dev_use(dev);
 
@@ -235,23 +250,32 @@
  */
 static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
 {
+       struct list_head *tmp, *head;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
        struct uhci_td *td, *prevtd;
 
        if (!urbp)
                return;
 
-       td = urbp->list.begin;
-       if (!td)
+       head = &urbp->list;
+       tmp = head->next;
+       if (head == tmp)
                return;
 
+       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);
 
        prevtd = td;
 
        /* Then link the rest of the TD's */
-       for (td = td->list.next; td; td = td->list.next) {
+       tmp = tmp->next;
+       while (tmp != head) {
+               td = list_entry(tmp, struct uhci_td, list);
+
+               tmp = tmp->next;
+
                prevtd->link = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
 
                prevtd = td;
@@ -260,9 +284,45 @@
        prevtd->link = UHCI_PTR_TERM;
 }
 
+/* This function will append one URB's QH to another URB's QH. This is for */
+/*  USB_QUEUE_BULK support */
+static void uhci_append_urb_qh(struct uhci *uhci, struct urb *eurb, struct urb *urb)
+{
+       struct urb *nurb;
+       struct urb_priv *eurbp, *urbp, *nurbp;
+       struct list_head *tmp;
+       struct uhci_td *td, *ntd;
+       unsigned long flags;
+
+       eurbp = eurb->hcpriv;
+       urbp = urb->hcpriv;
+
+       spin_lock_irqsave(&eurb->lock, flags);
+
+       /* Grab the last URB in the queue */
+       tmp = eurbp->urb_queue_list.prev;
+
+       /* Add this one to the end */
+       list_add_tail(&urbp->urb_queue_list, &eurbp->urb_queue_list);
+
+       spin_unlock_irqrestore(&eurb->lock, flags);
+
+       nurbp = list_entry(tmp, struct urb_priv, urb_queue_list);
+       nurb = nurbp->urb;
+
+       tmp = nurbp->list.prev;
+       td = list_entry(tmp, struct uhci_td, list);
+
+       tmp = urbp->list.next;
+       ntd = list_entry(tmp, struct uhci_td, list);
+
+       /* No breadth since this will only be called for bulk transfers */
+       td->link = virt_to_bus(ntd);
+}
+
 static void uhci_free_td(struct uhci_td *td)
 {
-       if (td->list.next || td->list.prev)
+       if (!list_empty(&td->list))
                dbg("td is still in URB list!");
 
        kmem_cache_free(uhci_td_cachep, td);
@@ -322,6 +382,10 @@
 static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
 {
        unsigned long flags;
+       int delayed;
+
+       /* If the QH isn't queued, then we don't need to delay unlink it */
+       delayed = (qh->prevqh || qh->nextqh);
 
        spin_lock_irqsave(&uhci->framelist_lock, flags);
        if (qh->prevqh) {
@@ -333,9 +397,20 @@
        qh->prevqh = qh->nextqh = NULL;
        spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 
-       spin_lock_irqsave(&uhci->qh_remove_lock, flags);
-       list_add(&qh->remove_list, &uhci->qh_remove_list);
-       spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
+       if (delayed) {
+               spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+
+               /* 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);
+
+               /* Add it */
+               list_add(&qh->remove_list, &uhci->qh_remove_list);
+
+               spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
+       } else
+               uhci_free_qh(qh);
 }
 
 struct urb_priv *uhci_alloc_urb_priv(struct urb *urb)
@@ -348,91 +423,86 @@
 
        memset((void *)urbp, 0, sizeof(*urbp));
 
-       urbp->list.begin = urbp->list.end = NULL;
+       urbp->inserttime = jiffies;
+       urbp->urb = urb;
+       
+       INIT_LIST_HEAD(&urbp->list);
+       INIT_LIST_HEAD(&urbp->urb_queue_list);
 
        urb->hcpriv = urbp;
 
-       urbp->inserttime = jiffies;
-
        usb_inc_dev_use(urb->dev);
 
        return urbp;
 }
 
-static void uhci_add_td_to_urb(urb_t *urb, struct uhci_td *td)
+static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
 {
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
        td->urb = urb;
 
-       if (!urbp->list.begin)
-               urbp->list.begin = td;
-
-       if (urbp->list.end) {
-               urbp->list.end->list.next = td;
-               td->list.prev = urbp->list.end;
-       }
-       urbp->list.end = td;
+       list_add_tail(&td->list, &urbp->list);
 }
 
-static void uhci_remove_td_from_urb(urb_t *urb, struct uhci_td *td)
+static void uhci_remove_td_from_urb(struct urb *urb, struct uhci_td *td)
 {
-       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       urb = NULL;     /* No warnings */
 
-       if (!urbp->list.begin && !urbp->list.end)
+       if (list_empty(&td->list))
                return;
 
-       if (td->list.prev)
-               td->list.prev->list.next = td->list.next;
-       else
-               urbp->list.begin = td->list.next;
+       list_del(&td->list);
+       INIT_LIST_HEAD(&td->list);
 
-       if (td->list.next)
-               td->list.next->list.prev = td->list.prev;
-       else
-               urbp->list.end = td->list.prev;
-
-       td->list.next = td->list.prev = NULL;
        td->urb = NULL;
 }
 
-static void uhci_destroy_urb_priv(urb_t *urb)
+static void uhci_destroy_urb_priv(struct urb *urb)
 {
+       struct list_head *tmp, *head;
        struct urb_priv *urbp;
        struct uhci *uhci;
-       struct uhci_td *td, *nexttd;
+       struct uhci_td *td;
        unsigned long flags;
 
        spin_lock_irqsave(&urb->lock, flags);
 
        urbp = (struct urb_priv *)urb->hcpriv;
        if (!urbp)
-               return;
+               goto unlock;
 
        if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
-               return;
+               goto unlock;
 
        uhci = urb->dev->bus->hcpriv;
 
-       td = urbp->list.begin;
-       while (td) {
-               nexttd = td->list.next;
+       head = &urbp->list;
+       tmp = head->next;
+       while (tmp != head) {
+               td = list_entry(tmp, struct uhci_td, list);
+
+               tmp = tmp->next;
 
                uhci_remove_td_from_urb(urb, td);
 
                uhci_remove_td(uhci, td);
 
                uhci_free_td(td);
-               
-               td = nexttd;
+       }
+
+       if (!list_empty(&urbp->urb_queue_list)) {
+               list_del(&urbp->urb_queue_list);
+               INIT_LIST_HEAD(&urbp->urb_queue_list);
        }
 
        urb->hcpriv = NULL;
        kmem_cache_free(uhci_up_cachep, urbp);
 
-       spin_unlock_irqrestore(&urb->lock, flags);
-
        usb_dec_dev_use(urb->dev);
+
+unlock:
+       spin_unlock_irqrestore(&urb->lock, flags);
 }
 
 static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
@@ -508,7 +578,7 @@
 /*
  * Control transfers
  */
-static int uhci_submit_control(urb_t *urb)
+static int uhci_submit_control(struct urb *urb)
 {
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -602,10 +672,12 @@
        if (urb->pipe & TD_CTRL_LS) {
                uhci_insert_tds_in_qh(qh, urb, 0);
                uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, qh);
+               urbp->queued = 0;
        } else {
                uhci_insert_tds_in_qh(qh, urb, 1);
                uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh);
                uhci_inc_fsbr(uhci, urb);
+               urbp->queued = 0;
        }
 
        urbp->qh = qh;
@@ -617,10 +689,11 @@
        return -EINPROGRESS;
 }
 
-static int usb_control_retrigger_status(urb_t *urb);
+static int usb_control_retrigger_status(struct urb *urb);
 
-static int uhci_result_control(urb_t *urb)
+static int uhci_result_control(struct urb *urb)
 {
+       struct list_head *tmp, *head;
        struct urb_priv *urbp = urb->hcpriv;
        struct uhci_td *td;
        unsigned int status;
@@ -628,13 +701,16 @@
        if (!urbp)
                return -EINVAL;
 
-       td = urbp->list.begin;
-       if (!td)
+       head = &urbp->list;
+       tmp = head->next;
+       if (head == tmp)
                return -EINVAL;
 
        if (urbp->short_control_packet)
                goto status_phase;
 
+       td = list_entry(tmp, struct uhci_td, list);
+
        /* The first TD is the SETUP phase, check the status, but skip */
        /*  the count */
        status = uhci_status_bits(td->status);
@@ -646,10 +722,13 @@
 
        urb->actual_length = 0;
 
-       td = td->list.next;
-
        /* The rest of the TD's (but the last) are data */
-       while (td && td->list.next) {
+       tmp = tmp->next;
+       while (tmp != head && tmp->next != head) {
+               td = list_entry(tmp, struct uhci_td, list);
+
+               tmp = tmp->next;
+
                status = uhci_status_bits(td->status);
                if (status & TD_CTRL_ACTIVE)
                        return -EINPROGRESS;
@@ -664,18 +743,18 @@
 
                if (status)
                        goto td_error;
-
-               td = td->list.next;
        }
 
 status_phase:
+       td = list_entry(tmp, struct uhci_td, list);
+
        /* Control status phase */
        status = uhci_status_bits(td->status);
 
        /* APC BackUPS Pro kludge */
        /* It tries to send all of the descriptor instead of the amount */
        /*  we requested */
-       if (td->status & TD_CTRL_IOC &&
+       if (td->status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */
            status & TD_CTRL_ACTIVE &&
            status & TD_CTRL_NAK)
                return 0;
@@ -704,34 +783,37 @@
        return uhci_map_status(status, uhci_packetout(td->info));
 }
 
-static int usb_control_retrigger_status(urb_t *urb)
+static int usb_control_retrigger_status(struct urb *urb)
 {
+       struct list_head *tmp, *head;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
        struct uhci *uhci = urb->dev->bus->hcpriv;
-       struct uhci_td *td, *nexttd;
 
        urbp->short_control_packet = 1;
 
        /* Create a new QH to avoid pointer overwriting problems */
        uhci_remove_qh(uhci, urbp->qh);
 
-       urbp->qh = uhci_alloc_qh(urb->dev);
        /* Delete all of the TD's except for the status TD at the end */
-       td = urbp->list.begin;
-       while (td && td->list.next) {
-               nexttd = td->list.next;
+       head = &urbp->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(uhci, td);
 
                uhci_free_td(td);
-               
-               td = nexttd;
        }
 
-       if (!urbp->qh)
+       urbp->qh = uhci_alloc_qh(urb->dev);
+       if (!urbp->qh) {
+               err("unable to allocate new QH for control retrigger");
                return -ENOMEM;
+       }
 
        /* One TD, who cares about Breadth first? */
        uhci_insert_tds_in_qh(urbp->qh, urb, 0);
@@ -741,6 +823,7 @@
                uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
        else
                uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+       urbp->queued = 0;
 
        return -EINPROGRESS;
 }
@@ -748,7 +831,7 @@
 /*
  * Interrupt transfers
  */
-static int uhci_submit_interrupt(urb_t *urb)
+static int uhci_submit_interrupt(struct urb *urb)
 {
        struct uhci_td *td;
        unsigned long destination, status;
@@ -782,18 +865,25 @@
        return -EINPROGRESS;
 }
 
-static int uhci_result_interrupt(urb_t *urb)
+static int uhci_result_interrupt(struct urb *urb)
 {
+       struct list_head *tmp, *head;
        struct urb_priv *urbp = urb->hcpriv;
-       struct uhci_td *td;
        unsigned int status;
+       struct uhci_td *td;
 
        if (!urbp)
                return -EINVAL;
 
        urb->actual_length = 0;
 
-       for (td = urbp->list.begin; td; td = td->list.next) {
+       head = &urbp->list;
+       tmp = head->next;
+       while (tmp != head) {
+               td = list_entry(tmp, struct uhci_td, list);
+
+               tmp = tmp->next;
+
                status = uhci_status_bits(td->status);
                if (status & TD_CTRL_ACTIVE)
                        return -EINPROGRESS;
@@ -836,15 +926,17 @@
        return uhci_map_status(status, uhci_packetout(td->info));
 }
 
-static void uhci_reset_interrupt(urb_t *urb)
+static void uhci_reset_interrupt(struct urb *urb)
 {
+       struct list_head *tmp;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
        struct uhci_td *td;
 
        if (!urbp)
                return;
 
-       td = urbp->list.begin;
+       tmp = urbp->list.next;
+       td = list_entry(tmp, struct uhci_td, list);
        if (!td)
                return;
 
@@ -859,7 +951,7 @@
 /*
  * Bulk transfers
  */
-static int uhci_submit_bulk(urb_t *urb)
+static int uhci_submit_bulk(struct urb *urb, struct urb *eurb)
 {
        struct uhci_td *td;
        struct uhci_qh *qh;
@@ -881,14 +973,14 @@
        destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
 
        /* 3 errors */
-       status = TD_CTRL_ACTIVE | (3 << 27);
+       status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT);
        if (!(urb->transfer_flags & USB_DISABLE_SPD))
                status |= TD_CTRL_SPD;
 
        /*
         * Build the DATA TD's
         */
-       while (len > 0) {
+       do {    /* Allow zero length packets */
                int pktsze = len;
 
                if (pktsze > maxsze)
@@ -912,16 +1004,24 @@
 
                usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
                        usb_pipeout(urb->pipe));
-       }
+       } while (len > 0);
 
        qh = uhci_alloc_qh(urb->dev);
        if (!qh)
                return -ENOMEM;
 
+       urbp->qh = qh;
+
+       /* Always assume depth first */
        uhci_insert_tds_in_qh(qh, urb, 1);
 
-       uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);
-       urbp->qh = qh;
+       if (urb->transfer_flags & USB_QUEUE_BULK && eurb) {
+               uhci_append_urb_qh(uhci, eurb, urb);
+               urbp->queued = 1;
+       } else {
+               uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);
+               urbp->queued = 0;
+       }
 
        uhci_add_urb_list(uhci, urb);
 
@@ -936,9 +1036,9 @@
 /*
  * Isochronous transfers
  */
-static int isochronous_find_limits(urb_t *urb, unsigned int *start, unsigned int *end)
+static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int 
+*end)
 {
-       urb_t *u, *last_urb = NULL;
+       struct urb *last_urb = NULL;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
        struct list_head *tmp, *head = &uhci->urb_list;
        int ret = 0;
@@ -947,7 +1047,7 @@
        nested_lock(&uhci->urblist_lock, flags);
        tmp = head->next;
        while (tmp != head) {
-               u = list_entry(tmp, urb_t, urb_list);
+               struct urb *u = list_entry(tmp, struct urb, urb_list);
 
                /* look for pending URB's with identical pipe handle */
                if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
@@ -970,7 +1070,7 @@
        return ret;
 }
 
-static int isochronous_find_start(urb_t *urb)
+static int isochronous_find_start(struct urb *urb)
 {
        int limits;
        unsigned int start = 0, end = 0;
@@ -996,7 +1096,7 @@
        return 0;
 }
 
-static int uhci_submit_isochronous(urb_t *urb)
+static int uhci_submit_isochronous(struct urb *urb)
 {
        struct uhci_td *td;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -1034,10 +1134,10 @@
        return -EINPROGRESS;
 }
 
-static int uhci_result_isochronous(urb_t *urb)
+static int uhci_result_isochronous(struct urb *urb)
 {
+       struct list_head *tmp, *head;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-       struct uhci_td *td;
        int status;
        int i, ret = 0;
 
@@ -1046,9 +1146,15 @@
 
        urb->actual_length = 0;
 
-       for (i = 0, td = urbp->list.begin; td; i++, td = td->list.next) {
+       i = 0;
+       head = &urbp->list;
+       tmp = head->next;
+       while (tmp != head) {
+               struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
                int actlength;
 
+               tmp = tmp->next;
+
                if (td->status & TD_CTRL_ACTIVE)
                        return -EINPROGRESS;
 
@@ -1062,16 +1168,47 @@
                        urb->error_count++;
                        ret = status;
                }
+
+               i++;
        }
 
        return ret;
 }
 
-static int uhci_submit_urb(urb_t *urb)
+static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
+{
+       struct list_head *tmp, *head = &uhci->urb_list;
+       unsigned long flags;
+       struct urb *u = NULL;
+
+       if (usb_pipeisoc(urb->pipe))
+               return NULL;
+
+       nested_lock(&uhci->urblist_lock, flags);
+       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;
+       }
+       u = NULL;
+
+found:
+       nested_unlock(&uhci->urblist_lock, flags);
+
+       return u;
+}
+
+static int uhci_submit_urb(struct urb *urb)
 {
        int ret = -EINVAL;
        struct uhci *uhci;
        unsigned long flags;
+       struct urb *u;
 
        if (!urb)
                return -EINVAL;
@@ -1085,6 +1222,10 @@
        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;
+
        spin_lock_irqsave(&urb->lock, flags);
 
        if (!uhci_alloc_urb_priv(urb)) {
@@ -1100,7 +1241,7 @@
                ret = uhci_submit_interrupt(urb);
                break;
        case PIPE_BULK:
-               ret = uhci_submit_bulk(urb);
+               ret = uhci_submit_bulk(urb, u);
                break;
        case PIPE_ISOCHRONOUS:
                ret = uhci_submit_isochronous(urb);
@@ -1124,9 +1265,9 @@
  *
  * Must be called with urblist_lock acquired
  */
-static void uhci_transfer_result(urb_t *urb)
+static void uhci_transfer_result(struct urb *urb)
 {
-       urb_t *turb;
+       struct urb *turb;
        int proceed = 0, is_ring = 0;
        int ret = -EINVAL;
        unsigned long flags;
@@ -1206,7 +1347,7 @@
        }
 }
 
-static int uhci_unlink_generic(urb_t *urb)
+static int uhci_unlink_generic(struct urb *urb)
 {
        struct urb_priv *urbp = urb->hcpriv;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -1222,12 +1363,21 @@
                /* The interrupt loop will reclaim the QH's */
                uhci_remove_qh(uhci, urbp->qh);
 
+       if (!list_empty(&urbp->urb_queue_list)) {
+               struct list_head *tmp = urbp->urb_queue_list.next;
+               struct urb_priv *nurbp = list_entry(tmp, struct urb_priv, 
+urb_queue_list);
+               if (nurbp->queued) {
+                       uhci_insert_qh(uhci, &uhci->skel_bulk_qh, nurbp->qh);
+                       nurbp->queued = 0;
+               }
+       }
+
        uhci_destroy_urb_priv(urb);
 
        return 0;
 }
 
-static int uhci_unlink_urb(urb_t *urb)
+static int uhci_unlink_urb(struct urb *urb)
 {
        struct uhci *uhci;
        int ret = 0;
@@ -1252,7 +1402,13 @@
                        urb->status = -ECONNABORTED;
 
                        spin_lock_irqsave(&uhci->urb_remove_lock, flags);
+
+                       /* Check to see if the remove list is empty */
+                       if (list_empty(&uhci->urb_remove_list))
+                               uhci_set_next_interrupt(uhci);
+                       
                        list_add(&urb->urb_list, &uhci->urb_remove_list);
+
                        spin_unlock_irqrestore(&uhci->urb_remove_lock, flags);
                } else {
                        urb->status = -ENOENT;
@@ -1372,7 +1528,7 @@
 
 /*-------------------------------------------------------------------------*/
 /* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
-static int rh_send_irq(urb_t *urb)
+static int rh_send_irq(struct urb *urb)
 {
        int i, len = 1;
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -1399,7 +1555,7 @@
 
 /*-------------------------------------------------------------------------*/
 /* Virtual Root Hub INTs are polled by this timer every "interval" ms */
-static int rh_init_int_timer(urb_t *urb);
+static int rh_init_int_timer(struct urb *urb);
 
 static void rh_int_timer_do(unsigned long ptr)
 {
@@ -1434,7 +1590,7 @@
 
                        /* Check if the URB timed out */
                        if (u->timeout && time_after(u->timeout, jiffies)) {
-                               u->transfer_flags |= USB_ASYNC_UNLINK;
+                               u->transfer_flags |= USB_ASYNC_UNLINK | 
+USB_TIMEOUT_KILLED;
                                uhci_unlink_urb(u);
                        }
                }
@@ -1446,7 +1602,7 @@
 
 /*-------------------------------------------------------------------------*/
 /* Root Hub INTs are polled by this timer */
-static int rh_init_int_timer(urb_t *urb)
+static int rh_init_int_timer(struct urb *urb)
 {
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
@@ -1479,7 +1635,7 @@
  ** Root Hub Control Pipe
  *************************/
 
-static int rh_submit_urb(urb_t *urb)
+static int rh_submit_urb(struct urb *urb)
 {
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
        unsigned int pipe = urb->pipe;
@@ -1643,6 +1799,11 @@
                OK(1);
        case RH_SET_CONFIGURATION:
                OK(0);
+       case RH_GET_INTERFACE | RH_INTERFACE:
+               *(__u8 *)data = 0x00;
+               OK(1);
+       case RH_SET_INTERFACE | RH_INTERFACE:
+               OK(0);
        default:
                stat = -EPIPE;
        }
@@ -1656,7 +1817,7 @@
 }
 /*-------------------------------------------------------------------------*/
 
-static int rh_unlink_urb(urb_t *urb)
+static int rh_unlink_urb(struct urb *urb)
 {
        struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
@@ -1735,6 +1896,8 @@
        }
        spin_unlock(&uhci->urb_remove_lock);
 
+       uhci_clear_next_interrupt(uhci);
+
        /* Walk the list of pending TD's to see which ones completed */
        nested_lock(&uhci->urblist_lock, flags);
        head = &uhci->urb_list;
@@ -2144,7 +2307,7 @@
 
        /* We only want to return an error code if ther was an error */
        /*  and we didn't find a UHCI controller */
-       if (retval && uhci_list.next == &uhci_list)
+       if (retval && list_empty(&uhci_list))
                goto init_failed;
 
        return 0;
--- linux-2.3.99-pre4-5.orig/drivers/usb/uhci.h Tue Mar 14 17:48:14 2000
+++ linux-2.3.99-pre4-5/drivers/usb/uhci.h      Mon Apr 10 13:15:41 2000
@@ -97,6 +97,10 @@
 #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 {
+       __u32 frame[UHCI_NUMFRAMES];
+} __attribute__((aligned(4096)));
+
 struct uhci_td;
 
 struct uhci_qh {
@@ -106,22 +110,19 @@
 
        /* Software fields */
        /* Can't use list_head since we want a specific order */
-       struct uhci_qh *prevqh, *nextqh;
-
        struct usb_device *dev;                 /* The owning device */
 
+       struct uhci_qh *prevqh, *nextqh;
+
        struct list_head remove_list;
 } __attribute__((aligned(16)));
 
-struct uhci_framelist {
-       __u32 frame[UHCI_NUMFRAMES];
-} __attribute__((aligned(4096)));
-
 /*
  * for TD <status>:
  */
 #define TD_CTRL_SPD            (1 << 29)       /* Short Packet Detect */
 #define TD_CTRL_C_ERR_MASK     (3 << 27)       /* Error Counter bits */
+#define TD_CTRL_C_ERR_SHIFT    27
 #define TD_CTRL_LS             (1 << 26)       /* Low Speed Device */
 #define TD_CTRL_IOS            (1 << 25)       /* Isochronous Select */
 #define TD_CTRL_IOC            (1 << 24)       /* Interrupt on Complete */
@@ -184,10 +185,8 @@
 
        struct usb_device *dev;
        struct urb *urb;                /* URB this TD belongs to */
-       /* We can't use list_head since we need a specific order */
-       struct ut_list {
-               struct uhci_td *prev, *next;
-       } list;
+
+       struct list_head list;
 } __attribute__((aligned(16)));
 
 /*
@@ -336,9 +335,12 @@
 };
 
 struct urb_priv {
+       struct urb *urb;
+
        struct uhci_qh *qh;             /* QH for this URB */
 
-       int fsbr;                       /* Did this URB turn on FSBR? */
+       int fsbr : 1;                   /* Did this URB turn on FSBR? */
+       int queued : 1;                 /* 0 if QH was linked in */
 
        char short_control_packet;      /* If we get a short packet during */
                                        /*  a control transfer, retrigger */
@@ -346,15 +348,16 @@
 
        unsigned long inserttime;       /* In jiffies */
 
-       struct up_list {
-               struct uhci_td *begin, *end;
-       } list;
+       struct list_head list;
+
+       struct list_head urb_queue_list;        /* URB's linked together */
 };
 
 /* -------------------------------------------------------------------------
    Virtual Root HUB
    ------------------------------------------------------------------------- */
 /* destination of request */
+#define RH_DEVICE              0x00
 #define RH_INTERFACE           0x01
 #define RH_ENDPOINT            0x02
 #define RH_OTHER               0x03

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to