On Wed, Nov 07 2012, Sebastian Andrzej Siewior wrote:
> This patch provides an infrastructure to register & unregister an 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 functions defines the DECLARE_USB_FUNCTION 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 some minor changes:

> - a generic configuration struct is attached
>   This struct describes all argumens which can be passed to the
>   function. It includes basically two entries: The name of the argument
>   and its value. With this piece we will have a "generic" query of all
>   arguments which can be set from the outside. This should be also handy
>   for configfs because it can query each function for its options and
>   expose to the user, that needs to do the thinking. This can be used
>   the set the MAC address for one of the network gadgets (which is a
>   string) or the number of current serial ports for the ACM function
>   (which is an interger).

The code looks promising, and definitely a step in right direction, but
I don't think this configuration structure is good solution for
ConfigFS.

Sure, many (most) functions have same static unstructured configuration
arguments which can be expressed in a flat array, but if you look at
mass storage, this is not really a good solution.

At the moment, mass storage is using a static and flat list of arguments
because it uses module parameters, but the “file” argument is very
ugly...  It accepts a list of file names to use for each LUN.

With ConfigFS I believe we can do much better than to use inflexible
list of arguments.  In particular, the way I would see mass storage
function be configured from ConfigFS is:

        $ cd /path/to/mass/storage/function/configfs/directory
        $ ls
        nluns
        $ echo 2 >nluns
        $ ls
        nluns lun0 lun1
        $ echo 0 >lun0/ro
        $ echo 0 >lun0/cdrom
        $ echo 0 >lun0/nofua
        $ echo 0 >lun0/removable
        $ echo /path/to/some/file >lun0/file
        $ echo 1 >lun0/ro
        $ echo 1 >lun0/cdrom
        $ echo 0 >lun0/nofua
        $ echo 1 >lun0/removable

> - a generic function to configure the function
>   This passes the struct and sets the arguments. The "old" gadgets would
>   most likely set all values at once but the "new configfs" interface
>   would prefer to set one value at a time that once the use did
>     "echo value > option"
>   kind of thing.
> - a function de-allocate function
>   This is mostly the same thing that is done by the unbind function.
>
> composite gains usb_add_config_only() which a subset of
> usb_add_config(). The bind/attaching can be deferred to a later point in
> time.
>
> Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
> ---
>  drivers/usb/gadget/Makefile    |    2 +-
>  drivers/usb/gadget/composite.c |   53 +++++++++++++---------
>  drivers/usb/gadget/functions.c |   97 
> ++++++++++++++++++++++++++++++++++++++++
>  include/linux/usb/composite.h  |   68 ++++++++++++++++++++++++++++
>  4 files changed, 199 insertions(+), 21 deletions(-)
>  create mode 100644 drivers/usb/gadget/functions.c
>
> diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
> index 307be5f..fa65050 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..79f9472f3
> --- /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_d_function *f;
> +     struct usb_function *uf;
> +
> +     mutex_lock(&func_lock);
> +     list_for_each_entry(f, &func_list, list) {
> +             if (!strcmp(name, f->name)) {

Alternatively

+               if (strcmp(name, f->name))
+                       continue;

which will make the rest one indention less, but feel free to stick to
your preference here.

> +                     bool ok;
> +                     ok = try_module_get(f->mod);
> +                     uf = ERR_PTR(-EBUSY);
> +                     if (!ok)
> +                             goto out;

This temporary variable looks strange to me.  How about:

+                       if (!try_module_get(f->mod)) {
+                               uf = ERR_PTR(-EBUSY);
+                               goto out;
+                       }

> +                     uf = f->alloc();
> +                     if (uf) {
> +                             uf->mod = f->mod;
> +                     } else {
> +                             module_put(f->mod);
> +                             uf = ERR_PTR(-ENOMEM);
> +                     }
> +                     mutex_unlock(&func_lock);
> +                     return uf;

“goto out;” instead of those two lines perhaps?  This will keep a single
mutex_unlock() location.

> +             }
> +     }
> +     uf = ERR_PTR(-ENOENT);
> +out:

Actually, if you initialise uf with ERR_PTR(-ENOENT) at the beginning,
and apply my change with the “bool ok” variable, you can get rid of the
out label and use “break” instead of “goto”.

