From 61e2ab6a478f6550534f161f7bc91de48ed0b029 Mon Sep 17 00:00:00 2001 From: Philippe De Swert Date: Fri, 12 Jun 2015 14:28:11 +0300 Subject: [PATCH] [usb:android.c] Add functionfs support to usb gadget. Fixes: NEMO#811 Add functionfs support to the android gadget so that mtp works in SailfishOS. Signed-off-by: Philippe De Swert --- drivers/usb/gadget/android.c | 206 +++++++++++++++++++++++++++++++++-- drivers/usb/gadget/f_fs.c | 1 + 2 files changed, 198 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 4c735f5031d6..5cccc152f6bd 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -93,6 +93,7 @@ #endif #include "f_ncm.c" #include "f_laf.c" +#include "f_fs.c" MODULE_AUTHOR("Mike Lockwood"); MODULE_DESCRIPTION("Android Composite USB Driver"); @@ -206,6 +207,8 @@ struct android_dev { struct pm_qos_request pm_qos_req_dma; struct work_struct work; + char ffs_aliases[256]; + /* A list of struct android_configuration */ struct list_head configs; int configs_num; @@ -226,6 +229,7 @@ struct android_configuration { struct dload_struct __iomem *diag_dload; static struct class *android_class; +static struct android_dev *_android_dev; static struct list_head android_dev_list; static int android_dev_count; static int android_bind_config(struct usb_configuration *c); @@ -2088,6 +2092,163 @@ static struct android_usb_function uasp_function = { .bind_config = uasp_function_bind_config, }; +/* functionfs */ +struct functionfs_config { + bool opened; + bool enabled; + struct ffs_data *data; +}; + +static int functionfs_check_dev_callback(const char *dev_name) +{ + return 0; +} + +static int ffs_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + if(!_android_dev) + _android_dev = cdev_to_android_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); + _android_dev = NULL; +} + +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 struct android_usb_function *supported_functions[] = { &mbim_function, &ecm_qc_function, @@ -2118,6 +2279,7 @@ static struct android_usb_function *supported_functions[] = { &audio_source_function, #endif &uasp_function, + &ffs_function, NULL }; @@ -2394,9 +2556,12 @@ functions_store(struct device *pdev, struct device_attribute *attr, struct android_configuration *conf; char *conf_str; struct android_usb_function_holder *f_holder; - char *name; + char *name = NULL; + char aliases[256], *a; char buf[256], *b; int err; + int is_ffs; + int ffs_enabled = 0; mutex_lock(&dev->mutex); @@ -2438,14 +2603,37 @@ functions_store(struct device *pdev, struct device_attribute *attr, while (conf_str) { name = strsep(&conf_str, ","); - if (name) { - err = android_enable_function(dev, conf, name); - if (err) - pr_err("android_usb: Cannot enable %s", - 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, conf, "ffs"); + if (err) + pr_err("android_usb: Cannot enable ffs (%d)", err); + else + ffs_enabled = 1; + continue; + } + + err = android_enable_function(dev, conf, name); + if (err) + pr_err("android_usb: Cannot enable %s (%d)", name, err); + } + } /* Free uneeded configurations if exists */ while (curr_conf->next != &dev->configs) { diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index f52cb1ae45d9..8d68c2883c8f 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -37,6 +37,7 @@ # define ffs_dump_mem(prefix, ptr, len) \ print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len) #else +#undef pr_vdebug # define pr_vdebug(...) do { } while (0) # define ffs_dump_mem(prefix, ptr, len) do { } while (0) #endif /* VERBOSE_DEBUG */