linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] usb: dwc3: gadget: Add disconnect checking when changing function dynamically
@ 2016-09-08 11:36 Baolin Wang
  2016-09-08 12:00 ` Felipe Balbi
  0 siblings, 1 reply; 3+ messages in thread
From: Baolin Wang @ 2016-09-08 11:36 UTC (permalink / raw)
  To: balbi, gregkh; +Cc: peter, broonie, linux-usb, linux-kernel, baolin.wang

When we change the USB function with configfs dynamically, we possibly met this
situation: one core is doing the control transfer, another core is trying to
unregister the USB gadget from userspace, we must wait for completing this
control tranfer, or it will hang the controller to set the DEVCTRLHLT flag.

Also adding some disconnect checking to avoid queuing any requests when the
gadget is stopped.

Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
 drivers/usb/dwc3/ep0.c    |    8 ++++++++
 drivers/usb/dwc3/gadget.c |   32 +++++++++++++++++++++++++++-----
 2 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index fe79d77..11519d7 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -228,6 +228,14 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
 	int				ret;
 
 	spin_lock_irqsave(&dwc->lock, flags);
+	if (dwc->pullups_connected == false) {
+		dwc3_trace(trace_dwc3_ep0,
+			"queuing request %p to %s when gadget is disconnected",
+			request, dep->name);
+		ret = -ESHUTDOWN;
+		goto out;
+	}
+
 	if (!dep->endpoint.desc) {
 		dwc3_trace(trace_dwc3_ep0,
 				"trying to queue request %p to disabled %s",
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 1783406..bbac8f5 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1040,6 +1040,13 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 	struct dwc3		*dwc = dep->dwc;
 	int			ret;
 
+	if (dwc->pullups_connected == false) {
+		dwc3_trace(trace_dwc3_gadget,
+			"queuing request %p to %s when gadget is disconnected",
+			&req->request, dep->endpoint.name);
+		return -ESHUTDOWN;
+	}
+
 	if (!dep->endpoint.desc) {
 		dwc3_trace(trace_dwc3_gadget,
 				"trying to queue request %p to disabled %s",
@@ -1434,6 +1441,13 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 	if (pm_runtime_suspended(dwc->dev))
 		return 0;
 
+	/*
+	 * Per databook, when we want to stop the gadget, if a control transfer
+	 * is still in process, complete it and get the core into setup phase.
+	 */
+	if (!is_on && dwc->ep0state != EP0_SETUP_PHASE)
+		return -EBUSY;
+
 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 	if (is_on) {
 		if (dwc->revision <= DWC3_REVISION_187A) {
@@ -1481,12 +1495,20 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
 	struct dwc3		*dwc = gadget_to_dwc(g);
 	unsigned long		flags;
 	int			ret;
+	int			timeout = 500;
 
 	is_on = !!is_on;
 
-	spin_lock_irqsave(&dwc->lock, flags);
-	ret = dwc3_gadget_run_stop(dwc, is_on, false);
-	spin_unlock_irqrestore(&dwc->lock, flags);
+	do {
+		spin_lock_irqsave(&dwc->lock, flags);
+		ret = dwc3_gadget_run_stop(dwc, is_on, false);
+		spin_unlock_irqrestore(&dwc->lock, flags);
+
+		if (ret != -EBUSY)
+			break;
+
+		udelay(10);
+	} while (--timeout);
 
 	return ret;
 }
@@ -1990,7 +2012,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
 	 * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
 	 * early on so DWC3_EP_BUSY flag gets cleared
 	 */
-	if (!dep->endpoint.desc)
+	if (!dep->endpoint.desc || dwc->pullups_connected == false)
 		return 1;
 
 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
@@ -2064,7 +2086,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
 	 * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
 	 * early on so DWC3_EP_BUSY flag gets cleared
 	 */
-	if (!dep->endpoint.desc)
+	if (!dep->endpoint.desc || dwc->pullups_connected == false)
 		return;
 
 	if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] usb: dwc3: gadget: Add disconnect checking when changing function dynamically
  2016-09-08 11:36 [PATCH] usb: dwc3: gadget: Add disconnect checking when changing function dynamically Baolin Wang
@ 2016-09-08 12:00 ` Felipe Balbi
  2016-09-09  2:07   ` Baolin Wang
  0 siblings, 1 reply; 3+ messages in thread
From: Felipe Balbi @ 2016-09-08 12:00 UTC (permalink / raw)
  To: Baolin Wang, gregkh; +Cc: peter, broonie, linux-usb, linux-kernel, baolin.wang

[-- Attachment #1: Type: text/plain, Size: 4253 bytes --]


Hi,

Baolin Wang <baolin.wang@linaro.org> writes:
> When we change the USB function with configfs dynamically, we possibly met this
> situation: one core is doing the control transfer, another core is trying to
> unregister the USB gadget from userspace, we must wait for completing this
> control tranfer, or it will hang the controller to set the DEVCTRLHLT flag.
>
> Also adding some disconnect checking to avoid queuing any requests when the
> gadget is stopped.
>
> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
> ---
>  drivers/usb/dwc3/ep0.c    |    8 ++++++++
>  drivers/usb/dwc3/gadget.c |   32 +++++++++++++++++++++++++++-----
>  2 files changed, 35 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
> index fe79d77..11519d7 100644
> --- a/drivers/usb/dwc3/ep0.c
> +++ b/drivers/usb/dwc3/ep0.c
> @@ -228,6 +228,14 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
>  	int				ret;
>  
>  	spin_lock_irqsave(&dwc->lock, flags);
> +	if (dwc->pullups_connected == false) {
> +		dwc3_trace(trace_dwc3_ep0,
> +			"queuing request %p to %s when gadget is disconnected",
> +			request, dep->name);
> +		ret = -ESHUTDOWN;
> +		goto out;
> +	}

this could go on its own patch, however we can use if
(!dwc->pullups_connected) instead.

> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index 1783406..bbac8f5 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -1040,6 +1040,13 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
>  	struct dwc3		*dwc = dep->dwc;
>  	int			ret;
>  
> +	if (dwc->pullups_connected == false) {
> +		dwc3_trace(trace_dwc3_gadget,
> +			"queuing request %p to %s when gadget is disconnected",
> +			&req->request, dep->endpoint.name);
> +		return -ESHUTDOWN;
> +	}

ditto

> @@ -1434,6 +1441,13 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
>  	if (pm_runtime_suspended(dwc->dev))
>  		return 0;
>  
> +	/*
> +	 * Per databook, when we want to stop the gadget, if a control transfer
> +	 * is still in process, complete it and get the core into setup phase.
> +	 */

Do you have the section reference for this? Which databook version are
you reading?

> +	if (!is_on && dwc->ep0state != EP0_SETUP_PHASE)
> +		return -EBUSY;
> +
>  	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
>  	if (is_on) {
>  		if (dwc->revision <= DWC3_REVISION_187A) {
> @@ -1481,12 +1495,20 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
>  	struct dwc3		*dwc = gadget_to_dwc(g);
>  	unsigned long		flags;
>  	int			ret;
> +	int			timeout = 500;
>  
>  	is_on = !!is_on;
>  
> -	spin_lock_irqsave(&dwc->lock, flags);
> -	ret = dwc3_gadget_run_stop(dwc, is_on, false);
> -	spin_unlock_irqrestore(&dwc->lock, flags);
> +	do {
> +		spin_lock_irqsave(&dwc->lock, flags);
> +		ret = dwc3_gadget_run_stop(dwc, is_on, false);
> +		spin_unlock_irqrestore(&dwc->lock, flags);
> +
> +		if (ret != -EBUSY)
> +			break;
> +
> +		udelay(10);
> +	} while (--timeout);

no, this is not a good idea at all. I'd rather see:

wait_event_timeout(dwc->wq, dwc->ep0state == EP0_SETUP_PHASE,
	jiffies + msecs_to_jiffies(500));

or, perhaps, a wait_for_completion_timeout(). Then, ep0.c needs to call
complete() everytime Status Phase completes. That's probably a better
idea ;-)

> @@ -1990,7 +2012,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
>  	 * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
>  	 * early on so DWC3_EP_BUSY flag gets cleared
>  	 */
> -	if (!dep->endpoint.desc)
> +	if (!dep->endpoint.desc || dwc->pullups_connected == false)

is this really necessary? Can we really get pullups_connect set to false
with dep->endpoint.desc still valid?

> @@ -2064,7 +2086,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
>  	 * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
>  	 * early on so DWC3_EP_BUSY flag gets cleared
>  	 */
> -	if (!dep->endpoint.desc)
> +	if (!dep->endpoint.desc || dwc->pullups_connected == false)

ditto

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 800 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] usb: dwc3: gadget: Add disconnect checking when changing function dynamically
  2016-09-08 12:00 ` Felipe Balbi
