Since:

commit 855ed04a3758 ("usb: gadget: udc-core: independent registration
of gadgets and gadget drivers")

if we load gadget module but there is no free udc available
then it will be stored on a pending gadgets list.

$ modprobe g_zero.ko
$ modprobe g_ether.ko
[] udc-core: couldn't find an available UDC - added [g_ether] to list
of pending drivers

We scan this list each time when new UDC appears in system.
But we can get a free UDC each time after gadget unbind.
This commit add scanning of that list directly after unbinding
gadget from udc.

Thanks to this, when we unload first gadget:

$ rmmod g_zero.ko

gadget which is pending is automatically
attached to that UDC (if name matches).

Fixes: 855ed04a3758  ("usb: gadget: udc-core: independent registration of 
gadgets and gadget drivers")
Cc: stable <sta...@vger.kernel.org>
Signed-off-by: Krzysztof Opasiak <k.opas...@samsung.com>
---
Changes since v2:
- cc stable
Changes since v1:
- remove unused variable driver
---
 drivers/usb/gadget/udc/core.c | 45 +++++++++++++++++++++++++++++--------------
 1 file changed, 31 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 9483489080f6..73292459acd6 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1080,6 +1080,24 @@ static void usb_udc_nop_release(struct device *dev)
        dev_vdbg(dev, "%s\n", __func__);
 }
 
+/* should be called with udc_lock held */
+static int check_pending_gadget_drivers(struct usb_udc *udc)
+{
+       struct usb_gadget_driver *driver;
+       int ret = 0;
+
+       list_for_each_entry(driver, &gadget_driver_pending_list, pending)
+               if (!driver->udc_name || strcmp(driver->udc_name,
+                                               dev_name(&udc->dev)) == 0) {
+                       ret = udc_bind_to_driver(udc, driver);
+                       if (ret != -EPROBE_DEFER)
+                               list_del(&driver->pending);
+                       break;
+               }
+
+       return ret;
+}
+
 /**
  * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
  * @parent: the parent device to this udc. Usually the controller driver's
@@ -1093,7 +1111,6 @@ int usb_add_gadget_udc_release(struct device *parent, 
struct usb_gadget *gadget,
                void (*release)(struct device *dev))
 {
        struct usb_udc          *udc;
-       struct usb_gadget_driver *driver;
        int                     ret = -ENOMEM;
 
        udc = kzalloc(sizeof(*udc), GFP_KERNEL);
@@ -1136,17 +1153,9 @@ int usb_add_gadget_udc_release(struct device *parent, 
struct usb_gadget *gadget,
        udc->vbus = true;
 
        /* pick up one of pending gadget drivers */
-       list_for_each_entry(driver, &gadget_driver_pending_list, pending) {
-               if (!driver->udc_name || strcmp(driver->udc_name,
-                                               dev_name(&udc->dev)) == 0) {
-                       ret = udc_bind_to_driver(udc, driver);
-                       if (ret != -EPROBE_DEFER)
-                               list_del(&driver->pending);
-                       if (ret)
-                               goto err5;
-                       break;
-               }
-       }
+       ret = check_pending_gadget_drivers(udc);
+       if (ret)
+               goto err5;
 
        mutex_unlock(&udc_lock);
 
@@ -1352,14 +1361,22 @@ int usb_gadget_unregister_driver(struct 
usb_gadget_driver *driver)
                return -EINVAL;
 
        mutex_lock(&udc_lock);
-       list_for_each_entry(udc, &udc_list, list)
+       list_for_each_entry(udc, &udc_list, list) {
                if (udc->driver == driver) {
                        usb_gadget_remove_driver(udc);
                        usb_gadget_set_state(udc->gadget,
-                                       USB_STATE_NOTATTACHED);
+                                            USB_STATE_NOTATTACHED);
+
+                       /* Maybe there is someone waiting for this UDC? */
+                       check_pending_gadget_drivers(udc);
+                       /*
+                        * For now we ignore bind errors as probably it's
+                        * not a valid reason to fail other's gadget unbind
+                        */
                        ret = 0;
                        break;
                }
+       }
 
        if (ret) {
                list_del(&driver->pending);
-- 
2.9.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