Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8236800d authored by Krzysztof Opasiak's avatar Krzysztof Opasiak Committed by Felipe Balbi
Browse files

usb: gadget: udc-core: Rescan pending list on driver unbind



Since:

commit 855ed04a ("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: 855ed04a  ("usb: gadget: udc-core: independent registration of gadgets and gadget drivers")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarKrzysztof Opasiak <k.opasiak@samsung.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent 49def185
Loading
Loading
Loading
Loading
+31 −14
Original line number Diff line number Diff line
@@ -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);
	ret = check_pending_gadget_drivers(udc);
	if (ret)
		goto err5;
			break;
		}
	}

	mutex_unlock(&udc_lock);

@@ -1356,14 +1365,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);

			/* 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);