USB: add urb->unlinked field
[linux-2.6-block.git] / drivers / usb / host / uhci-q.c
index f4ebdb3e488f59ad23d17ae7fc2a301b2aae0e9c..bab5672665597b29f28ea6c6b3f7b3c948fba1d9 100644 (file)
@@ -45,43 +45,27 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
  */
 static void uhci_fsbr_on(struct uhci_hcd *uhci)
 {
-       struct uhci_qh *fsbr_qh, *lqh, *tqh;
+       struct uhci_qh *lqh;
 
+       /* The terminating skeleton QH always points back to the first
+        * FSBR QH.  Make the last async QH point to the terminating
+        * skeleton QH. */
        uhci->fsbr_is_on = 1;
        lqh = list_entry(uhci->skel_async_qh->node.prev,
                        struct uhci_qh, node);
-
-       /* Find the first FSBR QH.  Linear search through the list is
-        * acceptable because normally FSBR gets turned on as soon as
-        * one QH needs it. */
-       fsbr_qh = NULL;
-       list_for_each_entry_reverse(tqh, &uhci->skel_async_qh->node, node) {
-               if (tqh->skel < SKEL_FSBR)
-                       break;
-               fsbr_qh = tqh;
-       }
-
-       /* No FSBR QH means we must insert the terminating skeleton QH */
-       if (!fsbr_qh) {
-               uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);
-               wmb();
-               lqh->link = uhci->skel_term_qh->link;
-
-       /* Otherwise loop the last QH to the first FSBR QH */
-       } else
-               lqh->link = LINK_TO_QH(fsbr_qh);
+       lqh->link = LINK_TO_QH(uhci->skel_term_qh);
 }
 
 static void uhci_fsbr_off(struct uhci_hcd *uhci)
 {
        struct uhci_qh *lqh;
 
+       /* Remove the link from the last async QH to the terminating
+        * skeleton QH. */
        uhci->fsbr_is_on = 0;
        lqh = list_entry(uhci->skel_async_qh->node.prev,
                        struct uhci_qh, node);
-
-       /* End the async list normally and unlink the terminating QH */
-       lqh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM;
+       lqh->link = UHCI_PTR_TERM;
 }
 
 static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
@@ -139,10 +123,14 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
 
 static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
 {
-       if (!list_empty(&td->list))
+       if (!list_empty(&td->list)) {
                dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
-       if (!list_empty(&td->fl_list))
+               WARN_ON(1);
+       }
+       if (!list_empty(&td->fl_list)) {
                dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+               WARN_ON(1);
+       }
 
        dma_pool_free(uhci->td_pool, td, td->dma_handle);
 }
@@ -307,8 +295,10 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
 static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
 {
        WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);
