Re: [PATCH 06/17] usb/gadget: add some infracture to register/unregister functions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <bigeasy@xxxxxxxxxxxxx>
> ---
>  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 majordomo@xxxxxxxxxxxxxxx
> 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: mpn@xxxxxxxxxx>--------------ooO--(_)--Ooo--

Attachment: pgphdcV0oga4T.pgp
Description: PGP signature


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux