This is useful on gadgets that depend on userland
daemons to function properly. We can delay connection
to the host until userland is ready.

Signed-off-by: Felipe Balbi <ba...@ti.com>
---
 drivers/usb/gadget/composite.c |  3 ++-
 drivers/usb/gadget/udc-core.c  | 26 ++++++++++++++++++++++++--
 include/linux/usb/composite.h  |  4 ++++
 include/linux/usb/gadget.h     |  3 +++
 4 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 7c821de..790ccf2 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1784,8 +1784,9 @@ int usb_composite_probe(struct usb_composite_driver 
*driver)
                driver->name = "composite";
 
        driver->gadget_driver = composite_driver_template;
-       gadget_driver = &driver->gadget_driver;
 
+       gadget_driver = &driver->gadget_driver;
+       gadget_driver->controls_pullups = driver->controls_pullups;
        gadget_driver->function =  (char *) driver->name;
        gadget_driver->driver.name = driver->name;
        gadget_driver->max_speed = driver->max_speed;
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index 8a1eeb2..c0f4fca 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -235,7 +235,18 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
 
        kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
 
-       usb_gadget_disconnect(udc->gadget);
+       /*
+        * NOTICE: if gadget driver wants to control
+        * pullup, it needs to make sure that when
+        * user tries to rmmod the gadget driver, it
+        * will disconnect the pullups before returning
+        * from its ->unbind() method.
+        *
+        * We are truly trusting the gadget driver here.
+        */
+       if (!udc->driver->controls_pullups)
+               usb_gadget_disconnect(udc->gadget);
+
        udc->driver->disconnect(udc->gadget);
        udc->driver->unbind(udc->gadget);
        usb_gadget_udc_stop(udc->gadget, udc->driver);
@@ -300,7 +311,18 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct 
usb_gadget_driver *dri
                driver->unbind(udc->gadget);
                goto err1;
        }
-       usb_gadget_connect(udc->gadget);
+
+       /*
+        * NOTICE: if gadget driver wants to control
+        * pullups, it needs to make sure its calls
+        * to usb_function_activate() and
+        * usb_function_deactivate() are balanced,
+        * otherwise gadget_driver will never enumerate.
+        *
+        * We are truly trusting the gadget driver here.
+        */
+       if (!driver->controls_pullups)
+               usb_gadget_connect(udc->gadget);
 
        kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
        return 0;
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 3c671c1..7ae797c 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -157,6 +157,7 @@ struct usb_function {
        int                     (*get_status)(struct usb_function *);
        int                     (*func_suspend)(struct usb_function *,
                                                u8 suspend_opt);
+
        /* private: */
        /* internals */
        struct list_head                list;
@@ -279,6 +280,8 @@ enum {
  * @max_speed: Highest speed the driver supports.
  * @needs_serial: set to 1 if the gadget needs userspace to provide
  *     a serial number.  If one is not provided, warning will be printed.
+ * @controls_pullups: this driver will control pullup and udc-core shouldn't
+ *     enable it by default
  * @bind: (REQUIRED) Used to allocate resources that are shared across the
  *     whole device, such as string IDs, and add its configurations using
  *     @usb_add_config(). This may fail by returning a negative errno
@@ -308,6 +311,7 @@ struct usb_composite_driver {
        struct usb_gadget_strings               **strings;
        enum usb_device_speed                   max_speed;
        unsigned                needs_serial:1;
+       unsigned                controls_pullups:1;
 
        int                     (*bind)(struct usb_composite_dev *cdev);
        int                     (*unbind)(struct usb_composite_dev *);
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 32b734d..87971fa 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -774,6 +774,7 @@ static inline int usb_gadget_disconnect(struct usb_gadget 
*gadget)
  * @suspend: Invoked on USB suspend.  May be called in_interrupt.
  * @resume: Invoked on USB resume.  May be called in_interrupt.
  * @driver: Driver model state for this driver.
+ * @controls_pullups: tells udc-core to not enable pullups by default
  *
  * Devices are disabled till a gadget driver successfully bind()s, which
  * means the driver will handle setup() requests needed to enumerate (and
@@ -833,6 +834,8 @@ struct usb_gadget_driver {
 
        /* FIXME support safe rmmod */
        struct device_driver    driver;
+
+       unsigned                controls_pullups:1;
 };
 
 
-- 
1.8.1.rc1.5.g7e0651a

--
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