mirror of https://gitee.com/openkylin/linux.git
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: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
parent
78f46f09a8
commit
de53c25447
|
@ -6,7 +6,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
|
||||||
obj-$(CONFIG_USB_GADGET) += udc-core.o
|
obj-$(CONFIG_USB_GADGET) += udc-core.o
|
||||||
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
|
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
|
||||||
libcomposite-y := usbstring.o config.o epautoconf.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_DUMMY_HCD) += dummy_hcd.o
|
||||||
obj-$(CONFIG_USB_NET2272) += net2272.o
|
obj-$(CONFIG_USB_NET2272) += net2272.o
|
||||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||||
|
|
|
@ -683,6 +683,31 @@ static int set_config(struct usb_composite_dev *cdev,
|
||||||
return result;
|
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.
|
* usb_add_config() - add a configuration to a device.
|
||||||
* @cdev: wraps the USB gadget
|
* @cdev: wraps the USB gadget
|
||||||
|
@ -703,30 +728,18 @@ int usb_add_config(struct usb_composite_dev *cdev,
|
||||||
int (*bind)(struct usb_configuration *))
|
int (*bind)(struct usb_configuration *))
|
||||||
{
|
{
|
||||||
int status = -EINVAL;
|
int status = -EINVAL;
|
||||||
struct usb_configuration *c;
|
|
||||||
|
if (!bind)
|
||||||
|
goto done;
|
||||||
|
|
||||||
DBG(cdev, "adding config #%u '%s'/%p\n",
|
DBG(cdev, "adding config #%u '%s'/%p\n",
|
||||||
config->bConfigurationValue,
|
config->bConfigurationValue,
|
||||||
config->label, config);
|
config->label, config);
|
||||||
|
|
||||||
if (!config->bConfigurationValue || !bind)
|
status = usb_add_config_only(cdev, config);
|
||||||
|
if (status)
|
||||||
goto done;
|
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);
|
status = bind(config);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
while (!list_empty(&config->functions)) {
|
while (!list_empty(&config->functions)) {
|
||||||
|
|
|
@ -77,6 +77,8 @@ struct usb_configuration;
|
||||||
* in interface or class descriptors; endpoints; I/O buffers; and so on.
|
* in interface or class descriptors; endpoints; I/O buffers; and so on.
|
||||||
* @unbind: Reverses @bind; called as a side effect of unregistering the
|
* @unbind: Reverses @bind; called as a side effect of unregistering the
|
||||||
* driver which added this function.
|
* 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
|
* @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
|
||||||
* initialize usb_ep.driver data at this time (when it is used).
|
* initialize usb_ep.driver data at this time (when it is used).
|
||||||
* Note that setting an interface to its current altsetting resets
|
* 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
|
* two or more distinct instances within the same configuration, providing
|
||||||
* several independent logical data links to a USB host.
|
* several independent logical data links to a USB host.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct usb_function {
|
struct usb_function {
|
||||||
const char *name;
|
const char *name;
|
||||||
struct usb_gadget_strings **strings;
|
struct usb_gadget_strings **strings;
|
||||||
|
@ -136,6 +139,8 @@ struct usb_function {
|
||||||
struct usb_function *);
|
struct usb_function *);
|
||||||
void (*unbind)(struct usb_configuration *,
|
void (*unbind)(struct usb_configuration *,
|
||||||
struct usb_function *);
|
struct usb_function *);
|
||||||
|
void (*free_func)(struct usb_function *f);
|
||||||
|
struct module *mod;
|
||||||
|
|
||||||
/* runtime state management */
|
/* runtime state management */
|
||||||
int (*set_alt)(struct usb_function *,
|
int (*set_alt)(struct usb_function *,
|
||||||
|
@ -432,6 +437,53 @@ static inline u16 get_default_bcdDevice(void)
|
||||||
return bcdDevice;
|
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 */
|
/* messaging utils */
|
||||||
#define DBG(d, fmt, args...) \
|
#define DBG(d, fmt, args...) \
|
||||||
dev_dbg(&(d)->gadget->dev , fmt , ## args)
|
dev_dbg(&(d)->gadget->dev , fmt , ## args)
|
||||||
|
|
Loading…
Reference in New Issue