diff mbox series

[v2] usb: dwc3: gadget: Don't delay End Transfer on delayed_status

Message ID 3f9f59e5d74efcbaee444cf4b30ef639cc7b124e.1666146954.git.Thinh.Nguyen@synopsys.com (mailing list archive)
State Accepted
Commit 4db0fbb601361767144e712beb96704b966339f5
Headers show
Series [v2] usb: dwc3: gadget: Don't delay End Transfer on delayed_status | expand

Commit Message

Thinh Nguyen Oct. 19, 2022, 2:39 a.m. UTC
The gadget driver may wait on the request completion when it sets the
USB_GADGET_DELAYED_STATUS. Make sure that the End Transfer command can
go through if the dwc->delayed_status is set so that the request can
complete. When the delayed_status is set, the Setup packet is already
processed, and the next phase should be either Data or Status. It's
unlikely that the host would cancel the control transfer and send a new
Setup packet during End Transfer command. But if that's the case, we can
try again when ep0state returns to EP0_SETUP_PHASE.

Fixes: e1ee843488d5 ("usb: dwc3: gadget: Force sending delayed status during soft disconnect")
Cc: stable@vger.kernel.org
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
---
 Changes in v2:
 - Fix build issue due to cherry-picking...

 drivers/usb/dwc3/gadget.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)


base-commit: 9abf2313adc1ca1b6180c508c25f22f9395cc780

Comments

Wesley Cheng Oct. 19, 2022, 10:40 p.m. UTC | #1
On 10/18/2022 7:39 PM, Thinh Nguyen wrote:
> The gadget driver may wait on the request completion when it sets the
> USB_GADGET_DELAYED_STATUS. Make sure that the End Transfer command can
> go through if the dwc->delayed_status is set so that the request can
> complete. When the delayed_status is set, the Setup packet is already
> processed, and the next phase should be either Data or Status. It's
> unlikely that the host would cancel the control transfer and send a new
> Setup packet during End Transfer command. But if that's the case, we can
> try again when ep0state returns to EP0_SETUP_PHASE.

Hi Thinh,

In the scenario you saw your issue in, was there something else that 
triggered the EP0 stall and restart to bring back EP0 to SETUP state? 
(which will do the retry)  Just wanted to make sure, because there were 
situations that I had to add that sequence for the endxfer retry to 
happen. (ie in the disconnect interrupt)

Thanks
Wesley Cheng

> 
> Fixes: e1ee843488d5 ("usb: dwc3: gadget: Force sending delayed status during soft disconnect")
> Cc: stable@vger.kernel.org
> Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
> ---
>   Changes in v2:
>   - Fix build issue due to cherry-picking...
> 
>   drivers/usb/dwc3/gadget.c | 12 +++++++++++-
>   1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index 079cd333632e..dd8ecbe61bec 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -1698,6 +1698,16 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
>   	cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
>   	memset(&params, 0, sizeof(params));
>   	ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
> +	/*
> +	 * If the End Transfer command was timed out while the device is
> +	 * not in SETUP phase, it's possible that an incoming Setup packet
> +	 * may prevent the command's completion. Let's retry when the
> +	 * ep0state returns to EP0_SETUP_PHASE.
> +	 */
> +	if (ret == -ETIMEDOUT && dep->dwc->ep0state != EP0_SETUP_PHASE) {
> +		dep->flags |= DWC3_EP_DELAY_STOP;
> +		return 0;
> +	}
>   	WARN_ON_ONCE(ret);
>   	dep->resource_index = 0;
>   
> @@ -3719,7 +3729,7 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
>   	 * timeout. Delay issuing the End Transfer command until the Setup TRB is
>   	 * prepared.
>   	 */
> -	if (dwc->ep0state != EP0_SETUP_PHASE) {
> +	if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
>   		dep->flags |= DWC3_EP_DELAY_STOP;
>   		return;
>   	}
> 
> base-commit: 9abf2313adc1ca1b6180c508c25f22f9395cc780
Thinh Nguyen Oct. 19, 2022, 11:16 p.m. UTC | #2
On Wed, Oct 19, 2022, Wesley Cheng wrote:
> 
> 
> On 10/18/2022 7:39 PM, Thinh Nguyen wrote:
> > The gadget driver may wait on the request completion when it sets the
> > USB_GADGET_DELAYED_STATUS. Make sure that the End Transfer command can
> > go through if the dwc->delayed_status is set so that the request can
> > complete. When the delayed_status is set, the Setup packet is already
> > processed, and the next phase should be either Data or Status. It's
> > unlikely that the host would cancel the control transfer and send a new
> > Setup packet during End Transfer command. But if that's the case, we can
> > try again when ep0state returns to EP0_SETUP_PHASE.
> 
> Hi Thinh,
> 
> In the scenario you saw your issue in, was there something else that
> triggered the EP0 stall and restart to bring back EP0 to SETUP state? (which
> will do the retry)  Just wanted to make sure, because there were situations
> that I had to add that sequence for the endxfer retry to happen. (ie in the
> disconnect interrupt)
> 

My scenario happens while the device is connected and not related to
soft-disconnect. It doesn't involve cancellation of the control
transfer.

The scenario you mentioned in your change is related to soft-disconnect
right?

	"Ocasionally, a host may abort the current SETUP transaction, by
	issuing a subsequent SETUP token.  In those situations, it would
	result in an endxfer timeout as well."

I added a check to retry End Transfer to account for the scenario you
reported.

Thanks,
Thinh
diff mbox series

Patch

diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 079cd333632e..dd8ecbe61bec 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1698,6 +1698,16 @@  static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
 	cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
 	memset(&params, 0, sizeof(params));
 	ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+	/*
+	 * If the End Transfer command was timed out while the device is
+	 * not in SETUP phase, it's possible that an incoming Setup packet
+	 * may prevent the command's completion. Let's retry when the
+	 * ep0state returns to EP0_SETUP_PHASE.
+	 */
+	if (ret == -ETIMEDOUT && dep->dwc->ep0state != EP0_SETUP_PHASE) {
+		dep->flags |= DWC3_EP_DELAY_STOP;
+		return 0;
+	}
 	WARN_ON_ONCE(ret);
 	dep->resource_index = 0;
 
@@ -3719,7 +3729,7 @@  void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
 	 * timeout. Delay issuing the End Transfer command until the Setup TRB is
 	 * prepared.
 	 */
-	if (dwc->ep0state != EP0_SETUP_PHASE) {
+	if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
 		dep->flags |= DWC3_EP_DELAY_STOP;
 		return;
 	}