diff mbox

[14/14] usb: udc-core: add judgement logic for usb_gadget_connect

Message ID 1363240242-25775-15-git-send-email-peter.chen@freescale.com (mailing list archive)
State New, archived
Headers show

Commit Message

Peter Chen March 14, 2013, 5:50 a.m. UTC
- If there is no vbus control to indicate connection.
and disconnect, we can pullup dp when we load gadget module.
- If we have vbus control logic, the dp is better pull up
when there is a vbus session.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
---
 drivers/usb/gadget/udc-core.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)

Comments

Felipe Balbi March 14, 2013, 9 a.m. UTC | #1
Hi,

On Thu, Mar 14, 2013 at 01:50:42PM +0800, Peter Chen wrote:
> - If there is no vbus control to indicate connection.
> and disconnect, we can pullup dp when we load gadget module.
> - If we have vbus control logic, the dp is better pull up
> when there is a vbus session.
> 
> Signed-off-by: Peter Chen <peter.chen@freescale.com>
> ---
>  drivers/usb/gadget/udc-core.c |   10 ++++++++--
>  1 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
> index 4d90bdf..4b56f7c 100644
> --- a/drivers/usb/gadget/udc-core.c
> +++ b/drivers/usb/gadget/udc-core.c
> @@ -278,7 +278,10 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
>  		driver->unbind(gadget);
>  		goto err1;
>  	}
> -	usb_gadget_connect(gadget);
> +	if (!gadget->ops->vbus_session ||
> +			(gadget->ops->vbus_session
> +				&& gadget->vbus_active))
> +		usb_gadget_connect(gadget);
>  
>  	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
>  	return 0;
> @@ -384,7 +387,10 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
>  
>  	if (sysfs_streq(buf, "connect")) {
>  		usb_gadget_udc_start(gadget, udc->driver);
> -		usb_gadget_connect(gadget);
> +		if (!gadget->ops->vbus_session ||
> +				(gadget->ops->vbus_session
> +					&& gadget->vbus_active))
> +			usb_gadget_connect(gadget);

this patch is incomplete. What happens if this test fails ? Who will
connect pullup then ?
Peter Chen March 14, 2013, 9:24 a.m. UTC | #2
On Thu, Mar 14, 2013 at 11:00:05AM +0200, Felipe Balbi wrote:
> Hi,
> 
> On Thu, Mar 14, 2013 at 01:50:42PM +0800, Peter Chen wrote:
> > - If there is no vbus control to indicate connection.
> > and disconnect, we can pullup dp when we load gadget module.
> > - If we have vbus control logic, the dp is better pull up
> > when there is a vbus session.
> > 
> > Signed-off-by: Peter Chen <peter.chen@freescale.com>
> > ---
> >  drivers/usb/gadget/udc-core.c |   10 ++++++++--
> >  1 files changed, 8 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
> > index 4d90bdf..4b56f7c 100644
> > --- a/drivers/usb/gadget/udc-core.c
> > +++ b/drivers/usb/gadget/udc-core.c
> > @@ -278,7 +278,10 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
> >  		driver->unbind(gadget);
> >  		goto err1;
> >  	}
> > -	usb_gadget_connect(gadget);
> > +	if (!gadget->ops->vbus_session ||
> > +			(gadget->ops->vbus_session
> > +				&& gadget->vbus_active))
> > +		usb_gadget_connect(gadget);
> >  
> >  	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
> >  	return 0;
> > @@ -384,7 +387,10 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
> >  
> >  	if (sysfs_streq(buf, "connect")) {
> >  		usb_gadget_udc_start(gadget, udc->driver);
> > -		usb_gadget_connect(gadget);
> > +		if (!gadget->ops->vbus_session ||
> > +				(gadget->ops->vbus_session
> > +					&& gadget->vbus_active))
> > +			usb_gadget_connect(gadget);
> 
> this patch is incomplete. What happens if this test fails ? Who will
> connect pullup then ?

gadget->ops->vbus_session will handle it when the vbus interrupt comes

> 
> -- 
> balbi
Felipe Balbi March 14, 2013, 10:19 a.m. UTC | #3
Hi,

On Thu, Mar 14, 2013 at 05:24:39PM +0800, Peter Chen wrote:
> > > @@ -278,7 +278,10 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
> > >  		driver->unbind(gadget);
> > >  		goto err1;
> > >  	}
> > > -	usb_gadget_connect(gadget);
> > > +	if (!gadget->ops->vbus_session ||
> > > +			(gadget->ops->vbus_session
> > > +				&& gadget->vbus_active))
> > > +		usb_gadget_connect(gadget);
> > >  
> > >  	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
> > >  	return 0;
> > > @@ -384,7 +387,10 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
> > >  
> > >  	if (sysfs_streq(buf, "connect")) {
> > >  		usb_gadget_udc_start(gadget, udc->driver);
> > > -		usb_gadget_connect(gadget);
> > > +		if (!gadget->ops->vbus_session ||
> > > +				(gadget->ops->vbus_session
> > > +					&& gadget->vbus_active))
> > > +			usb_gadget_connect(gadget);
> > 
> > this patch is incomplete. What happens if this test fails ? Who will
> > connect pullup then ?
> 
> gadget->ops->vbus_session will handle it when the vbus interrupt comes

for your driver, what about all the others ? Also, we shouldn't allow
this ping-pong between who handles pullup and who handles vbus_session.

It should all be managed by udc-core with UDC drivers just providing
enough hooks. If we allow the UDC driver to connect pullups when VBUS
IRQ comes, we could fall into all sorts of traps:

a) we could connect cable with no gadget driver loaded
b) there will be code duplication among all UDC drivers to call
	usb_gadge_connect() from vbus_session
