diff options
Diffstat (limited to 'drivers/usb/gadget/android.c')
-rw-r--r-- | drivers/usb/gadget/android.c | 117 |
1 files changed, 109 insertions, 8 deletions
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index b39ae673762..d2c3393237b 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -79,6 +79,11 @@ struct android_usb_function { int (*init)(struct android_usb_function *, struct usb_composite_dev *); /* Optional: cleanup during gadget unbind */ void (*cleanup)(struct android_usb_function *); + /* Optional: called when the function is added the list of + * enabled functions */ + void (*enable)(struct android_usb_function *); + /* Optional: called when it is removed */ + void (*disable)(struct android_usb_function *); int (*bind_config)(struct android_usb_function *, struct usb_configuration *); @@ -99,6 +104,7 @@ struct android_dev { struct device *dev; bool enabled; + int disable_depth; struct mutex mutex; bool connected; bool sw_connected; @@ -183,20 +189,55 @@ static void android_work(struct work_struct *data) } } +static void android_enable(struct android_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + + if (WARN_ON(!dev->disable_depth)) + return; + + if (--dev->disable_depth == 0) { + usb_add_config(cdev, &android_config_driver, + android_bind_config); + usb_gadget_connect(cdev->gadget); + } +} + +static void android_disable(struct android_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + + if (dev->disable_depth++ == 0) { + usb_gadget_disconnect(cdev->gadget); + /* Cancel pending control requests */ + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + usb_remove_config(cdev, &android_config_driver); + } +} /*-------------------------------------------------------------------------*/ /* Supported functions initialization */ +struct adb_data { + bool opened; + bool enabled; +}; + static int adb_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { + f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + return adb_setup(); } static void adb_function_cleanup(struct android_usb_function *f) { adb_cleanup(); + kfree(f->config); } static int @@ -206,13 +247,69 @@ adb_function_bind_config(struct android_usb_function *f, return adb_bind_config(c); } +static void adb_android_function_enable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = f->config; + + data->enabled = true; + + /* Disable the gadget until adbd is ready */ + if (!data->opened) + android_disable(dev); +} + +static void adb_android_function_disable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = f->config; + + data->enabled = false; + + /* Balance the disable that was called in closed_callback */ + if (!data->opened) + android_enable(dev); +} + static struct android_usb_function adb_function = { .name = "adb", + .enable = adb_android_function_enable, + .disable = adb_android_function_disable, .init = adb_function_init, .cleanup = adb_function_cleanup, .bind_config = adb_function_bind_config, }; +static void adb_ready_callback(void) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = adb_function.config; + + mutex_lock(&dev->mutex); + + data->opened = true; + + if (data->enabled) + android_enable(dev); + + mutex_unlock(&dev->mutex); +} + +static void adb_closed_callback(void) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = adb_function.config; + + mutex_lock(&dev->mutex); + + data->opened = false; + + if (data->enabled) + android_disable(dev); + + mutex_unlock(&dev->mutex); +} + #define MAX_ACM_INSTANCES 4 struct acm_function_config { @@ -858,6 +955,7 @@ static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, { struct android_dev *dev = dev_get_drvdata(pdev); struct usb_composite_dev *cdev = dev->cdev; + struct android_usb_function *f; int enabled = 0; @@ -879,16 +977,18 @@ static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, cdev->desc.bDeviceClass = device_desc.bDeviceClass; cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; - - usb_add_config(cdev, &android_config_driver, - android_bind_config); - usb_gadget_connect(cdev->gadget); + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->enable) + f->enable(f); + } + android_enable(dev); dev->enabled = true; } else if (!enabled && dev->enabled) { - usb_gadget_disconnect(cdev->gadget); - /* Cancel pending control requests */ - usb_ep_dequeue(cdev->gadget->ep0, cdev->req); - usb_remove_config(cdev, &android_config_driver); + android_disable(dev); + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->disable) + f->disable(f); + } dev->enabled = false; } else { pr_err("android_usb: already %s\n", @@ -1184,6 +1284,7 @@ static int __init init(void) if (!dev) return -ENOMEM; + dev->disable_depth = 1; dev->functions = supported_functions; INIT_LIST_HEAD(&dev->enabled_functions); INIT_WORK(&dev->work, android_work); |