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:
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user