usb: gadget: android: Add FunctionFS
authorBenoit Goby <benoit@android.com>
Fri, 30 Mar 2012 02:25:23 +0000 (19:25 -0700)
committerArve Hjønnevåg <arve@android.com>
Mon, 1 Jul 2013 20:40:49 +0000 (13:40 -0700)
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 <benoit@android.com>
drivers/usb/gadget/android.c

index 7a2af5844113281774cbab826a6de379c361ec77..ff60af76822804657336176fc280cd35704719bb 100644 (file)
@@ -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);