usb: gadget: composite: Add usb_remove_config

This allows composite drivers to dynamically change their configuration.
For example, a driver might remove a configuration and register a new
one with a different set of functions.
User should prevent the host from enumerating the device while changing
the configuration:

	usb_gadget_disconnect(cdev->gadget);
	usb_remove_config(cdev, old_config);
	usb_add_config(cdev, new_config, new_conf_bind);
	usb_gadget_connect(cdev->gadget);

Change-Id: Icbfb4ce41685fde9bf63d5d58fca1ad242aa69f9
Signed-off-by: Benoit Goby <benoit@android.com>
This commit is contained in:
Benoit Goby
2011-05-25 23:59:43 -07:00
parent f6e7d49e03
commit 94df1bdfdc
2 changed files with 43 additions and 20 deletions

View File

@@ -667,6 +667,45 @@ done:
return status;
}
static int remove_config(struct usb_composite_dev *cdev,
struct usb_configuration *config)
{
while (!list_empty(&config->functions)) {
struct usb_function *f;
f = list_first_entry(&config->functions,
struct usb_function, list);
list_del(&f->list);
if (f->unbind) {
DBG(cdev, "unbind function '%s'/%p\n", f->name, f);
f->unbind(config, f);
/* may free memory for "f" */
}
}
list_del(&config->list);
if (config->unbind) {
DBG(cdev, "unbind config '%s'/%p\n", config->label, config);
config->unbind(config);
/* may free memory for "c" */
}
return 0;
}
int usb_remove_config(struct usb_composite_dev *cdev,
struct usb_configuration *config)
{
unsigned long flags;
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config == config)
reset_config(cdev);
spin_unlock_irqrestore(&cdev->lock, flags);
return remove_config(cdev, config);
}
/*-------------------------------------------------------------------------*/
/* We support strings in multiple languages ... string descriptor zero
@@ -1192,28 +1231,9 @@ composite_unbind(struct usb_gadget *gadget)
while (!list_empty(&cdev->configs)) {
struct usb_configuration *c;
c = list_first_entry(&cdev->configs,
struct usb_configuration, list);
while (!list_empty(&c->functions)) {
struct usb_function *f;
f = list_first_entry(&c->functions,
struct usb_function, list);
list_del(&f->list);
if (f->unbind) {
DBG(cdev, "unbind function '%s'/%p\n",
f->name, f);
f->unbind(c, f);
/* may free memory for "f" */
}
}
list_del(&c->list);
if (c->unbind) {
DBG(cdev, "unbind config '%s'/%p\n", c->label, c);
c->unbind(c);
/* may free memory for "c" */
}
remove_config(cdev, c);
}
if (composite->unbind)
composite->unbind(cdev);

View File

@@ -249,6 +249,9 @@ int usb_add_config(struct usb_composite_dev *,
struct usb_configuration *,
int (*)(struct usb_configuration *));
int usb_remove_config(struct usb_composite_dev *,
struct usb_configuration *);
/**
* struct usb_composite_driver - groups configurations into a gadget
* @name: For diagnostics, identifies the driver.