-       if (!list_empty(&qh->queue))
+       if (!list_empty(&qh->queue)) {
                dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
+               WARN_ON(1);
+       }
 
        list_del(&qh->node);
        if (qh->udev) {
@@ -464,9 +454,8 @@ static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
  */
 static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
 {
-       struct uhci_qh *pqh, *lqh;
+       struct uhci_qh *pqh;
        __le32 link_to_new_qh;
-       __le32 *extra_link = &link_to_new_qh;
 
        /* Find the predecessor QH for our new one and insert it in the list.
         * The list of QHs is expected to be short, so linear search won't
@@ -476,31 +465,17 @@ static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
                        break;
        }
        list_add(&qh->node, &pqh->node);
-       qh->link = pqh->link;
-
-       link_to_new_qh = LINK_TO_QH(qh);
-
-       /* If this is now the first FSBR QH, take special action */
-       if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR &&
-                       qh->skel >= SKEL_FSBR) {
-               lqh = list_entry(uhci->skel_async_qh->node.prev,
-                               struct uhci_qh, node);
-
-               /* If the new QH is also the last one, we must unlink
-                * the terminating skeleton QH and make the new QH point
-                * back to itself. */
-               if (qh == lqh) {
-                       qh->link = link_to_new_qh;
-                       extra_link = &uhci->skel_term_qh->link;
-
-               /* Otherwise the last QH must point to the new QH */
-               } else
-                       extra_link = &lqh->link;
-       }
 
        /* Link it into the schedule */
+       qh->link = pqh->link;
        wmb();
-       *extra_link = pqh->link = link_to_new_qh;
+       link_to_new_qh = LINK_TO_QH(qh);
+       pqh->link = link_to_new_qh;
+
+       /* If this is now the first FSBR QH, link the terminating skeleton
+        * QH to it. */
+       if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
+               uhci->skel_term_qh->link = link_to_new_qh;
 }
 
 /*
@@ -561,31 +536,16 @@ static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
  */
 static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
 {
-       struct uhci_qh *pqh, *lqh;
+       struct uhci_qh *pqh;
        __le32 link_to_next_qh = qh->link;
 
        pqh = list_entry(qh->node.prev, struct uhci_qh, node);
-
-       /* If this is the first FSBQ QH, take special action */
-       if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR &&
-                       qh->skel >= SKEL_FSBR) {
-               lqh = list_entry(uhci->skel_async_qh->node.prev,
-                               struct uhci_qh, node);
-
-               /* If this QH is also the last one, we must link in
-                * the terminating skeleton QH. */
-               if (qh == lqh) {
-                       link_to_next_qh = LINK_TO_QH(uhci->skel_term_qh);
-                       uhci->skel_term_qh->link = link_to_next_qh;
-                       wmb();
-                       qh->link = link_to_next_qh;
-
-               /* Otherwise the last QH must point to the new first FSBR QH */
-               } else
-                       lqh->link = link_to_next_qh;
-       }
-
        pqh->link = link_to_next_qh;
+
+       /* If this was the old first FSBR QH, link the terminating skeleton
+        * QH to the next (new first FSBR) QH. */
+       if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
+               uhci->skel_term_qh->link = link_to_next_qh;
        mb();
 }
 
@@ -786,16 +746,17 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci,
 {
        struct uhci_td *td, *tmp;
 
-       if (!list_empty(&urbp->node))
+       if (!list_empty(&urbp->node)) {
                dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n",
                                urbp->urb);
+               WARN_ON(1);
+       }
 
        list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
                uhci_remove_td_from_urbp(td);
                uhci_free_td(uhci, td);
        }
 
-       urbp->urb->hcpriv = NULL;
        kmem_cache_free(uhci_up_cachep, urbp);
 }
 
@@ -865,8 +826,10 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
         * If direction is "send", change the packet ID from SETUP (0x2D)
         * to OUT (0xE1).  Else change it from SETUP to IN (0x69) and
         * set Short Packet Detect (SPD) for all data packets.
+        *
+        * 0-length transfers always get treated as "send".
         */
