]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/usb/host/xhci-hcd.c
USB: xhci: Use GFP_ATOMIC while holding spinlocks.
[mirror_ubuntu-artful-kernel.git] / drivers / usb / host / xhci-hcd.c
index dba3e07ccd09a1098660fb5c4a17c921deefc483..ff99365cae4273b28d544767eeeeed2fa307c082 100644 (file)
@@ -601,10 +601,13 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
                goto exit;
        }
        if (usb_endpoint_xfer_control(&urb->ep->desc))
-               ret = xhci_queue_ctrl_tx(xhci, mem_flags, urb,
+               /* We have a spinlock and interrupts disabled, so we must pass
+                * atomic context to this function, which may allocate memory.
+                */
+               ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
                                slot_id, ep_index);
        else if (usb_endpoint_xfer_bulk(&urb->ep->desc))
-               ret = xhci_queue_bulk_tx(xhci, mem_flags, urb,
+               ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
                                slot_id, ep_index);
        else
                ret = -EINVAL;
@@ -787,8 +790,11 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
        int ret = 0;
 
        ret = xhci_check_args(hcd, udev, ep, 1, __func__);
-       if (ret <= 0)
+       if (ret <= 0) {
+               /* So we won't queue a reset ep command for a root hub */
+               ep->hcpriv = NULL;
                return ret;
+       }
        xhci = hcd_to_xhci(hcd);
 
        added_ctxs = xhci_get_endpoint_flag(&ep->desc);
@@ -851,6 +857,9 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
        }
        new_slot_info = in_ctx->slot.dev_info;
 
+       /* Store the usb_device pointer for later use */
+       ep->hcpriv = udev;
+
        xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
                        (unsigned int) ep->desc.bEndpointAddress,
                        udev->slot_id,
@@ -1026,6 +1035,42 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
        xhci_zero_in_ctx(virt_dev);
 }
 
+/* Deal with stalled endpoints.  The core should have sent the control message
+ * to clear the halt condition.  However, we need to make the xHCI hardware
+ * reset its sequence number, since a device will expect a sequence number of
+ * zero after the halt condition is cleared.
+ * Context: in_interrupt
+ */
+void xhci_endpoint_reset(struct usb_hcd *hcd,
+               struct usb_host_endpoint *ep)
+{
+       struct xhci_hcd *xhci;
+       struct usb_device *udev;
+       unsigned int ep_index;
+       unsigned long flags;
+       int ret;
+
+       xhci = hcd_to_xhci(hcd);
+       udev = (struct usb_device *) ep->hcpriv;
+       /* Called with a root hub endpoint (or an endpoint that wasn't added
+        * with xhci_add_endpoint()
+        */
+       if (!ep->hcpriv)
+               return;
+       ep_index = xhci_get_endpoint_index(&ep->desc);
+
+       xhci_dbg(xhci, "Queueing reset endpoint command\n");
+       spin_lock_irqsave(&xhci->lock, flags);
+       ret = xhci_queue_reset_ep(xhci, udev->slot_id, ep_index);
+       if (!ret) {
+               xhci_ring_cmd_db(xhci);
+       }
+       spin_unlock_irqrestore(&xhci->lock, flags);
+
+       if (ret)
+               xhci_warn(xhci, "FIXME allocate a new ring segment\n");
+}
+
 /*
  * At this point, the struct usb_device is about to go away, the device has
  * disconnected, and all traffic has been stopped and the endpoints have been