Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xhci: add helper to stop endpoint and wait for completion

Expose xhci_stop_endpoint_sync() which is a synchronous variant of
xhci_queue_stop_endpoint(). This is useful for client drivers that are
using the secondary interrupters, and need to stop/clean up the current
session. The stop endpoint command handler will also take care of cleaning
up the ring.

Modifications to repurpose the new API into existing stop endpoint
sequences was implemented by Wesley Cheng.

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Co-developed-by: Wesley Cheng <quic_wcheng@quicinc.com>
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
Link: https://lore.kernel.org/r/20240217001017.29969-11-quic_wcheng@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Mathias Nyman and committed by
Greg Kroah-Hartman
9affb1d9 edc47759

+48 -16
+46 -16
drivers/usb/host/xhci.c
··· 2796 2796 return -ENOMEM; 2797 2797 } 2798 2798 2799 + /* 2800 + * Synchronous XHCI stop endpoint helper. Issues the stop endpoint command and 2801 + * waits for the command completion before returning. 2802 + */ 2803 + int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend, 2804 + gfp_t gfp_flags) 2805 + { 2806 + struct xhci_command *command; 2807 + unsigned long flags; 2808 + int ret; 2809 + 2810 + command = xhci_alloc_command(xhci, true, gfp_flags); 2811 + if (!command) 2812 + return -ENOMEM; 2813 + 2814 + spin_lock_irqsave(&xhci->lock, flags); 2815 + ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id, 2816 + ep->ep_index, suspend); 2817 + if (ret < 0) { 2818 + spin_unlock_irqrestore(&xhci->lock, flags); 2819 + goto out; 2820 + } 2821 + 2822 + xhci_ring_cmd_db(xhci); 2823 + spin_unlock_irqrestore(&xhci->lock, flags); 2824 + 2825 + ret = wait_for_completion_timeout(command->completion, msecs_to_jiffies(3000)); 2826 + if (!ret) 2827 + xhci_warn(xhci, "%s: Unable to stop endpoint.\n", 2828 + __func__); 2829 + 2830 + if (command->status == COMP_COMMAND_ABORTED || 2831 + command->status == COMP_COMMAND_RING_STOPPED) { 2832 + xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n"); 2833 + ret = -ETIME; 2834 + } 2835 + out: 2836 + xhci_free_command(xhci, command); 2837 + 2838 + return ret; 2839 + } 2840 + EXPORT_SYMBOL_GPL(xhci_stop_endpoint_sync); 2799 2841 2800 2842 /* Issue a configure endpoint command or evaluate context command 2801 2843 * and wait for it to finish. ··· 3161 3119 struct xhci_virt_device *vdev; 3162 3120 struct xhci_virt_ep *ep; 3163 3121 struct xhci_input_control_ctx *ctrl_ctx; 3164 - struct xhci_command *stop_cmd, *cfg_cmd; 3122 + struct xhci_command *cfg_cmd; 3165 3123 unsigned int ep_index; 3166 3124 unsigned long flags; 3167 3125 u32 ep_flag; ··· 3219 3177 if (ep_flag == SLOT_FLAG || ep_flag == EP0_FLAG) 3220 3178 return; 3221 3179 3222 - stop_cmd = xhci_alloc_command(xhci, true, GFP_NOWAIT); 3223 - if (!stop_cmd) 3224 - return; 3225 - 3226 3180 cfg_cmd = xhci_alloc_command_with_ctx(xhci, true, GFP_NOWAIT); 3227 3181 if (!cfg_cmd) 3228 3182 goto cleanup; ··· 3241 3203 goto cleanup; 3242 3204 } 3243 3205 3244 - err = xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id, 3245 - ep_index, 0); 3206 + spin_unlock_irqrestore(&xhci->lock, flags); 3207 + 3208 + err = xhci_stop_endpoint_sync(xhci, ep, 0, GFP_NOWAIT); 3246 3209 if (err < 0) { 3247 - spin_unlock_irqrestore(&xhci->lock, flags); 3248 - xhci_free_command(xhci, cfg_cmd); 3249 3210 xhci_dbg(xhci, "%s: Failed to queue stop ep command, %d ", 3250 3211 __func__, err); 3251 3212 goto cleanup; 3252 3213 } 3253 3214 3254 - xhci_ring_cmd_db(xhci); 3255 - spin_unlock_irqrestore(&xhci->lock, flags); 3256 - 3257 - wait_for_completion(stop_cmd->completion); 3258 - 3259 3215 spin_lock_irqsave(&xhci->lock, flags); 3260 - 3261 3216 /* config ep command clears toggle if add and drop ep flags are set */ 3262 3217 ctrl_ctx = xhci_get_input_control_ctx(cfg_cmd->in_ctx); 3263 3218 if (!ctrl_ctx) { ··· 3282 3251 3283 3252 xhci_free_command(xhci, cfg_cmd); 3284 3253 cleanup: 3285 - xhci_free_command(xhci, stop_cmd); 3286 3254 spin_lock_irqsave(&xhci->lock, flags); 3287 3255 if (ep->ep_state & EP_SOFT_CLEAR_TOGGLE) 3288 3256 ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE;
+2
drivers/usb/host/xhci.h
··· 1920 1920 void xhci_cleanup_command_queue(struct xhci_hcd *xhci); 1921 1921 void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring); 1922 1922 unsigned int count_trbs(u64 addr, u64 len); 1923 + int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, 1924 + int suspend, gfp_t gfp_flags); 1923 1925 1924 1926 /* xHCI roothub code */ 1925 1927 void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,