Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit de53c254 authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Felipe Balbi
Browse files

usb: gadget: add some infracture to register/unregister functions



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: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 78f46f09
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+30 −17
Original line number Diff line number Diff line
@@ -683,6 +683,31 @@ 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;

	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,29 +728,17 @@ 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)
		goto done;

	/* Prevent duplicate configuration identifiers */
	list_for_each_entry(c, &cdev->configs, list) {
		if (c->bConfigurationValue == config->bConfigurationValue) {
			status = -EBUSY;
	status = usb_add_config_only(cdev, config);
	if (status)
		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) {
+52 −0
Original line number Diff line number Diff line
@@ -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)