xhci: Manually give back cancelled URB if we can't queue it for cancel
[pandora-kernel.git] / drivers / usb / host / xhci.c
index a834373..547a039 100644 (file)
@@ -1522,19 +1522,38 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
        unsigned int ep_index;
        struct xhci_ring *ep_ring;
        struct xhci_virt_ep *ep;
+       struct xhci_virt_device *vdev;
 
        xhci = hcd_to_xhci(hcd);
        spin_lock_irqsave(&xhci->lock, flags);
        /* Make sure the URB hasn't completed or been unlinked already */
        ret = usb_hcd_check_unlink_urb(hcd, urb, status);
-       if (ret || !urb->hcpriv)
+       if (ret)
                goto done;
+
+       /* give back URB now if we can't queue it for cancel */
+       vdev = xhci->devs[urb->dev->slot_id];
+       urb_priv = urb->hcpriv;
+       if (!vdev || !urb_priv)
+               goto err_giveback;
+
+       xhci_dbg(xhci, "Cancel URB %p\n", urb);
+       xhci_dbg(xhci, "Event ring:\n");
+       xhci_debug_ring(xhci, xhci->event_ring);
+       ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+       ep = &vdev->eps[ep_index];
+       ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
+       if (!ep || !ep_ring)
+               goto err_giveback;
+
+       xhci_dbg(xhci, "Endpoint ring:\n");
+       xhci_debug_ring(xhci, ep_ring);
+
        temp = xhci_readl(xhci, &xhci->op_regs->status);
        if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
                xhci_dbg(xhci, "HW died, freeing TD.\n");
-               urb_priv = urb->hcpriv;
                for (i = urb_priv->td_cnt;
-                    i < urb_priv->length && xhci->devs[urb->dev->slot_id];
+                    i < urb_priv->length;
                     i++) {
                        td = urb_priv->td[i];
                        if (!list_empty(&td->td_list))
@@ -1542,30 +1561,9 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                        if (!list_empty(&td->cancelled_td_list))
                                list_del_init(&td->cancelled_td_list);
                }
-
-               usb_hcd_unlink_urb_from_ep(hcd, urb);
-               spin_unlock_irqrestore(&xhci->lock, flags);
-               usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
-               xhci_urb_free_priv(xhci, urb_priv);
-               return ret;
+               goto err_giveback;
        }
 
-       xhci_dbg(xhci, "Cancel URB %p\n", urb);
-       xhci_dbg(xhci, "Event ring:\n");
-       xhci_debug_ring(xhci, xhci->event_ring);
-       ep_index = xhci_get_endpoint_index(&urb->ep->desc);
-       ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
-       ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
-       if (!ep_ring) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       xhci_dbg(xhci, "Endpoint ring:\n");
-       xhci_debug_ring(xhci, ep_ring);
-
-       urb_priv = urb->hcpriv;
-
        for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
                td = urb_priv->td[i];
                list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
@@ -1586,6 +1584,14 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 done:
        spin_unlock_irqrestore(&xhci->lock, flags);
        return ret;
+
+err_giveback:
+       if (urb_priv)
+               xhci_urb_free_priv(xhci, urb_priv);
+       usb_hcd_unlink_urb_from_ep(hcd, urb);
+       spin_unlock_irqrestore(&xhci->lock, flags);
+       usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
+       return ret;
 }
 
 /* Drop an endpoint from a new bandwidth configuration for this device.