-       if (usb_pipeout(urb->pipe))
+       if (usb_pipeout(urb->pipe) || len == 0)
                destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
        else {
                destination ^= (USB_PID_SETUP ^ USB_PID_IN);
@@ -877,7 +840,12 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
         * Build the DATA TDs
         */
        while (len > 0) {
-               int pktsze = min(len, maxsze);
+               int pktsze = maxsze;
+
+               if (len <= pktsze) {            /* The last data packet */
+                       pktsze = len;
+                       status &= ~TD_CTRL_SPD;
+               }
 
                td = uhci_alloc_td(uhci);
                if (!td)
@@ -904,20 +872,10 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
                goto nomem;
        *plink = LINK_TO_TD(td);
 
-       /*
-        * It's IN if the pipe is an output pipe or we're not expecting
-        * data back.
-        */
-       destination &= ~TD_TOKEN_PID_MASK;
-       if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
-               destination |= USB_PID_IN;
-       else
-               destination |= USB_PID_OUT;
-
+       /* Change direction for the status transaction */
+       destination ^= (USB_PID_IN ^ USB_PID_OUT);
        destination |= TD_TOKEN_TOGGLE;         /* End in Data1 */
 
-       status &= ~TD_CTRL_SPD;
-
        uhci_add_td_to_urbp(td, urbp);
        uhci_fill_td(td, status | TD_CTRL_IOC,
                        destination | uhci_explen(0), 0);
@@ -1217,16 +1175,24 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
 
                                if (debug > 1 && errbuf) {
                                        /* Print the chain for debugging */
-                                       uhci_show_qh(urbp->qh, errbuf,
+                                       uhci_show_qh(uhci, urbp->qh, errbuf,
                                                        ERRBUF_LEN, 0);
                                        lprintk(errbuf);
                                }
                        }
 
+               /* Did we receive a short packet? */
                } else if (len < uhci_expected_length(td_token(td))) {
 
-                       /* We received a short packet */
-                       if (urb->transfer_flags & URB_SHORT_NOT_OK)
+                       /* For control transfers, go to the status TD if
+                        * this isn't already the last data TD */
+                       if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+                               if (td->list.next != urbp->td_list.prev)
+                                       ret = 1;
+                       }
+
+                       /* For bulk and interrupt, this may be an error */
+                       else if (urb->transfer_flags & URB_SHORT_NOT_OK)
                                ret = -EREMOTEIO;
 
                        /* Fixup needed only if this isn't the URB's last TD */
@@ -1246,10 +1212,6 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
 
 err:
        if (ret < 0) {
-               /* In case a control transfer gets an error
-                * during the setup stage */
-               urb->actual_length = max(urb->actual_length, 0);
-
                /* Note that the queue has stopped and save
                 * the next toggle value */
                qh->element = UHCI_PTR_TERM;
@@ -1361,7 +1323,6 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
        if (list_empty(&qh->queue)) {
                qh->iso_packet_desc = &urb->iso_frame_desc[0];
                qh->iso_frame = urb->start_frame;
-               qh->iso_status = 0;
        }
 
        qh->skel = SKEL_ISO;
@@ -1398,22 +1359,18 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
                        qh->iso_packet_desc->actual_length = actlength;
                        qh->iso_packet_desc->status = status;
                }
-
-               if (status) {
+               if (status)
                        urb->error_count++;
-                       qh->iso_status = status;
-               }
 
                uhci_remove_td_from_urbp(td);
                uhci_free_td(uhci, td);
                qh->iso_frame += qh->period;
                ++qh->iso_packet_desc;
        }
-       return qh->iso_status;
+       return 0;
 }
 
 static int uhci_urb_enqueue(struct usb_hcd *hcd,
-               struct usb_host_endpoint *hep,
                struct urb *urb, gfp_t mem_flags)
 {
        int ret;
@@ -1424,19 +1381,19 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
 
        spin_lock_irqsave(&uhci->lock, flags);
 
-       ret = urb->status;
-       if (ret != -EINPROGRESS)                /* URB already unlinked! */
-               goto done;
+       ret = usb_hcd_link_urb_to_ep(hcd, urb);
+       if (ret)
+               goto done_not_linked;
 
        ret = -ENOMEM;
        urbp = uhci_alloc_urb_priv(uhci, urb);
        if (!urbp)
                goto done;
 
-       if (hep->hcpriv)
-               qh = (struct uhci_qh *) hep->hcpriv;
+       if (urb->ep->hcpriv)
+               qh = urb->ep->hcpriv;
        else {
-               qh = uhci_alloc_qh(uhci, urb->dev, hep);
+               qh = uhci_alloc_qh(uhci, urb->dev, urb->ep);
                if (!qh)
                        goto err_no_qh;
        }
@@ -1477,27 +1434,29 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
 err_submit_failed:
        if (qh->state == QH_STATE_IDLE)
                uhci_make_qh_idle(uhci, qh);    /* Reclaim unused QH */
-
 err_no_qh:
        uhci_free_urb_priv(uhci, urbp);
-
 done:
+       if (ret)
+               usb_hcd_unlink_urb_from_ep(hcd, urb);
+done_not_linked:
        spin_unlock_irqrestore(&uhci->lock, flags);
        return ret;
 }
 
