usb: gadget: add some infracture to register/unregister functions
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Sun, 23 Dec 2012 20:10:00 +0000 (21:10 +0100)
committerFelipe Balbi <balbi@ti.com>
Mon, 21 Jan 2013 18:52:40 +0000 (20:52 +0200)
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 two allocation functions.
- one to create an "instance". The instance holds the current configuration
  set. In case there are two usb_configudations with one function there will
  be one instance and two usb_functions
- one to create an "function" from the instance.

The name of the instance is used to automaticaly load the module if it the
instance is not yet available.
The usb_function callbacks are slightly modified and extended:
- usb_get_function()
  creates a struct usb_function inclunding all pointers (bind,
  unbind,…). It uses the "instance" to map its configuration. So we can
  have _two_ struct usb_function, one for each usb_configuration.
- ->unbind()
  Since the struct usb_function was not allocated in ->bind() it should
  not kfree()d here. This function should only reverse what happens in
  ->bind() that is request cleanup and the cleanup of allocated
  descriptors.
- ->free_func()
  a simple kfree() of the struct usb_function

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/gadget/Makefile
drivers/usb/gadget/composite.c
include/linux/usb/composite.h

index 8b4acfd92aa31fa4b1d392f0b9f4051b20c57ac9..fef41f5d6e8d67ac84c914fa2492d134903fde59 100644 (file)
@@ -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
index 9db000013f5d001ab877fa159acd043dceef1e51..4aa0e4652228a1de5b91137170b02a4415022e08 100644 (file)
@@ -683,6 +683,31 @@ done:
        return result;
 }
 
+int usb_add_config_only(struct usb_composite_dev *cdev,
+               struct usb_configuration *config)
+{
+       struct usb_configuration *c;
+
+       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
@@ -703,30 +728,18 @@ int usb_add_config(struct usb_composite_dev *cdev,
                int (*bind)(struct usb_configuration *))
 {
        int                             status = -EINVAL;
-       struct usb_configuration        *c;
+
+       if (!bind)
+               goto done;
 
        DBG(cdev, "adding config #%u '%s'/%p\n",
                        config->bConfigurationValue,
                        config->label, config);
 
-       if (!config->bConfigurationValue || !bind)
+       status = usb_add_config_only(cdev, config);
+       if (status)
                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 = bind(config);
        if (status < 0) {
                while (!list_empty(&config->functions)) {
index dc512c9432d7609a8e38ed10922009b0f72291a7..3834e3330b23abfa913f1bd30a41f27b6ae4bc4e 100644 (file)
@@ -77,6 +77,8 @@ 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: free the struct usb_function.
+ * @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 +118,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 +139,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 *,
@@ -432,6 +437,53 @@ 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_instance *(*alloc_inst)(void);
+       struct usb_function *(*alloc_func)(struct usb_function_instance *inst);
+};
+
+struct usb_function_instance {
+       struct usb_function_driver *fd;
+       void (*free_func_inst)(struct usb_function_instance *inst);
+};
+
+void usb_function_unregister(struct usb_function_driver *f);
+int usb_function_register(struct usb_function_driver *newf);
+void usb_put_function_instance(struct usb_function_instance *fi);
+void usb_put_function(struct usb_function *f);
+struct usb_function_instance *usb_get_function_instance(const char *name);
+struct usb_function *usb_get_function(struct usb_function_instance *fi);
+
+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, _inst_alloc, _func_alloc)          \
+       static struct usb_function_driver _name ## usb_func = {         \
+               .name = __stringify(_name),                             \
+               .mod  = THIS_MODULE,                                    \
+               .alloc_inst = _inst_alloc,                              \
+               .alloc_func = _func_alloc,                              \
+       };                                                              \
+       MODULE_ALIAS("usbfunc:"__stringify(_name));
+
+#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc)     \
+       DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_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)