c) we might screw up the usb_function_activate()/deactivate() counter

Need to be very careful with all these details, there are many, many
users to udc-core with different requirements. So unless we can come up
with a way which wouldn't cause code duplication or regressions, I don't
think we can solve the real problem.

I guess the best solution to all problems is to start deferring
pullup to when gadget driver says it's ok to connect them. It's far more
likely that we will already have connection to a host and VBUS will be
alive.

Also, I'm not sure what does this all mean for SRP. Should we connect
pullup before or after SRP ?
Peter Chen March 15, 2013, 6:06 a.m. UTC | #4
On Thu, Mar 14, 2013 at 12:19:04PM +0200, Felipe Balbi wrote:
> Hi,
> 
> On Thu, Mar 14, 2013 at 05:24:39PM +0800, Peter Chen wrote:
> > > > @@ -278,7 +278,10 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
> > > >  		driver->unbind(gadget);
> > > >  		goto err1;
> > > >  	}
> > > > -	usb_gadget_connect(gadget);
> > > > +	if (!gadget->ops->vbus_session ||
> > > > +			(gadget->ops->vbus_session
> > > > +				&& gadget->vbus_active))
> > > > +		usb_gadget_connect(gadget);
> > > >  
> > > >  	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
> > > >  	return 0;
> > > > @@ -384,7 +387,10 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
> > > >  
> > > >  	if (sysfs_streq(buf, "connect")) {
> > > >  		usb_gadget_udc_start(gadget, udc->driver);
> > > > -		usb_gadget_connect(gadget);
> > > > +		if (!gadget->ops->vbus_session ||
> > > > +				(gadget->ops->vbus_session
> > > > +					&& gadget->vbus_active))
> > > > +			usb_gadget_connect(gadget);
> > > 
> > > this patch is incomplete. What happens if this test fails ? Who will
> > > connect pullup then ?
> > 
> > gadget->ops->vbus_session will handle it when the vbus interrupt comes
> 
> for your driver, what about all the others ?

All drivers have .vbus_session will try to pullup, but some still check
if .pullup (using .softconnect) is called beforehand, it is duplicated
with this patch. Sorry, my careless.
If you have a look with these drivers, even usb_gadget_connect is called
at udc_bind_to_driver, they will NOT pull up if vbus is not there.

The most strict condition is : 
gadget_is_loaded && vbus_session_is_called && pullup_is_called

In fact, calling usb_gadget_connect(gadget) unconditionally at udc-core
may hang system if low power is enabled as udc will enter low
power mode after udc_start if no vbus is there.

> Also, we shouldn't allow
> this ping-pong between who handles pullup and who handles vbus_session.
> 
> It should all be managed by udc-core with UDC drivers just providing
> enough hooks. If we allow the UDC driver to connect pullups when VBUS
> IRQ comes, we could fall into all sorts of traps:
> 
> a) we could connect cable with no gadget driver loaded

In that case, the pullup will not be called, it will check if gadget module
is loaded.

> b) there will be code duplication among all UDC drivers to call
> 	usb_gadge_connect() from vbus_session

Yes

> c) we might screw up the usb_function_activate()/deactivate() counter
> 

why?

> Need to be very careful with all these details, there are many, many
> users to udc-core with different requirements. So unless we can come up
> with a way which wouldn't cause code duplication or regressions, I don't
> think we can solve the real problem.

Yes, udc has vendor specific, no uniform spec like host.

> 
> I guess the best solution to all problems is to start deferring
> pullup to when gadget driver says it's ok to connect them. It's far more
> likely that we will already have connection to a host and VBUS will be
> alive.

I still think (gadget_is_loaded && vbus_is_there) is enough.

> 
> Also, I'm not sure what does this all mean for SRP. Should we connect
> pullup before or after SRP ?

I am not familiar with SRP, but I think vbus is pre-condition.
Felipe Balbi March 15, 2013, 7:51 a.m. UTC | #5
Hi,

