diff options
Diffstat (limited to 'drivers/usb/gadget/udc/core.c')
-rw-r--r-- | drivers/usb/gadget/udc/core.c | 289 |
1 files changed, 143 insertions, 146 deletions
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 85b194011a16..7886497253cc 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/list.h> +#include <linux/idr.h> #include <linux/err.h> #include <linux/dma-mapping.h> #include <linux/sched/task_stack.h> @@ -23,6 +24,10 @@ #include "trace.h" +static DEFINE_IDA(gadget_id_numbers); + +static struct bus_type gadget_bus_type; + /** * struct usb_udc - describes one usb device controller * @driver: the gadget driver pointer. For use by the class code @@ -47,11 +52,9 @@ struct usb_udc { static struct class *udc_class; static LIST_HEAD(udc_list); -static LIST_HEAD(gadget_driver_pending_list); -static DEFINE_MUTEX(udc_lock); -static int udc_bind_to_driver(struct usb_udc *udc, - struct usb_gadget_driver *driver); +/* Protects udc_list, udc->driver, driver->is_bound, and related calls */ +static DEFINE_MUTEX(udc_lock); /* ------------------------------------------------------------------------- */ @@ -1238,38 +1241,16 @@ 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_init(&driver->pending); - break; - } - - return ret; -} - /** * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's * device. * @gadget: the gadget to be initialized. * @release: a gadget release function. - * - * Returns zero on success, negative errno otherwise. - * Calls the gadget release function in the latter case. */ void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)) { - dev_set_name(&gadget->dev, "gadget"); INIT_WORK(&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent; @@ -1279,6 +1260,7 @@ void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, gadget->dev.release = usb_udc_nop_release; device_initialize(&gadget->dev); + gadget->dev.bus = &gadget_bus_type; } EXPORT_SYMBOL_GPL(usb_initialize_gadget); @@ -1308,10 +1290,6 @@ int usb_add_gadget(struct usb_gadget *gadget) if (ret) goto err_put_udc; - ret = device_add(&gadget->dev); - if (ret) - goto err_put_udc; - udc->gadget = gadget; gadget->udc = udc; @@ -1319,6 +1297,7 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); + mutex_unlock(&udc_lock); ret = device_add(&udc->dev); if (ret) @@ -1327,25 +1306,30 @@ int usb_add_gadget(struct usb_gadget *gadget) usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); udc->vbus = true; - /* pick up one of pending gadget drivers */ - ret = check_pending_gadget_drivers(udc); - if (ret) + ret = ida_alloc(&gadget_id_numbers, GFP_KERNEL); + if (ret < 0) goto err_del_udc; + gadget->id_number = ret; + dev_set_name(&gadget->dev, "gadget.%d", ret); - mutex_unlock(&udc_lock); + ret = device_add(&gadget->dev); + if (ret) + goto err_free_id; return 0; + err_free_id: + ida_free(&gadget_id_numbers, gadget->id_number); + err_del_udc: flush_work(&gadget->work); device_del(&udc->dev); err_unlist_udc: + mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); - device_del(&gadget->dev); - err_put_udc: put_device(&udc->dev); @@ -1421,30 +1405,11 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) } EXPORT_SYMBOL_GPL(usb_add_gadget_udc); -static void usb_gadget_remove_driver(struct usb_udc *udc) -{ - dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", - udc->driver->function); - - kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); - - usb_gadget_disconnect(udc->gadget); - usb_gadget_disable_async_callbacks(udc); - if (udc->gadget->irq) - synchronize_irq(udc->gadget->irq); - udc->driver->unbind(udc->gadget); - usb_gadget_udc_stop(udc); - - udc->driver = NULL; - udc->gadget->dev.driver = NULL; -} - /** - * usb_del_gadget - deletes @udc from udc_list - * @gadget: the gadget to be removed. + * usb_del_gadget - deletes a gadget and unregisters its udc + * @gadget: the gadget to be deleted. * - * This will call usb_gadget_unregister_driver() if - * the @udc is still busy. + * This will unbind @gadget, if it is bound. * It will not do a final usb_put_gadget(). */ void usb_del_gadget(struct usb_gadget *gadget) @@ -1458,25 +1423,19 @@ void usb_del_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_del(&udc->list); - - if (udc->driver) { - struct usb_gadget_driver *driver = udc->driver; - - usb_gadget_remove_driver(udc); - list_add(&driver->pending, &gadget_driver_pending_list); - } mutex_unlock(&udc_lock); kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); flush_work(&gadget->work); - device_unregister(&udc->dev); device_del(&gadget->dev); + ida_free(&gadget_id_numbers, gadget->id_number); + device_unregister(&udc->dev); } EXPORT_SYMBOL_GPL(usb_del_gadget); /** - * usb_del_gadget_udc - deletes @udc from udc_list - * @gadget: the gadget to be removed. + * usb_del_gadget_udc - unregisters a gadget + * @gadget: the gadget to be unregistered. * * Calls usb_del_gadget() and does a final usb_put_gadget(). */ @@ -1489,123 +1448,147 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc); /* ------------------------------------------------------------------------- */ -static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) +static int gadget_match_driver(struct device *dev, struct device_driver *drv) { - int ret; + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = container_of(drv, + struct usb_gadget_driver, driver); - dev_dbg(&udc->dev, "registering UDC driver [%s]\n", - driver->function); + /* If the driver specifies a udc_name, it must match the UDC's name */ + if (driver->udc_name && + strcmp(driver->udc_name, dev_name(&udc->dev)) != 0) + return 0; + + /* If the driver is already bound to a gadget, it doesn't match */ + if (driver->is_bound) + return 0; + + /* Otherwise any gadget driver matches any UDC */ + return 1; +} +static int gadget_bind_driver(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = container_of(dev->driver, + struct usb_gadget_driver, driver); + int ret = 0; + + mutex_lock(&udc_lock); + if (driver->is_bound) { + mutex_unlock(&udc_lock); + return -ENXIO; /* Driver binds to only one gadget */ + } + driver->is_bound = true; udc->driver = driver; - udc->gadget->dev.driver = &driver->driver; + mutex_unlock(&udc_lock); + + dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function); usb_gadget_udc_set_speed(udc, driver->max_speed); + mutex_lock(&udc_lock); ret = driver->bind(udc->gadget, driver); if (ret) - goto err1; + goto err_bind; + ret = usb_gadget_udc_start(udc); - if (ret) { - driver->unbind(udc->gadget); - goto err1; - } + if (ret) + goto err_start; usb_gadget_enable_async_callbacks(udc); usb_udc_connect_control(udc); + mutex_unlock(&udc_lock); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; -err1: + + err_start: + driver->unbind(udc->gadget); + + err_bind: if (ret != -EISNAM) dev_err(&udc->dev, "failed to start %s: %d\n", - udc->driver->function, ret); + driver->function, ret); + udc->driver = NULL; - udc->gadget->dev.driver = NULL; + driver->is_bound = false; + mutex_unlock(&udc_lock); + return ret; } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver) +static void gadget_unbind_driver(struct device *dev) { - struct usb_udc *udc = NULL, *iter; - int ret = -ENODEV; + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = udc->driver; + + dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function); + + kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + + mutex_lock(&udc_lock); + usb_gadget_disconnect(gadget); + usb_gadget_disable_async_callbacks(udc); + if (gadget->irq) + synchronize_irq(gadget->irq); + udc->driver->unbind(gadget); + usb_gadget_udc_stop(udc); + + driver->is_bound = false; + udc->driver = NULL; + mutex_unlock(&udc_lock); +} + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver, + struct module *owner, const char *mod_name) +{ + int ret; if (!driver || !driver->bind || !driver->setup) return -EINVAL; + driver->driver.bus = &gadget_bus_type; + driver->driver.owner = owner; + driver->driver.mod_name = mod_name; + ret = driver_register(&driver->driver); + if (ret) { + pr_warn("%s: driver registration failed: %d\n", + driver->function, ret); + return ret; + } + mutex_lock(&udc_lock); - if (driver->udc_name) { - list_for_each_entry(iter, &udc_list, list) { - ret = strcmp(driver->udc_name, dev_name(&iter->dev)); - if (ret) - continue; - udc = iter; - break; - } - if (ret) - ret = -ENODEV; - else if (udc->driver) + if (!driver->is_bound) { + if (driver->match_existing_only) { + pr_warn("%s: couldn't find an available UDC or it's busy\n", + driver->function); ret = -EBUSY; - else - goto found; - } else { - list_for_each_entry(iter, &udc_list, list) { - /* For now we take the first one */ - if (iter->driver) - continue; - udc = iter; - goto found; + } else { + pr_info("%s: couldn't find an available UDC\n", + driver->function); + ret = 0; } } - - if (!driver->match_existing_only) { - list_add_tail(&driver->pending, &gadget_driver_pending_list); - pr_info("couldn't find an available UDC - added [%s] to list of pending drivers\n", - driver->function); - ret = 0; - } - mutex_unlock(&udc_lock); + if (ret) - pr_warn("couldn't find an available UDC or it's busy: %d\n", ret); - return ret; -found: - ret = udc_bind_to_driver(udc, driver); - mutex_unlock(&udc_lock); + driver_unregister(&driver->driver); return ret; } -EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); +EXPORT_SYMBOL_GPL(usb_gadget_register_driver_owner); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { - struct usb_udc *udc = NULL; - int ret = -ENODEV; - if (!driver || !driver->unbind) return -EINVAL; - mutex_lock(&udc_lock); - 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); - ret = 0; - } - mutex_unlock(&udc_lock); - return ret; + driver_unregister(&driver->driver); + return 0; } EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); @@ -1757,8 +1740,17 @@ static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } +static struct bus_type gadget_bus_type = { + .name = "gadget", + .probe = gadget_bind_driver, + .remove = gadget_unbind_driver, + .match = gadget_match_driver, +}; + static int __init usb_udc_init(void) { + int rc; + udc_class = class_create(THIS_MODULE, "udc"); if (IS_ERR(udc_class)) { pr_err("failed to create udc class --> %ld\n", @@ -1767,12 +1759,17 @@ static int __init usb_udc_init(void) } udc_class->dev_uevent = usb_udc_uevent; - return 0; + + rc = bus_register(&gadget_bus_type); + if (rc) + class_destroy(udc_class); + return rc; } subsys_initcall(usb_udc_init); static void __exit usb_udc_exit(void) { + bus_unregister(&gadget_bus_type); class_destroy(udc_class); } module_exit(usb_udc_exit); |