This patch provides an infrastructure to register & unregister a USB function. This allows to turn a function into a module and avoid the '#include "f_.*.c"' magic and we get a clear API / cut between the bare gadget and its functions. The concept is simple: Each function defines the DECLARE_USB_FUNCTION_INIT macro whith an unique name of the function and an allocation function. The name is used for automaticaly loading the module if it is not yet present and request the function from the gadget because we don't include the functions anymore. The allocate function is mostly the "old" bind-callback which was passed to usb_add_config() with a minor change: - a function de-allocate function This is mostly the same thing that is done by the unbind function. It is called from within the function on "free" instead from the unbind path on gadget removal. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- drivers/usb/gadget/Makefile | 2 +- drivers/usb/gadget/composite.c | 53 +++++++++++++--------- drivers/usb/gadget/functions.c | 97 ++++++++++++++++++++++++++++++++++++++++ include/linux/usb/composite.h | 44 ++++++++++++++++++ 4 files changed, 175 insertions(+), 21 deletions(-) create mode 100644 drivers/usb/gadget/functions.c diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 8b4acfd..fef41f5 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -6,7 +6,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o -libcomposite-y += composite.o +libcomposite-y += composite.o functions.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2280) += net2280.o diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 4eb07c7..c46ae24 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -664,6 +664,35 @@ static int set_config(struct usb_composite_dev *cdev, return result; } +int usb_add_config_only(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + struct usb_configuration *c; + + DBG(cdev, "adding config #%u '%s'/%p\n", + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue) + return -EINVAL; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) + return -EBUSY; + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + memset(config->interface, 0, sizeof(config->interface)); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_add_config_only); + /** * usb_add_config() - add a configuration to a device. * @cdev: wraps the USB gadget @@ -684,29 +713,13 @@ int usb_add_config(struct usb_composite_dev *cdev, int (*bind)(struct usb_configuration *)) { int status = -EINVAL; - struct usb_configuration *c; - - DBG(cdev, "adding config #%u '%s'/%p\n", - config->bConfigurationValue, - config->label, config); - if (!config->bConfigurationValue || !bind) + if (!bind) goto done; - /* Prevent duplicate configuration identifiers */ - list_for_each_entry(c, &cdev->configs, list) { - if (c->bConfigurationValue == config->bConfigurationValue) { - status = -EBUSY; - goto done; - } - } - - config->cdev = cdev; - list_add_tail(&config->list, &cdev->configs); - - INIT_LIST_HEAD(&config->functions); - config->next_interface_id = 0; - memset(config->interface, 0, sizeof(config->interface)); + status = usb_add_config_only(cdev, config); + if (status) + goto done; status = bind(config); if (status < 0) { diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c new file mode 100644 index 0000000..b7a36c3 --- /dev/null +++ b/drivers/usb/gadget/functions.c @@ -0,0 +1,97 @@ +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/err.h> + +#include <linux/usb/composite.h> + +static LIST_HEAD(func_list); +static DEFINE_MUTEX(func_lock); + +static struct usb_function *try_get_usb_function(const char *name) +{ + struct usb_function_driver *fd; + struct usb_function *f; + + f = ERR_PTR(-ENOENT); + mutex_lock(&func_lock); + list_for_each_entry(fd, &func_list, list) { + + if (strcmp(name, fd->name)) + continue; + + if (!try_module_get(fd->mod)) { + f = ERR_PTR(-EBUSY); + break; + } + f = fd->alloc(); + if (f) { + f->mod = fd->mod; + } else { + module_put(fd->mod); + f = ERR_PTR(-ENOMEM); + } + break; + } + mutex_unlock(&func_lock); + return f; +} + +struct usb_function *usb_get_function(const char *name) +{ + struct usb_function *f; + int ret; + + f = try_get_usb_function(name); + if (!IS_ERR(f)) + return f; + ret = PTR_ERR(f); + if (ret != -ENOENT) + return f; + ret = request_module("usbfunc:%s", name); + if (ret < 0) + return ERR_PTR(ret); + return try_get_usb_function(name); +} +EXPORT_SYMBOL_GPL(usb_get_function); + +void usb_put_function(struct usb_function *fd) +{ + struct module *mod; + + if (!fd) + return; + + mod = fd->mod; + fd->free_func(fd); + module_put(mod); +} +EXPORT_SYMBOL_GPL(usb_put_function); + +int usb_function_register(struct usb_function_driver *newf) +{ + struct usb_function_driver *fd; + int ret; + + ret = -EEXIST; + + mutex_lock(&func_lock); + list_for_each_entry(fd, &func_list, list) { + if (!strcmp(fd->name, newf->name)) + goto out; + } + ret = 0; + list_add_tail(&newf->list, &func_list); +out: + mutex_unlock(&func_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_function_register); + +void usb_function_unregister(struct usb_function_driver *fd) +{ + mutex_lock(&func_lock); + list_del(&fd->list); + mutex_unlock(&func_lock); +} +EXPORT_SYMBOL_GPL(usb_function_unregister); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index b09c37e..1fa9941 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -77,6 +77,10 @@ struct usb_configuration; * in interface or class descriptors; endpoints; I/O buffers; and so on. * @unbind: Reverses @bind; called as a side effect of unregistering the * driver which added this function. + * @free_func: If the function has been allocated by usb_get_function() then it + * should not use @unbind but this callback in order to free the allocated + * ressources. + * @mod: (internal) points to the module that created this structure. * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may * initialize usb_ep.driver data at this time (when it is used). * Note that setting an interface to its current altsetting resets @@ -116,6 +120,7 @@ struct usb_configuration; * two or more distinct instances within the same configuration, providing * several independent logical data links to a USB host. */ + struct usb_function { const char *name; struct usb_gadget_strings **strings; @@ -136,6 +141,8 @@ struct usb_function { struct usb_function *); void (*unbind)(struct usb_configuration *, struct usb_function *); + void (*free_func)(struct usb_function *f); + struct module *mod; /* runtime state management */ int (*set_alt)(struct usb_function *, @@ -431,6 +438,43 @@ static inline u16 get_default_bcdDevice(void) return bcdDevice; } +struct usb_function_driver { + const char *name; + struct module *mod; + struct list_head list; + struct usb_function *(*alloc)(void); +}; + +void usb_function_unregister(struct usb_function_driver *f); +int usb_function_register(struct usb_function_driver *newf); +void usb_put_function(struct usb_function *f); +struct usb_function *usb_get_function(const char *name); +struct usb_configuration *usb_get_config(struct usb_composite_dev *cdev, + int val); +int usb_add_config_only(struct usb_composite_dev *cdev, + struct usb_configuration *config); + +#define DECLARE_USB_FUNCTION(_name, _alloc) \ + static struct usb_function_driver _name ## usb_func = { \ + .name = __stringify(_name), \ + .mod = THIS_MODULE, \ + .alloc = _alloc, \ + }; \ + MODULE_ALIAS("usbfunc:"__stringify(_name)); + +#define DECLARE_USB_FUNCTION_INIT(_name, _alloc) \ + DECLARE_USB_FUNCTION(_name, _alloc) \ + static int __init _name ## mod_init(void) \ + { \ + return usb_function_register(&_name ## usb_func); \ + } \ + static void __exit _name ## mod_exit(void) \ + { \ + usb_function_unregister(&_name ## usb_func); \ + } \ + module_init(_name ## mod_init); \ + module_exit(_name ## mod_exit) + /* messaging utils */ #define DBG(d, fmt, args...) \ dev_dbg(&(d)->gadget->dev , fmt , ## args) -- 1.7.10.4 -- 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