-static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
        unsigned long flags;
-       struct urb_priv *urbp;
        struct uhci_qh *qh;
+       int rc;
 
        spin_lock_irqsave(&uhci->lock, flags);
-       urbp = urb->hcpriv;
-       if (!urbp)                      /* URB was never linked! */
+       rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+       if (rc)
                goto done;
-       qh = urbp->qh;
+
+       qh = ((struct urb_priv *) urb->hcpriv)->qh;
 
        /* Remove Isochronous TDs from the frame list ASAP */
        if (qh->type == USB_ENDPOINT_XFER_ISOC) {
@@ -1514,7 +1473,7 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
 
 done:
        spin_unlock_irqrestore(&uhci->lock, flags);
-       return 0;
+       return rc;
 }
 
 /*
@@ -1527,9 +1486,18 @@ __acquires(uhci->lock)
 {
        struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
 
+       if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+
+               /* urb->actual_length < 0 means the setup transaction didn't
+                * complete successfully.  Either it failed or the URB was
+                * unlinked first.  Regardless, don't confuse people with a
+                * negative length. */
+               urb->actual_length = max(urb->actual_length, 0);
+       }
+
        /* When giving back the first URB in an Isochronous queue,
         * reinitialize the QH's iso-related members for the next URB. */
-       if (qh->type == USB_ENDPOINT_XFER_ISOC &&
+       else if (qh->type == USB_ENDPOINT_XFER_ISOC &&
                        urbp->node.prev == &qh->queue &&
                        urbp->node.next != &qh->queue) {
                struct urb *nurb = list_entry(urbp->node.next,
@@ -1537,7 +1505,6 @@ __acquires(uhci->lock)
 
                qh->iso_packet_desc = &nurb->iso_frame_desc[0];
                qh->iso_frame = nurb->start_frame;
-               qh->iso_status = 0;
        }
 
        /* Take the URB off the QH's queue.  If the queue is now empty,
@@ -1550,6 +1517,7 @@ __acquires(uhci->lock)
        }
 
        uhci_free_urb_priv(uhci, urbp);
+       usb_hcd_unlink_urb_from_ep(uhci_to_hcd(uhci), urb);
 
        spin_unlock(&uhci->lock);
        usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb);
@@ -1589,15 +1557,12 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
                        break;
 
                spin_lock(&urb->lock);
-               if (urb->status == -EINPROGRESS)        /* Not dequeued */
-                       urb->status = status;
-               else
-                       status = ECONNRESET;            /* Not -ECONNRESET */
+               urb->status = status;
                spin_unlock(&urb->lock);
 
                /* Dequeued but completed URBs can't be given back unless
                 * the QH is stopped or has finished unlinking. */
-               if (status == ECONNRESET) {
+               if (urb->unlinked) {
                        if (QH_FINISHED_UNLINKING(qh))
                                qh->is_stopped = 1;
                        else if (!qh->is_stopped)
@@ -1605,7 +1570,7 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
                }
 
                uhci_giveback_urb(uhci, qh, urb);
-               if (status < 0 && qh->type != USB_ENDPOINT_XFER_ISOC)
+               if (status < 0)
                        break;
        }
 
@@ -1620,7 +1585,7 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
 restart:
        list_for_each_entry(urbp, &qh->queue, node) {
                urb = urbp->urb;
-               if (urb->status != -EINPROGRESS) {
+               if (urb->unlinked) {
 
                        /* Fix up the TD links and save the toggles for
                         * non-Isochronous queues.  For Isochronous queues,