diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2005-12-17 17:58:46 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-03-20 14:49:57 -0800 |
commit | dccf4a48d47120a42382ba526f1a0848c13ba2a4 (patch) | |
tree | 788a0a9f491d1a42df1dee1781156ccfc363b6ef /drivers/usb/host/uhci-q.c | |
parent | 499003e815344304c7b0c93aad923ddf644d24e0 (diff) | |
download | op-kernel-dev-dccf4a48d47120a42382ba526f1a0848c13ba2a4.zip op-kernel-dev-dccf4a48d47120a42382ba526f1a0848c13ba2a4.tar.gz |
[PATCH] UHCI: use one QH per endpoint, not per URB
This patch (as623) changes the uhci-hcd driver to make it use one QH per
device endpoint, instead of a QH per URB as it does now. Numerous areas
of the code are affected by this. For example, the distinction between
"queued" URBs and non-"queued" URBs no longer exists; all URBs belong to
a queue and some just happen to be at the queue's head.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/uhci-q.c')
-rw-r--r-- | drivers/usb/host/uhci-q.c | 985 |
1 files changed, 456 insertions, 529 deletions
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 7823980..b1b551a 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -13,13 +13,9 @@ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) - * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu + * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu */ -static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb); -static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb); -static void uhci_remove_pending_urbps(struct uhci_hcd *uhci); -static void uhci_free_pending_qhs(struct uhci_hcd *uhci); static void uhci_free_pending_tds(struct uhci_hcd *uhci); /* @@ -30,7 +26,7 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci); * games with the FSBR code to make sure we get the correct order in all * the cases. I don't think it's worth the effort */ -static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) +static void uhci_set_next_interrupt(struct uhci_hcd *uhci) { if (uhci->is_stopped) mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); @@ -42,12 +38,6 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC); } -static inline void uhci_moveto_complete(struct uhci_hcd *uhci, - struct urb_priv *urbp) -{ - list_move_tail(&urbp->urb_list, &uhci->complete_list); -} - static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci) { dma_addr_t dma_handle; @@ -71,6 +61,18 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci) return td; } +static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) +{ + if (!list_empty(&td->list)) + dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); + if (!list_empty(&td->remove_list)) + dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td); + if (!list_empty(&td->fl_list)) + dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); + + dma_pool_free(uhci->td_pool, td, td->dma_handle); +} + static inline void uhci_fill_td(struct uhci_td *td, u32 status, u32 token, u32 buffer) { @@ -82,7 +84,8 @@ static inline void uhci_fill_td(struct uhci_td *td, u32 status, /* * We insert Isochronous URBs directly into the frame list at the beginning */ -static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum) +static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci, + struct uhci_td *td, unsigned framenum) { framenum &= (UHCI_NUMFRAMES - 1); @@ -108,7 +111,7 @@ static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, } } -static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci, +static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci, struct uhci_td *td) { /* If it's not inserted, don't remove it */ @@ -139,48 +142,68 @@ static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci, td->frame = -1; } -static void unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb) +/* + * Remove all the TDs for an Isochronous URB from the frame list + */ +static void uhci_unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb) { struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; struct uhci_td *td; list_for_each_entry(td, &urbp->td_list, list) - uhci_remove_td_frame_list(uhci, td); + uhci_remove_td_from_frame_list(uhci, td); wmb(); } /* - * Inserts a td list into qh. + * Remove an URB's TDs from the hardware schedule */ -static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth) +static void uhci_remove_tds_from_schedule(struct uhci_hcd *uhci, + struct urb *urb, int status) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - __le32 *plink; + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; - /* Ordering isn't important here yet since the QH hasn't been */ - /* inserted into the schedule yet */ - plink = &qh->element; - list_for_each_entry(td, &urbp->td_list, list) { - *plink = cpu_to_le32(td->dma_handle) | breadth; - plink = &td->link; + /* Isochronous TDs get unlinked directly from the frame list */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + uhci_unlink_isochronous_tds(uhci, urb); + return; } - *plink = UHCI_PTR_TERM; -} -static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) -{ - if (!list_empty(&td->list)) - dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); - if (!list_empty(&td->remove_list)) - dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td); - if (!list_empty(&td->fl_list)) - dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); + /* If the URB isn't first on its queue, adjust the link pointer + * of the last TD in the previous URB. */ + if (urbp->node.prev != &urbp->qh->queue) { + struct urb_priv *purbp; + struct uhci_td *ptd, *ltd; + + if (status == -EINPROGRESS) + status = 0; + purbp = list_entry(urbp->node.prev, struct urb_priv, node); + ptd = list_entry(purbp->td_list.prev, struct uhci_td, + list); + ltd = list_entry(urbp->td_list.prev, struct uhci_td, + list); + ptd->link = ltd->link; + } - dma_pool_free(uhci->td_pool, td, td->dma_handle); + /* If the URB completed with an error, then the QH element certainly + * points to one of the URB's TDs. If it completed normally then + * the QH element has certainly moved on to the next URB. And if + * the URB is still in progress then it must have been dequeued. + * The QH element either hasn't reached it yet or is somewhere in + * the middle. If the URB wasn't first we can assume that it + * hasn't started yet (see above): Otherwise all the preceding URBs + * would have completed and been removed from the queue, so this one + * _would_ be first. + * + * If the QH element is inside this URB, clear it. It will be + * set properly when the QH is activated. + */ + if (status < 0) + urbp->qh->element = UHCI_PTR_TERM; } -static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci) +static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, + struct usb_device *udev, struct usb_host_endpoint *hep) { dma_addr_t dma_handle; struct uhci_qh *qh; @@ -194,256 +217,120 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci) qh->element = UHCI_PTR_TERM; qh->link = UHCI_PTR_TERM; - qh->urbp = NULL; + INIT_LIST_HEAD(&qh->queue); + INIT_LIST_HEAD(&qh->node); - INIT_LIST_HEAD(&qh->list); - INIT_LIST_HEAD(&qh->remove_list); + if (udev) { /* Normal QH */ + qh->state = QH_STATE_IDLE; + qh->hep = hep; + qh->udev = udev; + hep->hcpriv = qh; + usb_get_dev(udev); + } else { /* Skeleton QH */ + qh->state = QH_STATE_ACTIVE; + qh->udev = NULL; + } return qh; } static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { - if (!list_empty(&qh->list)) + WARN_ON(qh->state != QH_STATE_IDLE && qh->udev); + if (!list_empty(&qh->queue)) dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh); - if (!list_empty(&qh->remove_list)) - dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh); + list_del(&qh->node); + if (qh->udev) { + qh->hep->hcpriv = NULL; + usb_put_dev(qh->udev); + } dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); } /* - * Append this urb's qh after the last qh in skelqh->list - * - * Note that urb_priv.queue_list doesn't have a separate queue head; - * it's a ring with every element "live". + * Put a QH on the schedule in both hardware and software */ -static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb) +static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct urb_priv *turbp; - struct uhci_qh *lqh; + struct uhci_qh *pqh; - /* Grab the last QH */ - lqh = list_entry(skelqh->list.prev, struct uhci_qh, list); + WARN_ON(list_empty(&qh->queue)); - /* Point to the next skelqh */ - urbp->qh->link = lqh->link; - wmb(); /* Ordering is important */ + /* Set the element pointer if it isn't set already. + * This isn't needed for Isochronous queues, but it doesn't hurt. */ + if (qh_element(qh) == UHCI_PTR_TERM) { + struct urb_priv *urbp = list_entry(qh->queue.next, + struct urb_priv, node); + struct uhci_td *td = list_entry(urbp->td_list.next, + struct uhci_td, list); - /* - * Patch QHs for previous endpoint's queued URBs? HC goes - * here next, not to the next skelqh it now points to. - * - * lqh --> td ... --> qh ... --> td --> qh ... --> td - * | | | - * v v v - * +<----------------+-----------------+ - * v - * newqh --> td ... --> td - * | - * v - * ... - * - * The HC could see (and use!) any of these as we write them. - */ - lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; - if (lqh->urbp) { - list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list) - turbp->qh->link = lqh->link; + qh->element = cpu_to_le32(td->dma_handle); } - list_add_tail(&urbp->qh->list, &skelqh->list); + if (qh->state == QH_STATE_ACTIVE) + return; + qh->state = QH_STATE_ACTIVE; + + /* Move the QH from its old list to the end of the appropriate + * skeleton's list */ + list_move_tail(&qh->node, &qh->skel->node); + + /* Link it into the schedule */ + pqh = list_entry(qh->node.prev, struct uhci_qh, node); + qh->link = pqh->link; + wmb(); + pqh->link = UHCI_PTR_QH | cpu_to_le32(qh->dma_handle); } /* - * Start removal of QH from schedule; it finishes next frame. - * TDs should be unlinked before this is called. + * Take a QH off the hardware schedule */ -static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) +static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; - __le32 newlink; - if (!qh) + if (qh->state == QH_STATE_UNLINKING) return; + WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev); + qh->state = QH_STATE_UNLINKING; - /* - * Only go through the hoops if it's actually linked in - */ - if (!list_empty(&qh->list)) { - - /* If our queue is nonempty, make the next URB the head */ - if (!list_empty(&qh->urbp->queue_list)) { - struct urb_priv *nurbp; - - nurbp = list_entry(qh->urbp->queue_list.next, - struct urb_priv, queue_list); - nurbp->queued = 0; - list_add(&nurbp->qh->list, &qh->list); - newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; - } else - newlink = qh->link; - - /* Fix up the previous QH's queue to link to either - * the new head of this queue or the start of the - * next endpoint's queue. */ - pqh = list_entry(qh->list.prev, struct uhci_qh, list); - pqh->link = newlink; - if (pqh->urbp) { - struct urb_priv *turbp; - - list_for_each_entry(turbp, &pqh->urbp->queue_list, - queue_list) - turbp->qh->link = newlink; - } - wmb(); - - /* Leave qh->link in case the HC is on the QH now, it will */ - /* continue the rest of the schedule */ - qh->element = UHCI_PTR_TERM; - - list_del_init(&qh->list); - } - - list_del_init(&qh->urbp->queue_list); - qh->urbp = NULL; + /* Unlink the QH from the schedule and record when we did it */ + pqh = list_entry(qh->node.prev, struct uhci_qh, node); + pqh->link = qh->link; + mb(); uhci_get_current_frame_number(uhci); - if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) { - uhci_free_pending_qhs(uhci); - uhci->qh_remove_age = uhci->frame_number; - } + qh->unlink_frame = uhci->frame_number; - /* 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)) + /* Force an interrupt so we know when the QH is fully unlinked */ + if (list_empty(&uhci->skel_unlink_qh->node)) uhci_set_next_interrupt(uhci); - list_add(&qh->remove_list, &uhci->qh_remove_list); + /* Move the QH from its old list to the end of the unlinking list */ + list_move_tail(&qh->node, &uhci->skel_unlink_qh->node); } -static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - - list_for_each_entry(td, &urbp->td_list, list) { - if (toggle) - td->token |= cpu_to_le32(TD_TOKEN_TOGGLE); - else - td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE); - - toggle ^= 1; - } - - return toggle; -} - -/* This function will append one URB's QH to another URB's QH. This is for */ -/* queuing interrupt, control or bulk transfers */ -static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb) -{ - struct urb_priv *eurbp, *urbp, *furbp, *lurbp; - struct uhci_td *lltd; - - eurbp = eurb->hcpriv; - urbp = urb->hcpriv; - - /* Find the first URB in the queue */ - furbp = eurbp; - if (eurbp->queued) { - list_for_each_entry(furbp, &eurbp->queue_list, queue_list) - if (!furbp->queued) - break; - } - - lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); - - lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); - - /* Control transfers always start with toggle 0 */ - if (!usb_pipecontrol(urb->pipe)) - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), - uhci_fixup_toggle(urb, - uhci_toggle(td_token(lltd)) ^ 1)); - - /* All qhs in the queue need to link to the next queue */ - urbp->qh->link = eurbp->qh->link; - - wmb(); /* Make sure we flush everything */ - - lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; - - list_add_tail(&urbp->queue_list, &furbp->queue_list); - - urbp->queued = 1; -} - -static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) +/* + * When we and the controller are through with a QH, it becomes IDLE. + * This happens when a QH has been off the schedule (on the unlinking + * list) for more than one frame, or when an error occurs while adding + * the first URB onto a new QH. + */ +static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh) { - struct urb_priv *urbp, *nurbp, *purbp, *turbp; - struct uhci_td *pltd; - unsigned int toggle; - - urbp = urb->hcpriv; - - if (list_empty(&urbp->queue_list)) - return; - - nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); - - /* - * Fix up the toggle for the following URBs in the queue. - * Only needed for bulk and interrupt: control and isochronous - * endpoints don't propagate toggles between messages. - */ - if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) { - if (!urbp->queued) - /* We just set the toggle in uhci_unlink_generic */ - toggle = usb_gettoggle(urb->dev, - usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); - else { - /* If we're in the middle of the queue, grab the */ - /* toggle from the TD previous to us */ - 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(td_token(pltd)) ^ 1; - } - - list_for_each_entry(turbp, &urbp->queue_list, queue_list) { - if (!turbp->queued) - break; - toggle = uhci_fixup_toggle(turbp->urb, toggle); - } + WARN_ON(qh->state == QH_STATE_ACTIVE); - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - } - - if (urbp->queued) { - /* We're somewhere in the middle (or end). The case where - * we're at the head is handled in uhci_remove_qh(). */ - 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) - pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; - else - /* The next URB happens to be the beginning, so */ - /* we're the last, end the chain */ - pltd->link = UHCI_PTR_TERM; - } + list_move(&qh->node, &uhci->idle_qh_list); + qh->state = QH_STATE_IDLE; - /* urbp->queue_list is handled in uhci_remove_qh() */ + /* If anyone is waiting for a QH to become idle, wake them up */ + if (uhci->num_waiting) + wake_up_all(&uhci->waitqh); } -static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb) +static inline struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, + struct urb *urb) { struct urb_priv *urbp; @@ -453,17 +340,14 @@ static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *u memset((void *)urbp, 0, sizeof(*urbp)); - urbp->fsbrtime = jiffies; urbp->urb = urb; + urb->hcpriv = urbp; + urbp->fsbrtime = jiffies; + INIT_LIST_HEAD(&urbp->node); INIT_LIST_HEAD(&urbp->td_list); - INIT_LIST_HEAD(&urbp->queue_list); INIT_LIST_HEAD(&urbp->urb_list); - list_add_tail(&urbp->urb_list, &uhci->urb_list); - - urb->hcpriv = urbp; - return urbp; } @@ -482,18 +366,17 @@ static void uhci_remove_td_from_urb(struct uhci_td *td) list_del_init(&td->list); } -static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) +static void uhci_free_urb_priv(struct uhci_hcd *uhci, + struct urb_priv *urbp) { struct uhci_td *td, *tmp; - struct urb_priv *urbp; - - urbp = (struct urb_priv *)urb->hcpriv; - if (!urbp) - return; if (!list_empty(&urbp->urb_list)) - dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list " - "or uhci->remove_list!\n", urb); + dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list!\n", + urbp->urb); + if (!list_empty(&urbp->node)) + dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n", + urbp->urb); uhci_get_current_frame_number(uhci); if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) { @@ -502,7 +385,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) } /* Check to see if the remove list is empty. Set the IOC bit */ - /* to force an interrupt so we can remove the TDs*/ + /* to force an interrupt so we can remove the TDs. */ if (list_empty(&uhci->td_remove_list)) uhci_set_next_interrupt(uhci); @@ -511,7 +394,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) list_add(&td->remove_list, &uhci->td_remove_list); } - urb->hcpriv = NULL; + urbp->urb->hcpriv = NULL; kmem_cache_free(uhci_up_cachep, urbp); } @@ -568,17 +451,82 @@ static int uhci_map_status(int status, int dir_out) } /* + * Fix up the data toggles for URBs in a queue, when one of them + * terminates early (short transfer, error, or dequeued). + */ +static void uhci_fixup_toggles(struct urb *urb) +{ + struct list_head *head; + struct uhci_td *td; + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; + int prevactive = 0; + unsigned int toggle = 0; + struct urb_priv *turbp, *list_end; + + /* + * We need to find out what the last successful toggle was so + * we can update the data toggles for the following transfers. + * + * There are 2 ways the last successful completed TD is found: + * + * 1) The TD is NOT active and the actual length < expected length + * 2) The TD is NOT active and it's the last TD in the chain + * + * and a third way the first uncompleted TD is found: + * + * 3) The TD is active and the previous TD is NOT active + */ + head = &urbp->td_list; + list_for_each_entry(td, head, list) { + unsigned int ctrlstat = td_status(td); + + if (!(ctrlstat & TD_CTRL_ACTIVE) && + (uhci_actual_length(ctrlstat) < + uhci_expected_length(td_token(td)) || + td->list.next == head)) + toggle = uhci_toggle(td_token(td)) ^ 1; + else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive) + toggle = uhci_toggle(td_token(td)); + + prevactive = ctrlstat & TD_CTRL_ACTIVE; + } + + /* + * Fix up the toggle for the following URBs in the queue. + * + * We can stop as soon as we find an URB with toggles set correctly, + * because then all the following URBs will be correct also. + */ + list_end = list_entry(&urbp->qh->queue, struct urb_priv, node); + turbp = urbp; + while ((turbp = list_entry(turbp->node.next, struct urb_priv, node)) + != list_end) { + td = list_entry(turbp->td_list.next, struct uhci_td, list); + if (uhci_toggle(td_token(td)) == toggle) + return; + + list_for_each_entry(td, &turbp->td_list, list) { + td->token ^= __constant_cpu_to_le32(TD_TOKEN_TOGGLE); + toggle ^= 1; + } + } + + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), toggle); +} + +/* * Control transfers */ -static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_td *td; - struct uhci_qh *qh, *skelqh; unsigned long destination, status; - int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; + __le32 *plink; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; @@ -597,7 +545,8 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(8), - urb->setup_dma); + urb->setup_dma); + plink = &td->link; /* * If direction is "send", change the packet ID from SETUP (0x2D) @@ -615,21 +564,20 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur * Build the DATA TDs */ while (len > 0) { - int pktsze = len; - - if (pktsze > maxsze) - pktsze = maxsze; + int pktsze = min(len, maxsze); td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; + *plink = cpu_to_le32(td->dma_handle); /* Alternate Data0/1 (start with Data1) */ destination ^= TD_TOKEN_TOGGLE; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(pktsze), - data); + data); + plink = &td->link; data += pktsze; len -= pktsze; @@ -641,6 +589,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; + *plink = cpu_to_le32(td->dma_handle); /* * It's IN if the pipe is an output pipe or we're not expecting @@ -658,16 +607,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status | TD_CTRL_IOC, - destination | uhci_explen(0), 0); - - qh = uhci_alloc_qh(uhci); - if (!qh) - return -ENOMEM; - - urbp->qh = qh; - qh->urbp = urbp; - - uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); + destination | uhci_explen(0), 0); /* Low-speed transfers get a different queue, and won't hog the bus. * Also, some devices enumerate better without FSBR; the easiest way @@ -675,18 +615,13 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur * isn't in the CONFIGURED state. */ if (urb->dev->speed == USB_SPEED_LOW || urb->dev->state != USB_STATE_CONFIGURED) - skelqh = uhci->skel_ls_control_qh; + qh->skel = uhci->skel_ls_control_qh; else { - skelqh = uhci->skel_fs_control_qh; + qh->skel = uhci->skel_fs_control_qh; uhci_inc_fsbr(uhci, urb); } - if (eurb) - uhci_append_queued_urb(uhci, eurb, urb); - else - uhci_insert_qh(uhci, skelqh, urb); - - return -EINPROGRESS; + return 0; } /* @@ -703,7 +638,7 @@ static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb) struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_td *td; - urbp->short_control_packet = 1; + urbp->short_transfer = 1; td = list_entry(urbp->td_list.prev, struct uhci_td, list); urbp->qh->element = cpu_to_le32(td->dma_handle); @@ -720,16 +655,14 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) unsigned int status; int ret = 0; - if (list_empty(&urbp->td_list)) - return -EINVAL; - head = &urbp->td_list; - - if (urbp->short_control_packet) { + if (urbp->short_transfer) { tmp = head->prev; goto status_stage; } + urb->actual_length = 0; + tmp = head->next; td = list_entry(tmp, struct uhci_td, list); @@ -742,8 +675,6 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) if (status) goto td_error; - urb->actual_length = 0; - /* The rest of the TDs (but the last) are data */ tmp = tmp->next; while (tmp != head && tmp->next != head) { @@ -770,10 +701,7 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) goto err; } - if (uhci_packetid(td_token(td)) == USB_PID_IN) - return usb_control_retrigger_status(uhci, urb); - else - return 0; + return usb_control_retrigger_status(uhci, urb); } } @@ -825,15 +753,15 @@ err: /* * Common submit for bulk and interrupt */ -static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh) +static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { struct uhci_td *td; - struct uhci_qh *qh; unsigned long destination, status; - int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); int len = urb->transfer_buffer_length; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; dma_addr_t data = urb->transfer_dma; + __le32 *plink, fake_link; if (len < 0) return -EINVAL; @@ -841,7 +769,8 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); - status = uhci_maxerr(3) | TD_CTRL_ACTIVE; + /* 3 errors */ + status = TD_CTRL_ACTIVE | uhci_maxerr(3); if (urb->dev->speed == USB_SPEED_LOW) status |= TD_CTRL_LS; if (usb_pipein(urb->pipe)) @@ -850,10 +779,11 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb /* * Build the DATA TDs */ + plink = &fake_link; do { /* Allow zero length packets */ int pktsze = maxsze; - if (pktsze >= len) { + if (len <= pktsze) { /* The last packet */ pktsze = len; if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) status &= ~TD_CTRL_SPD; @@ -862,12 +792,15 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; + *plink = cpu_to_le32(td->dma_handle); uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(pktsze) | + uhci_fill_td(td, status, + destination | uhci_explen(pktsze) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), data); + plink = &td->link; data += pktsze; len -= maxsze; @@ -883,11 +816,13 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb * however, if transfer_length == 0, the zero packet was already * prepared above. */ - if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && - !len && urb->transfer_buffer_length) { + if ((urb->transfer_flags & URB_ZERO_PACKET) && + usb_pipeout(urb->pipe) && len == 0 && + urb->transfer_buffer_length > 0) { td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; + *plink = cpu_to_le32(td->dma_handle); uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(0) | @@ -905,24 +840,9 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb * fast side but not enough to justify delaying an interrupt * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT * flag setting. */ - td->status |= cpu_to_le32(TD_CTRL_IOC); - - qh = uhci_alloc_qh(uhci); - if (!qh) - return -ENOMEM; - - urbp->qh = qh; - qh->urbp = urbp; + td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); - /* Always breadth first */ - uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); - - if (eurb) - uhci_append_queued_urb(uhci, eurb, urb); - else - uhci_insert_qh(uhci, skelqh, urb); - - return -EINPROGRESS; + return 0; } /* @@ -954,8 +874,24 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) if (urb->transfer_flags & URB_SHORT_NOT_OK) { ret = -EREMOTEIO; goto err; - } else - return 0; + } + + /* + * This URB stopped short of its end. We have to + * fix up the toggles of the following URBs on the + * queue and restart the queue. + * + * Do this only the first time we encounter the + * short URB. + */ + if (!urbp->short_transfer) { + urbp->short_transfer = 1; + uhci_fixup_toggles(urb); + td = list_entry(urbp->td_list.prev, + struct uhci_td, list); + urbp->qh->element = td->link; + } + break; } } @@ -988,7 +924,8 @@ err: return ret; } -static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { int ret; @@ -996,21 +933,22 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struc if (urb->dev->speed == USB_SPEED_LOW) return -EINVAL; - ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh); - if (ret == -EINPROGRESS) + qh->skel = uhci->skel_bulk_qh; + ret = uhci_submit_common(uhci, urb, qh); + if (ret == 0) uhci_inc_fsbr(uhci, urb); - return ret; } -static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { - /* USB 1.1 interrupt transfers only involve one packet per interval; - * that's the uhci_submit_common() "breadth first" policy. Drivers - * can submit urbs of any length, but longer ones might need many - * intervals to complete. + /* USB 1.1 interrupt transfers only involve one packet per interval. + * Drivers can submit URBs of any length, but longer ones will need + * multiple intervals to complete. */ - return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]); + qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)]; + return uhci_submit_common(uhci, urb, qh); } /* @@ -1072,11 +1010,12 @@ static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb) /* * Isochronous transfers */ -static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) +static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { - struct uhci_td *td; + struct uhci_td *td = NULL; /* Since urb->number_of_packets > 0 */ int i, ret, frame; - int status, destination; + unsigned long destination, status; struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; status = TD_CTRL_ACTIVE | TD_CTRL_IOS; @@ -1092,20 +1031,25 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) return -ENOMEM; uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length), - urb->transfer_dma + urb->iso_frame_desc[i].offset); - - if (i + 1 >= urb->number_of_packets) - td->status |= cpu_to_le32(TD_CTRL_IOC); + uhci_fill_td(td, status, destination | + uhci_explen(urb->iso_frame_desc[i].length), + urb->transfer_dma + + urb->iso_frame_desc[i].offset); } + /* Set the interrupt-on-completion flag on the last packet. */ + td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); + + qh->skel = uhci->skel_iso_qh; + + /* Add the TDs to the frame list */ frame = urb->start_frame; list_for_each_entry(td, &urbp->td_list, list) { - uhci_insert_td_frame_list(uhci, td, frame); + uhci_insert_td_in_frame_list(uhci, td, frame); frame += urb->interval; } - return -EINPROGRESS; + return 0; } static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) @@ -1139,80 +1083,67 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) i++; } - unlink_isochronous_tds(uhci, urb); return ret; } -static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *up; - - /* We don't match Isoc transfers since they are special */ - if (usb_pipeisoc(urb->pipe)) - return NULL; - - list_for_each_entry(up, &uhci->urb_list, urb_list) { - struct urb *u = up->urb; - - if (u->dev == urb->dev && u->status == -EINPROGRESS) { - /* For control, ignore the direction */ - if (usb_pipecontrol(urb->pipe) && - (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN)) - return u; - else if (u->pipe == urb->pipe) - return u; - } - } - - return NULL; -} - static int uhci_urb_enqueue(struct usb_hcd *hcd, - struct usb_host_endpoint *ep, + struct usb_host_endpoint *hep, struct urb *urb, gfp_t mem_flags) { int ret; struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; - struct urb *eurb; + struct urb_priv *urbp; + struct uhci_qh *qh; int bustime; spin_lock_irqsave(&uhci->lock, flags); ret = urb->status; if (ret != -EINPROGRESS) /* URB already unlinked! */ - goto out; + goto done; - eurb = uhci_find_urb_ep(uhci, urb); + ret = -ENOMEM; + urbp = uhci_alloc_urb_priv(uhci, urb); + if (!urbp) + goto done; - if (!uhci_alloc_urb_priv(uhci, urb)) { - ret = -ENOMEM; - goto out; + if (hep->hcpriv) + qh = (struct uhci_qh *) hep->hcpriv; + else { + qh = uhci_alloc_qh(uhci, urb->dev, hep); + if (!qh) + goto err_no_qh; } + urbp->qh = qh; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: - ret = uhci_submit_control(uhci, urb, eurb); + ret = uhci_submit_control(uhci, urb, qh); + break; + case PIPE_BULK: + ret = uhci_submit_bulk(uhci, urb, qh); break; case PIPE_INTERRUPT: - if (!eurb) { + if (list_empty(&qh->queue)) { bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) ret = bustime; else { - ret = uhci_submit_interrupt(uhci, urb, eurb); - if (ret == -EINPROGRESS) + ret = uhci_submit_interrupt(uhci, urb, qh); + if (ret == 0) usb_claim_bandwidth(urb->dev, urb, bustime, 0); } } else { /* inherit from parent */ - urb->bandwidth = eurb->bandwidth; - ret = uhci_submit_interrupt(uhci, urb, eurb); + struct urb_priv *eurbp; + + eurbp = list_entry(qh->queue.prev, struct urb_priv, + node); + urb->bandwidth = eurbp->urb->bandwidth; + ret = uhci_submit_interrupt(uhci, urb, qh); } break; - case PIPE_BULK: - ret = uhci_submit_bulk(uhci, urb, eurb); - break; case PIPE_ISOCHRONOUS: bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { @@ -1220,22 +1151,59 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, break; } - ret = uhci_submit_isochronous(uhci, urb); - if (ret == -EINPROGRESS) + ret = uhci_submit_isochronous(uhci, urb, qh); + if (ret == 0) usb_claim_bandwidth(urb->dev, urb, bustime, 1); break; } + if (ret != 0) + goto err_submit_failed; - if (ret != -EINPROGRESS) { - /* Submit failed, so delete it from the urb_list */ - struct urb_priv *urbp = urb->hcpriv; + /* Add this URB to the QH */ + urbp->qh = qh; + list_add_tail(&urbp->node, &qh->queue); + list_add_tail(&urbp->urb_list, &uhci->urb_list); - list_del_init(&urbp->urb_list); - uhci_destroy_urb_priv(uhci, urb); - } else - ret = 0; + /* If the new URB is the first and only one on this QH then either + * the QH is new and idle or else it's unlinked and waiting to + * become idle, so we can activate it right away. */ + if (qh->queue.next == &urbp->node) + uhci_activate_qh(uhci, qh); + + /* If the QH is already active, we have a race with the hardware. + * This won't get fixed until dummy TDs are added. */ + else if (qh->state == QH_STATE_ACTIVE) { + + /* If the URB isn't first on its queue, adjust the link pointer + * of the last TD in the previous URB. */ + if (urbp->node.prev != &urbp->qh->queue) { + struct urb_priv *purbp = list_entry(urbp->node.prev, + struct urb_priv, node); + struct uhci_td *ptd = list_entry(purbp->td_list.prev, + struct uhci_td, list); + struct uhci_td *td = list_entry(urbp->td_list.next, + struct uhci_td, list); + + ptd->link = cpu_to_le32(td->dma_handle); + + } + if (qh_element(qh) == UHCI_PTR_TERM) { + struct uhci_td *td = list_entry(urbp->td_list.next, + struct uhci_td, list); + + qh->element = cpu_to_le32(td->dma_handle); + } + } + goto done; + +err_submit_failed: + if (qh->state == QH_STATE_IDLE) + uhci_make_qh_idle(uhci, qh); /* Reclaim unused QH */ -out: +err_no_qh: + uhci_free_urb_priv(uhci, urbp); + +done: spin_unlock_irqrestore(&uhci->lock, flags); return ret; } @@ -1245,119 +1213,115 @@ out: */ static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb) { - int ret = -EINPROGRESS; - struct urb_priv *urbp; - - spin_lock(&urb->lock); - - urbp = (struct urb_priv *)urb->hcpriv; - - if (urb->status != -EINPROGRESS) /* URB already dequeued */ - goto out; + int status; + int okay_to_giveback = 0; + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: - ret = uhci_result_control(uhci, urb); - break; - case PIPE_BULK: - case PIPE_INTERRUPT: - ret = uhci_result_common(uhci, urb); + status = uhci_result_control(uhci, urb); break; case PIPE_ISOCHRONOUS: - ret = uhci_result_isochronous(uhci, urb); + status = uhci_result_isochronous(uhci, urb); + break; + default: /* PIPE_BULK or PIPE_INTERRUPT */ + status = uhci_result_common(uhci, urb); break; } - if (ret == -EINPROGRESS) - goto out; - urb->status = ret; + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) { /* Not yet dequeued */ + if (status != -EINPROGRESS) { /* URB has completed */ + urb->status = status; + + /* If the URB got a real error (as opposed to + * simply being dequeued), we don't have to + * unlink the QH. Fix this later... */ + if (status < 0) + uhci_unlink_qh(uhci, urbp->qh); + else + okay_to_giveback = 1; + } + } else { /* Already dequeued */ + if (urbp->qh->state == QH_STATE_UNLINKING && + uhci->frame_number + uhci->is_stopped != + urbp->qh->unlink_frame) + okay_to_giveback = 1; + } + spin_unlock(&urb->lock); + if (!okay_to_giveback) + return; switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: case PIPE_ISOCHRONOUS: /* Release bandwidth for Interrupt or Isoc. transfers */ if (urb->bandwidth) usb_release_bandwidth(urb->dev, urb, 1); - uhci_unlink_generic(uhci, urb); break; case PIPE_INTERRUPT: /* Release bandwidth for Interrupt or Isoc. transfers */ /* Make sure we don't release if we have a queued URB */ - if (list_empty(&urbp->queue_list) && urb->bandwidth) + if (list_empty(&urbp->qh->queue) && urb->bandwidth) usb_release_bandwidth(urb->dev, urb, 0); else /* bandwidth was passed on to queued URB, */ /* so don't let usb_unlink_urb() release it */ urb->bandwidth = 0; - uhci_unlink_generic(uhci, urb); + /* Falls through */ + case PIPE_BULK: + if (status < 0) + uhci_fixup_toggles(urb); + break; + default: /* PIPE_CONTROL */ break; - default: - dev_info(uhci_dev(uhci), "%s: unknown pipe type %d " - "for urb %p\n", - __FUNCTION__, usb_pipetype(urb->pipe), urb); } - /* Move it from uhci->urb_list to uhci->complete_list */ - uhci_moveto_complete(uhci, urbp); + /* Take the URB's TDs off the hardware schedule */ + uhci_remove_tds_from_schedule(uhci, urb, status); -out: - spin_unlock(&urb->lock); -} - -static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) -{ - struct list_head *head; - struct uhci_td *td; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - int prevactive = 0; + /* Take the URB off the QH's queue and see if the QH is now unused */ + list_del_init(&urbp->node); + if (list_empty(&urbp->qh->queue)) + uhci_unlink_qh(uhci, urbp->qh); uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ - /* - * Now we need to find out what the last successful toggle was - * so we can update the local data toggle for the next transfer - * - * There are 2 ways the last successful completed TD is found: - * - * 1) The TD is NOT active and the actual length < expected length - * 2) The TD is NOT active and it's the last TD in the chain - * - * and a third way the first uncompleted TD is found: - * - * 3) The TD is active and the previous TD is NOT active - * - * Control and Isochronous ignore the toggle, so this is safe - * for all types - * - * FIXME: The toggle fixups won't be 100% reliable until we - * change over to using a single queue for each endpoint and - * stop the queue before unlinking. - */ - head = &urbp->td_list; - list_for_each_entry(td, head, list) { - unsigned int ctrlstat = td_status(td); + /* Queue it for giving back */ + list_move_tail(&urbp->urb_list, &uhci->complete_list); +} - if (!(ctrlstat & TD_CTRL_ACTIVE) && - (uhci_actual_length(ctrlstat) < - uhci_expected_length(td_token(td)) || - td->list.next == head)) - usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), - uhci_packetout(td_token(td)), - uhci_toggle(td_token(td)) ^ 1); - else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive) - usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), - uhci_packetout(td_token(td)), - uhci_toggle(td_token(td))); +/* + * Check out the QHs waiting to be fully unlinked + */ +static void uhci_scan_unlinking_qhs(struct uhci_hcd *uhci) +{ + struct uhci_qh *qh, *tmp; - prevactive = ctrlstat & TD_CTRL_ACTIVE; - } + list_for_each_entry_safe(qh, tmp, &uhci->skel_unlink_qh->node, node) { - uhci_delete_queued_urb(uhci, urb); + /* If the queue is empty and the QH is fully unlinked then + * it can become IDLE. */ + if (list_empty(&qh->queue)) { + if (uhci->frame_number + uhci->is_stopped != + qh->unlink_frame) + uhci_make_qh_idle(uhci, qh); - /* The interrupt loop will reclaim the QHs */ - uhci_remove_qh(uhci, urbp->qh); - urbp->qh = NULL; + /* If none of the QH's URBs have been dequeued then the QH + * should be re-activated. */ + } else { + struct urb_priv *urbp; + int any_dequeued = 0; + + list_for_each_entry(urbp, &qh->queue, node) { + if (urbp->urb->status != -EINPROGRESS) { + any_dequeued = 1; + break; + } + } + if (!any_dequeued) + uhci_activate_qh(uhci, qh); + } + } } static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) @@ -1370,22 +1334,11 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) urbp = urb->hcpriv; if (!urbp) /* URB was never linked! */ goto done; - list_del_init(&urbp->urb_list); + /* Remove Isochronous TDs from the frame list ASAP */ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) - unlink_isochronous_tds(uhci, urb); - uhci_unlink_generic(uhci, urb); - - uhci_get_current_frame_number(uhci); - if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) { - uhci_remove_pending_urbps(uhci); - uhci->urb_remove_age = uhci->frame_number; - } - - /* If we're the first, set the next interrupt bit */ - if (list_empty(&uhci->urb_remove_list)) - uhci_set_next_interrupt(uhci); - list_add_tail(&urbp->urb_list, &uhci->urb_remove_list); + uhci_unlink_isochronous_tds(uhci, urb); + uhci_unlink_qh(uhci, urbp->qh); done: spin_unlock_irqrestore(&uhci->lock, flags); @@ -1426,17 +1379,6 @@ static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) return 0; } -static void uhci_free_pending_qhs(struct uhci_hcd *uhci) -{ - struct uhci_qh *qh, *tmp; - - list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) { - list_del_init(&qh->remove_list); - - uhci_free_qh(uhci, qh); - } -} - static void uhci_free_pending_tds(struct uhci_hcd *uhci) { struct uhci_td *td, *tmp; @@ -1455,7 +1397,7 @@ __acquires(uhci->lock) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - uhci_destroy_urb_priv(uhci, urb); + uhci_free_urb_priv(uhci, (struct urb_priv *) (urb->hcpriv)); spin_unlock(&uhci->lock); usb_hcd_giveback_urb(hcd, urb, regs); @@ -1474,13 +1416,6 @@ static void uhci_finish_completion(struct uhci_hcd *uhci, struct pt_regs *regs) } } -static void uhci_remove_pending_urbps(struct uhci_hcd *uhci) -{ - - /* Splice the urb_remove_list onto the end of the complete_list */ - list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev); -} - /* Process events in the schedule, but only in one thread at a time */ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) { @@ -1498,12 +1433,8 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) uhci_clear_next_interrupt(uhci); uhci_get_current_frame_number(uhci); - if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) - uhci_free_pending_qhs(uhci); if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) uhci_free_pending_tds(uhci); - if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) - uhci_remove_pending_urbps(uhci); /* Walk the list of pending URBs to see which ones completed * (must be _safe because uhci_transfer_result() dequeues URBs) */ @@ -1516,25 +1447,21 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) uhci_finish_completion(uhci, regs); /* If the controller is stopped, we can finish these off right now */ - if (uhci->is_stopped) { - uhci_free_pending_qhs(uhci); + if (uhci->is_stopped) uhci_free_pending_tds(uhci); - uhci_remove_pending_urbps(uhci); - } if (uhci->need_rescan) goto rescan; uhci->scan_in_progress = 0; - if (list_empty(&uhci->urb_remove_list) && - list_empty(&uhci->td_remove_list) && - list_empty(&uhci->qh_remove_list)) + /* Check out the QHs waiting for unlinking */ + uhci_scan_unlinking_qhs(uhci); + + if (list_empty(&uhci->td_remove_list) && + list_empty(&uhci->skel_unlink_qh->node)) uhci_clear_next_interrupt(uhci); else uhci_set_next_interrupt(uhci); - - /* Wake up anyone waiting for an URB to complete */ - wake_up_all(&uhci->waitqh); } static void check_fsbr(struct uhci_hcd *uhci) |