From 823dc6c0d427884c45287210ea8c1c3eb05d4af7 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 29 Mar 2012 19:25:23 -0700 Subject: [PATCH] usb: gadget: android: Add FunctionFS Add support for FunctionFS (ffs) to implement usb functions in userspace. The aliases property stores the list of functions that are implemented using functionfs. For example: echo "adb,mtp" > /sys/class/android_usb/android0/f_ffs/aliases Then when the function are enabled: echo "adb,acm" > /sys/class/android_usb/android0/functions Internally, ffs and acm will be used. Change-Id: I44117b183d48a5a99ddbee3ef2cf8998be74598e Signed-off-by: Benoit Goby --- drivers/usb/gadget/android.c | 192 ++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 7a2af5844113..ff60af768228 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -42,6 +42,7 @@ #include "epautoconf.c" #include "composite.c" +#include "f_fs.c" #include "f_mass_storage.c" #include "u_serial.c" #include "f_acm.c" @@ -109,6 +110,7 @@ struct android_dev { bool connected; bool sw_connected; struct work_struct work; + char ffs_aliases[256]; }; static struct class *android_class; @@ -218,6 +220,158 @@ static void android_disable(struct android_dev *dev) /*-------------------------------------------------------------------------*/ /* Supported functions initialization */ +struct functionfs_config { + bool opened; + bool enabled; + struct ffs_data *data; +}; + +static int ffs_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + f->config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + + return functionfs_init(); +} + +static void ffs_function_cleanup(struct android_usb_function *f) +{ + functionfs_cleanup(); + kfree(f->config); +} + +static void ffs_function_enable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = f->config; + + config->enabled = true; + + /* Disable the gadget until the function is ready */ + if (!config->opened) + android_disable(dev); +} + +static void ffs_function_disable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = f->config; + + config->enabled = false; + + /* Balance the disable that was called in closed_callback */ + if (!config->opened) + android_enable(dev); +} + +static int ffs_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct functionfs_config *config = f->config; + return functionfs_bind_config(c->cdev, c, config->data); +} + +static ssize_t +ffs_aliases_show(struct device *pdev, struct device_attribute *attr, char *buf) +{ + struct android_dev *dev = _android_dev; + int ret; + + mutex_lock(&dev->mutex); + ret = sprintf(buf, "%s\n", dev->ffs_aliases); + mutex_unlock(&dev->mutex); + + return ret; +} + +static ssize_t +ffs_aliases_store(struct device *pdev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct android_dev *dev = _android_dev; + char buff[256]; + + mutex_lock(&dev->mutex); + + if (dev->enabled) { + mutex_unlock(&dev->mutex); + return -EBUSY; + } + + strlcpy(buff, buf, sizeof(buff)); + strlcpy(dev->ffs_aliases, strim(buff), sizeof(dev->ffs_aliases)); + + mutex_unlock(&dev->mutex); + + return size; +} + +static DEVICE_ATTR(aliases, S_IRUGO | S_IWUSR, ffs_aliases_show, + ffs_aliases_store); +static struct device_attribute *ffs_function_attributes[] = { + &dev_attr_aliases, + NULL +}; + +static struct android_usb_function ffs_function = { + .name = "ffs", + .init = ffs_function_init, + .enable = ffs_function_enable, + .disable = ffs_function_disable, + .cleanup = ffs_function_cleanup, + .bind_config = ffs_function_bind_config, + .attributes = ffs_function_attributes, +}; + +static int functionfs_ready_callback(struct ffs_data *ffs) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = ffs_function.config; + int ret = 0; + + mutex_lock(&dev->mutex); + + ret = functionfs_bind(ffs, dev->cdev); + if (ret) + goto err; + + config->data = ffs; + config->opened = true; + + if (config->enabled) + android_enable(dev); + +err: + mutex_unlock(&dev->mutex); + return ret; +} + +static void functionfs_closed_callback(struct ffs_data *ffs) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = ffs_function.config; + + mutex_lock(&dev->mutex); + + if (config->enabled) + android_disable(dev); + + config->opened = false; + config->data = NULL; + + functionfs_unbind(ffs); + + mutex_unlock(&dev->mutex); +} + +static int functionfs_check_dev_callback(const char *dev_name) +{ + return 0; +} + + struct adb_data { bool opened; bool enabled; @@ -765,6 +919,7 @@ static struct android_usb_function accessory_function = { static struct android_usb_function *supported_functions[] = { + &ffs_function, &adb_function, &acm_function, &mtp_function, @@ -915,7 +1070,10 @@ functions_store(struct device *pdev, struct device_attribute *attr, struct android_dev *dev = dev_get_drvdata(pdev); char *name; char buf[256], *b; + char aliases[256], *a; int err; + int is_ffs; + int ffs_enabled = 0; mutex_lock(&dev->mutex); @@ -926,16 +1084,42 @@ functions_store(struct device *pdev, struct device_attribute *attr, INIT_LIST_HEAD(&dev->enabled_functions); - strncpy(buf, buff, sizeof(buf)); + strlcpy(buf, buff, sizeof(buf)); b = strim(buf); while (b) { name = strsep(&b, ","); - if (name) { - err = android_enable_function(dev, name); + if (!name) + continue; + + is_ffs = 0; + strlcpy(aliases, dev->ffs_aliases, sizeof(aliases)); + a = aliases; + + while (a) { + char *alias = strsep(&a, ","); + if (alias && !strcmp(name, alias)) { + is_ffs = 1; + break; + } + } + + if (is_ffs) { + if (ffs_enabled) + continue; + err = android_enable_function(dev, "ffs"); if (err) - pr_err("android_usb: Cannot enable '%s'", name); + pr_err("android_usb: Cannot enable ffs (%d)", + err); + else + ffs_enabled = 1; + continue; } + + err = android_enable_function(dev, name); + if (err) + pr_err("android_usb: Cannot enable '%s' (%d)", + name, err); } mutex_unlock(&dev->mutex); -- 2.34.1