From 2840d6dfcf4306878f7d17ac16d4a0a6422728cc Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Thu, 14 Apr 2022 00:39:02 -0700 Subject: usb: dwc3: EP clear halt leading to clearing of delayed_status The usb_ep_clear_halt() API can be called from the function driver, and translates to dwc3_gadget_ep_set_halt(). This routine is shared with when the host issues a clear feature ENDPOINT_HALT, and is differentiated by the protocol argument. If the following sequence occurs, there can be a situation where the delayed_status flag is improperly cleared for the wrong SETUP transaction: 1. Vendor specific control transfer returns USB_GADGET_DELAYED_STATUS. 2. DWC3 gadget sets dwc->delayed_status to '1'. 3. Another function driver issues a usb_ep_clear_halt() call. 4. DWC3 gadget issues dwc3_stop_active_transfer() and sets DWC3_EP_PENDING_CLEAR_STALL. 5. EP command complete interrupt triggers for the end transfer, and dwc3_ep0_send_delayed_status() is allowed to run, as delayed_status is '1' due to step#1. 6. STATUS phase is sent, and delayed_status is cleared. 7. Vendor specific control transfer is finished being handled, and issues usb_composite_setup_continue(). This results in queuing of a data phase. Cache the protocol flag so that DWC3 gadget is aware of when the clear halt is due to a SETUP request from the host versus when it is sourced from a function driver. This allows for the EP command complete interrupt to know if it needs to issue a delayed status phase. Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20220414073902.21960-1-quic_wcheng@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/ep0.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/dwc3/ep0.c') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 1064be5518f6..aa8476da222d 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1080,6 +1080,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) unsigned int direction = !dwc->ep0_expect_in; dwc->delayed_status = false; + dwc->clear_stall_protocol = 0; if (dwc->ep0state != EP0_STATUS_PHASE) return; -- cgit