From ccf7b32222bb44ad7409e7596f6428d78eab9706 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Tue, 14 Nov 2017 20:06:15 -0800 Subject: [PATCH] usb: xhci: Add helper API to issue stop endpoint command This API is used to issue stop endpoint command on requested endpoint in order to retire all active TRBs in the transfer ring. Change-Id: I312772367a2cd293982a66ea8b75e04a8b1f2fd0 Signed-off-by: Hemant Kumar --- drivers/usb/host/xhci.c | 57 ++++++++++++++++++++++++++++++++++++ include/linux/usb/xhci-sec.h | 7 +++++ 2 files changed, 64 insertions(+) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 9cf78e8f4e41..8a83068a986e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5383,6 +5383,63 @@ phys_addr_t xhci_get_xfer_ring_phys_addr(struct usb_device *udev, } EXPORT_SYMBOL(xhci_get_xfer_ring_phys_addr); +int xhci_stop_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + unsigned int ep_index; + struct xhci_virt_device *virt_dev; + struct xhci_command *cmd; + unsigned long flags; + int ret = 0; + + if (udev->state == USB_STATE_NOTATTACHED || !HCD_RH_RUNNING(hcd)) + return 0; + + cmd = xhci_alloc_command(xhci, true, GFP_NOIO); + if (!cmd) + return -ENOMEM; + + spin_lock_irqsave(&xhci->lock, flags); + virt_dev = xhci->devs[udev->slot_id]; + if (!virt_dev) { + ret = -ENODEV; + goto err; + } + + ep_index = xhci_get_endpoint_index(&ep->desc); + if (virt_dev->eps[ep_index].ring && + virt_dev->eps[ep_index].ring->dequeue) { + ret = xhci_queue_stop_endpoint(xhci, cmd, udev->slot_id, + ep_index, 0); + if (ret) + goto err; + + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + /* Wait for stop endpoint command to finish */ + wait_for_completion(cmd->completion); + + if (cmd->status == COMP_COMMAND_ABORTED || + cmd->status == COMP_STOPPED) { + xhci_warn(xhci, + "stop endpoint command timeout for ep%d%s\n", + usb_endpoint_num(&ep->desc), + usb_endpoint_dir_in(&ep->desc) ? "in" : "out"); + ret = -ETIME; + } + goto free_cmd; + } + +err: + spin_unlock_irqrestore(&xhci->lock, flags); +free_cmd: + xhci_free_command(xhci, cmd); + return ret; +} +EXPORT_SYMBOL(xhci_stop_endpoint); + static const struct hc_driver xhci_hc_driver = { .description = "xhci-hcd", .product_desc = "xHCI Host Controller", diff --git a/include/linux/usb/xhci-sec.h b/include/linux/usb/xhci-sec.h index 878d45d372c9..5f4c3b1962db 100644 --- a/include/linux/usb/xhci-sec.h +++ b/include/linux/usb/xhci-sec.h @@ -17,6 +17,7 @@ phys_addr_t xhci_get_sec_event_ring_phys_addr(struct usb_device *udev, unsigned int intr_num, dma_addr_t *dma); phys_addr_t xhci_get_xfer_ring_phys_addr(struct usb_device *udev, struct usb_host_endpoint *ep, dma_addr_t *dma); +int xhci_stop_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); #else static inline int xhci_sec_event_ring_setup(struct usb_device *udev, unsigned int intr_num); @@ -42,6 +43,12 @@ static inline phys_addr_t xhci_get_xfer_ring_phys_addr(struct usb_device *udev, { return NULL; } + +static inline int xhci_stop_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return -ENODEV; +} #endif #endif /* __LINUX_XHCI_SEC_H */