On Fri, Mar 15, 2013 at 02:06:16PM +0800, Peter Chen wrote:
> > > > > @@ -278,7 +278,10 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
> > > > >  		driver->unbind(gadget);
> > > > >  		goto err1;
> > > > >  	}
> > > > > -	usb_gadget_connect(gadget);
> > > > > +	if (!gadget->ops->vbus_session ||
> > > > > +			(gadget->ops->vbus_session
> > > > > +				&& gadget->vbus_active))
> > > > > +		usb_gadget_connect(gadget);
> > > > >  
> > > > >  	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
> > > > >  	return 0;
> > > > > @@ -384,7 +387,10 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
> > > > >  
> > > > >  	if (sysfs_streq(buf, "connect")) {
> > > > >  		usb_gadget_udc_start(gadget, udc->driver);
> > > > > -		usb_gadget_connect(gadget);
> > > > > +		if (!gadget->ops->vbus_session ||
> > > > > +				(gadget->ops->vbus_session
> > > > > +					&& gadget->vbus_active))
> > > > > +			usb_gadget_connect(gadget);
> > > > 
> > > > this patch is incomplete. What happens if this test fails ? Who will
> > > > connect pullup then ?
> > > 
> > > gadget->ops->vbus_session will handle it when the vbus interrupt comes
> > 
> > for your driver, what about all the others ?
> 
> All drivers have .vbus_session will try to pullup, but some still check
> if .pullup (using .softconnect) is called beforehand, it is duplicated
> with this patch. Sorry, my careless.
> If you have a look with these drivers, even usb_gadget_connect is called
> at udc_bind_to_driver, they will NOT pull up if vbus is not there.

that's all wrong and needs to be fixed. UDC-core has to be the central
point for all these details. We start with the easiest way (call connect
unconditionally after gadget driver is loaded) and optimize it as we go
(making sure that there is VBUS before connecting pullups); but we can't
bypass UDC-core and call usb_gadget_connect() directly from UDC driver.

> The most strict condition is : 
> gadget_is_loaded && vbus_session_is_called && pullup_is_called
> 
> In fact, calling usb_gadget_connect(gadget) unconditionally at udc-core
> may hang system if low power is enabled as udc will enter low
> power mode after udc_start if no vbus is there.

that's a bug in the UDC driver though. It should add proper
pm_runtime_get_sync() and pm_runtime_put() (or put_sync()) around
register accesses, which means that ->pullup() should take care of that
too.

> > Also, we shouldn't allow
> > this ping-pong between who handles pullup and who handles vbus_session.
> > 
> > It should all be managed by udc-core with UDC drivers just providing
> > enough hooks. If we allow the UDC driver to connect pullups when VBUS
> > IRQ comes, we could fall into all sorts of traps:
> > 
> > a) we could connect cable with no gadget driver loaded
> 
> In that case, the pullup will not be called, it will check if gadget
> module is loaded.

I don't see that in your patch.

> > b) there will be code duplication among all UDC drivers to call
> > 	usb_gadge_connect() from vbus_session
> 
> Yes

yeah, we need to avoid it.

> > c) we might screw up the usb_function_activate()/deactivate() counter
> > 
> 
> why?

(USB cable already attached to a host, VBUS alive) gadget driver is
loaded, gadget driver calls usb_function_deactive() to prevent
enumeration until userland is ready, UDC driver calls
usb_gadget_connect() because there is a gadget driver and vbus is alive.

> > Need to be very careful with all these details, there are many, many
> > users to udc-core with different requirements. So unless we can come up
> > with a way which wouldn't cause code duplication or regressions, I don't
> > think we can solve the real problem.
> 
> Yes, udc has vendor specific, no uniform spec like host.

there are plenty of non-uniform hosts as well. MUSB and dwc2 are just
two instances of them. Still usbcore works just fine with those and the
standard ones.

> > I guess the best solution to all problems is to start deferring
> > pullup to when gadget driver says it's ok to connect them. It's far more
> > likely that we will already have connection to a host and VBUS will be
> > alive.
> 
> I still think (gadget_is_loaded && vbus_is_there) is enough.

it's not, see above.

> > Also, I'm not sure what does this all mean for SRP. Should we connect
> > pullup before or after SRP ?
> 
> I am not familiar with SRP, but I think vbus is pre-condition.

hehe, google SRP.

Nah, I'll save you the trouble. SRP is a mechanism for the USB
peripheral to ask the host to enable VBUS. This means that we can keep
the VBUS charge pump disabled, and thus save power, until peripheral
requests for a session.
diff mbox

Patch

diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index 4d90bdf..4b56f7c 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -278,7 +278,10 @@  static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
 		driver->unbind(gadget);
 		goto err1;
 	}
-	usb_gadget_connect(gadget);
+	if (!gadget->ops->vbus_session ||
+			(gadget->ops->vbus_session
+				&& gadget->vbus_active))
+		usb_gadget_connect(gadget);
 
 	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
 	return 0;
@@ -384,7 +387,10 @@  static ssize_t usb_udc_softconn_store(struct device *dev,
 
 	if (sysfs_streq(buf, "connect")) {
 		usb_gadget_udc_start(gadget, udc->driver);
-		usb_gadget_connect(gadget);
+		if (!gadget->ops->vbus_session ||
+				(gadget->ops->vbus_session
+					&& gadget->vbus_active))
+			usb_gadget_connect(gadget);
 	} else if (sysfs_streq(buf, "disconnect")) {
 		usb_gadget_disconnect(gadget);
 		usb_gadget_udc_stop(gadget, udc->driver);