@ 2016-09-09  2:07   ` Baolin Wang
  0 siblings, 0 replies; 3+ messages in thread
From: Baolin Wang @ 2016-09-09  2:07 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Greg KH, Peter Hurley, Mark Brown, USB, LKML

Hi Felipe,

On 8 September 2016 at 20:00, Felipe Balbi <balbi@kernel.org> wrote:
>
> Hi,
>
> Baolin Wang <baolin.wang@linaro.org> writes:
>> When we change the USB function with configfs dynamically, we possibly met this
>> situation: one core is doing the control transfer, another core is trying to
>> unregister the USB gadget from userspace, we must wait for completing this
>> control tranfer, or it will hang the controller to set the DEVCTRLHLT flag.
>>
>> Also adding some disconnect checking to avoid queuing any requests when the
>> gadget is stopped.
>>
>> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
>> ---
>>  drivers/usb/dwc3/ep0.c    |    8 ++++++++
>>  drivers/usb/dwc3/gadget.c |   32 +++++++++++++++++++++++++++-----
>>  2 files changed, 35 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
>> index fe79d77..11519d7 100644
>> --- a/drivers/usb/dwc3/ep0.c
>> +++ b/drivers/usb/dwc3/ep0.c
>> @@ -228,6 +228,14 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
>>       int                             ret;
>>
>>       spin_lock_irqsave(&dwc->lock, flags);
>> +     if (dwc->pullups_connected == false) {
>> +             dwc3_trace(trace_dwc3_ep0,
>> +                     "queuing request %p to %s when gadget is disconnected",
>> +                     request, dep->name);
>> +             ret = -ESHUTDOWN;
>> +             goto out;
>> +     }
>
> this could go on its own patch, however we can use if
> (!dwc->pullups_connected) instead.

Indeed. I will split it into one separate patch.

>
>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
>> index 1783406..bbac8f5 100644
>> --- a/drivers/usb/dwc3/gadget.c
>> +++ b/drivers/usb/dwc3/gadget.c
>> @@ -1040,6 +1040,13 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
>>       struct dwc3             *dwc = dep->dwc;
>>       int                     ret;
>>
>> +     if (dwc->pullups_connected == false) {
>> +             dwc3_trace(trace_dwc3_gadget,
>> +                     "queuing request %p to %s when gadget is disconnected",
>> +                     &req->request, dep->endpoint.name);
>> +             return -ESHUTDOWN;
>> +     }
>
> ditto

OK.

>
>> @@ -1434,6 +1441,13 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
>>       if (pm_runtime_suspended(dwc->dev))
>>               return 0;
>>
>> +     /*
>> +      * Per databook, when we want to stop the gadget, if a control transfer
>> +      * is still in process, complete it and get the core into setup phase.
>> +      */
>
> Do you have the section reference for this? Which databook version are
> you reading?

The databook version is 2.80a and you can find this description in
section 8.1.8 "Device-Initiated Disconnect".

>
>> +     if (!is_on && dwc->ep0state != EP0_SETUP_PHASE)
>> +             return -EBUSY;
>> +
>>       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
>>       if (is_on) {
>>               if (dwc->revision <= DWC3_REVISION_187A) {
>> @@ -1481,12 +1495,20 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
>>       struct dwc3             *dwc = gadget_to_dwc(g);
>>       unsigned long           flags;
>>       int                     ret;
>> +     int                     timeout = 500;
>>
>>       is_on = !!is_on;
>>
>> -     spin_lock_irqsave(&dwc->lock, flags);
>> -     ret = dwc3_gadget_run_stop(dwc, is_on, false);
>> -     spin_unlock_irqrestore(&dwc->lock, flags);
>> +     do {
>> +             spin_lock_irqsave(&dwc->lock, flags);
>> +             ret = dwc3_gadget_run_stop(dwc, is_on, false);
>> +             spin_unlock_irqrestore(&dwc->lock, flags);
>> +
>> +             if (ret != -EBUSY)
>> +                     break;
>> +
>> +             udelay(10);
>> +     } while (--timeout);
>
> no, this is not a good idea at all. I'd rather see:
>
> wait_event_timeout(dwc->wq, dwc->ep0state == EP0_SETUP_PHASE,
>         jiffies + msecs_to_jiffies(500));
>
> or, perhaps, a wait_for_completion_timeout(). Then, ep0.c needs to call
> complete() everytime Status Phase completes. That's probably a better
> idea ;-)

Yes, you are right. I will fix that with your suggestion. Thanks.

>
>> @@ -1990,7 +2012,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
>>        * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
>>        * early on so DWC3_EP_BUSY flag gets cleared
>>        */
>> -     if (!dep->endpoint.desc)
>> +     if (!dep->endpoint.desc || dwc->pullups_connected == false)
>
> is this really necessary? Can we really get pullups_connect set to false
> with dep->endpoint.desc still valid?

Yes, when we start to unregister gadget by
usb_gadget_unregister_driver(), the first thing is disconnect the
gadget by usb_gadget_disconnect() to set pullups_connect as false,
then will disable the endpoints by composite_disconnect() to set
dep->endpoint.desc as NULL. When 'dwc->pullups_connected' is set
false, we also need to avoiding transfer.

>
>> @@ -2064,7 +2086,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
>>        * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
>>        * early on so DWC3_EP_BUSY flag gets cleared
>>        */
>> -     if (!dep->endpoint.desc)
>> +     if (!dep->endpoint.desc || dwc->pullups_connected == false)
>
> ditto
>
> --
> balbi



-- 
Baolin.wang
Best Regards

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2016-09-09  2:07 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-08 11:36 [PATCH] usb: dwc3: gadget: Add disconnect checking when changing function dynamically Baolin Wang
2016-09-08 12:00 ` Felipe Balbi
2016-09-09  2:07   ` Baolin Wang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).