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.majewski@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html