[PATCH 04/10] usb/gadget: add some infracture to register/unregister functions

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

 



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).
- 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 |   92 ++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/composite.h  |   67 +++++++++++++++++++++++++++++
 4 files changed, 193 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 2a6bfe7..3017350 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..971c7e3
--- /dev/null
+++ b/drivers/usb/gadget/functions.c
@@ -0,0 +1,92 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.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)) {
+			bool ok;
+			ok = try_module_get(f->mod);
+			uf = ERR_PTR(-EBUSY);
+			if (!ok)
+				goto out;
+			uf = f->alloc();
+			if (uf)
+				uf->mod = f->mod;
+			else
+				uf = ERR_PTR(-ENOMEM);
+			mutex_unlock(&func_lock);
+			return uf;
+		}
+	}
+	uf = ERR_PTR(-ENOENT);
+out:
+	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;
+
+	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..e84c754 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;
+	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,13 @@ 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 +461,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


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

  Powered by Linux