aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
authorBenoit Goby <benoit@android.com>2012-03-29 19:25:23 -0700
committerArve Hjønnevåg <arve@android.com>2013-07-01 13:40:49 -0700
commit823dc6c0d427884c45287210ea8c1c3eb05d4af7 (patch)
treecc671f614390ee6cb82b9740d5e957faadaa9ec0 /drivers/usb/gadget
parent9405700955e4bb6eac2b80135b1b76d078e3c219 (diff)
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 <benoit@android.com>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/android.c192
1 files changed, 188 insertions, 4 deletions
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 7a2af584411..ff60af76822 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);