> +     mutex_unlock(&func_lock);
> +     return uf;
> +}
> +
> +struct usb_function *usb_get_function(const char *name)
> +{
> +     struct usb_function *uf;
> +     int ret;
> +
> +     uf = try_get_usb_function(name);
> +     if (!IS_ERR(uf))
> +             return uf;
> +     ret = PTR_ERR(uf);
> +     if (ret != -ENOENT)
> +             return uf;
> +     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 *f)
> +{
> +     struct module *mod;
> +
> +     if (!f)
> +             return;
> +     mod = f->mod;
> +     f->free_func(f);
> +     module_put(mod);
> +}
> +EXPORT_SYMBOL_GPL(usb_put_function);
> +
> +int usb_function_register(struct usb_d_function *newf)
> +{
> +     struct usb_d_function *f;
> +     int ret;
> +
> +     ret = -EEXIST;
> +
> +     mutex_lock(&func_lock);
> +     list_for_each_entry(f, &func_list, list) {
> +             if (!strcmp(f->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_d_function *f)
> +{
> +     mutex_lock(&func_lock);
> +     list_del(&f->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..ced2910 100644
> --- a/include/linux/usb/composite.h
> +++ b/include/linux/usb/composite.h
> @@ -116,6 +116,22 @@ struct usb_configuration;
>   * two or more distinct instances within the same configuration, providing
>   * several independent logical data links to a USB host.
>   */
> +
> +enum USBF_OPTION_TYPE {
> +     USBF_OPTION_INVALID,
> +     USBF_OPTION_STRING,
> +     USBF_OPTION_INT,
> +};
> +
> +struct usbf_option {
> +     enum USBF_OPTION_TYPE type;
> +     char *name;
> +     union {
> +             char *o_charp;
> +             int o_int;
> +     } val;
> +};
> +
>  struct usb_function {
>       const char                      *name;
>       struct usb_gadget_strings       **strings;
> @@ -136,6 +152,13 @@ struct usb_function {
>                                       struct usb_function *);
>       void                    (*unbind)(struct usb_configuration *,
>                                       struct usb_function *);
> +     /* */
> +     void                    (*free_func)(struct usb_function *f);
> +     const struct usbf_option        *avail_options;
> +     unsigned                        avail_options_num;

Why not make the avail_options array be terminated with a NULL-named
option or something like that?  I think that's simpler than having to
specify the number of arguments explicitly.

> +     int                     (*configure)(struct usb_function *f,
> +                     struct usbf_option *options, int num);
> +     struct module           *mod;
>  
>       /* runtime state management */
>       int                     (*set_alt)(struct usb_function *,
> @@ -158,6 +181,14 @@ struct usb_function {
>       DECLARE_BITMAP(endpoints, 32);
>  };
>  
> +static inline int _usbf_configure(struct usb_function *f,
> +             struct usbf_option *options, int num)
> +{
> +     return f->configure(f, options, num);
> +}
> +
> +#define usbf_configure(f, o) _usbf_configure(f, o, ARRAY_SIZE(o))
> +
>  int usb_add_function(struct usb_configuration *, struct usb_function *);
>  
>  int usb_function_deactivate(struct usb_function *);
> @@ -431,6 +462,43 @@ static inline u16 get_default_bcdDevice(void)
>       return bcdDevice;
>  }
>  
> +typedef struct usb_function *(*usb_func_alloc)(void);
> +typedef void (*usb_func_free)(struct usb_function *f);
> +
> +struct usb_d_function {
> +     const char *name;
> +     struct module *mod;
> +     struct list_head list;
> +     usb_func_alloc alloc;
> +};
> +
> +void usb_function_unregister(struct usb_d_function *f);
> +int usb_function_register(struct usb_d_function *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_d_function _name ## usb_func = {              \
> +             .name = __stringify(_name),                             \
> +             .mod  = THIS_MODULE,                                    \
> +             .alloc = _alloc,                                        \
> +     };                                                              \
> +     MODULE_ALIAS("usbfunc:"__stringify(_name));                     \
> +     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 [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Michał “mina86” Nazarewicz    (o o)
ooo +----<email/xmpp: [email protected]>--------------ooO--(_)--Ooo--

Attachment: pgpm2CETdbL7k.pgp
Description: PGP signature

Reply via email to