This commit fixes the way gadget's pullup method (wrapped at
usb_gadget_connect/disconnect) is called in the udc-core.

The composite driver allows correct driver registration, even when it calls
the usb_gadget_disconnect method (composite driver configuration is defered 
for user space - please look into the description of usb_composite_probe at
composite.c - line: 1623)

One such example is the CCG (Configurable Composite Gadget) driver (at
drivers/staging/ccg), which after its registration has no usb descriptor
(i.e. idProduct, idVendor etc.) and functions registered. Those are configured
after writing to /sys/module/g_ccg/parameters/ or /sys/class/ccg_usb/ccg0/.

Unfortunately, the code at 'usb_gadget_probe_driver' method (some code omitted):

        if (udc_is_newstyle(udc)) {
                bind(udc->gadget);
                usb_gadget_udc_start(udc->gadget, driver);
                usb_gadget_connect(udc->gadget);
        }

Explicitly calls the usb_gadget_connect method for this driver. It looks like
the udc-core enables pullup for a driver, which has no functions and no
descriptor filled (those values are feed from userspace).

The USB composite driver API allows correct driver registration with calling 
usb_gadget_disconnect method, but as it is now, _ALL_ newstyle usb gadgets are
connected by default. Therefore it violates the composite API.

The solution (at least until the udc-core is reworked) is to add atomic
variable, which helps in balancing the number of called usb_gadget_connect/
disconnect functions.

Signed-off-by: Lukasz Majewski <l.majew...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/usb/gadget/udc-core.c |   17 ++++++++++++++++-
 include/linux/usb/gadget.h    |   13 +++++++++++--
 2 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index e5e44f8..a26517e 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -349,7 +349,22 @@ found:
                }
                usb_gadget_connect(udc->gadget);
        } else {
-
+               /*
+                * This is a hack for "old style" gadgets:
+                *
+                * The udc_start for "old style" gadgets performs implicitly all
+                * operations done by usb_gadget_connect(but not calling it).
+                * Therefore non composite gadgets (like rndis) works even with
+                * wrong connect_count value ("old style" gadgets don't call
+                * usb_gadget_connect/disconnect).
+                *
+                * On the other hand the CCG (Configurable Composite Gadget)
+                * requires this incrementation since it calls
+                * usb_gadget_disconnect on its probe (it is allowed) and hence
+                * the proper balance is needed when the usb_gadget_connect(i.e.
+                * pullup) is called, when triggered from userspace event.
+                */
+               atomic_inc(&udc->gadget->connect_count);
                ret = usb_gadget_start(udc->gadget, driver, bind);
                if (ret)
                        goto err1;
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 9517466..0801d83 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -534,6 +534,7 @@ struct usb_gadget {
        unsigned                        b_hnp_enable:1;
        unsigned                        a_hnp_support:1;
        unsigned                        a_alt_hnp_support:1;
+       atomic_t                        connect_count;
        const char                      *name;
        struct device                   dev;
 };
@@ -739,7 +740,11 @@ static inline int usb_gadget_connect(struct usb_gadget 
*gadget)
 {
        if (!gadget->ops->pullup)
                return -EOPNOTSUPP;
-       return gadget->ops->pullup(gadget, 1);
+
+       if (atomic_inc_return(&gadget->connect_count) == 1)
+               return gadget->ops->pullup(gadget, 1);
+
+       return 0;
 }
 
 /**
@@ -761,7 +766,11 @@ static inline int usb_gadget_disconnect(struct usb_gadget 
*gadget)
 {
        if (!gadget->ops->pullup)
                return -EOPNOTSUPP;
-       return gadget->ops->pullup(gadget, 0);
+
+       if (atomic_dec_and_test(&gadget->connect_count))
+               return gadget->ops->pullup(gadget, 0);
+
+       return 0;
 }
 
 
-- 
1.7.2.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to