Add support for using FunctionFS in configfs-based USB gadgets.

Signed-off-by: Andrzej Pietrasiewicz <andrze...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
Acked-by: Michal Nazarewicz <min...@mina86.com>
---
 Documentation/ABI/testing/configfs-usb-gadget-ffs |    9 ++
 drivers/usb/gadget/Kconfig                        |   12 ++
 drivers/usb/gadget/f_fs.c                         |  150 ++++++++++++++++++++-
 drivers/usb/gadget/u_fs.h                         |    5 +
 4 files changed, 175 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-ffs

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-ffs 
b/Documentation/ABI/testing/configfs-usb-gadget-ffs
new file mode 100644
index 0000000..14343e2
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-ffs
@@ -0,0 +1,9 @@
+What:          /config/usb-gadget/gadget/functions/ffs.name
+Date:          Nov 2013
+KenelVersion:  3.13
+Description:   The purpose of this directory is to create and remove it.
+
+               A corresponding USB function instance is created/removed.
+               There are no attributes here.
+
+               All parameters are set through FunctionFS.
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 2479644..8da2b1d 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -689,6 +689,18 @@ config USB_CONFIGFS_MASS_STORAGE
          device (in much the same way as the "loop" device driver),
          specified as a module parameter or sysfs option.
 
+config USB_CONFIGFS_F_FS
+       boolean "Function filesystem (FunctionFS)"
+       depends on USB_CONFIGFS
+       select USB_F_FS
+       help
+         The Function Filesystem (FunctionFS) lets one create USB
+         composite functions in user space in the same way GadgetFS
+         lets one create USB gadgets in user space.  This allows creation
+         of composite gadgets such that some of the functions are
+         implemented in kernel space (for instance Ethernet, serial or
+         mass storage) and other are implemented in user space.
+
 config USB_ZERO
        tristate "Gadget Zero (DEVELOPMENT)"
        select USB_LIBCOMPOSITE
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index b24d754..f5c9b5c 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -29,6 +29,7 @@
 #include <linux/usb/functionfs.h>
 
 #include "u_fs.h"
+#include "configfs.h"
 
 #define FUNCTIONFS_MAGIC       0xa647361 /* Chosen by a honest dice roll ;) */
 
@@ -161,6 +162,7 @@ static struct ffs_dev *ffs_alloc_dev(void);
 static void ffs_free_dev(struct ffs_dev *dev);
 
 static struct mutex *ffs_dev_lock;
+static DEFINE_MUTEX(_ffs_dev_lock);
 
 static void *(*ffs_acquire_dev_cb)(const char *name);
 static void (*ffs_release_dev_cb)(struct ffs_data *ffs_data);
@@ -2362,6 +2364,117 @@ void ffs_set_closed_cb(void (*cb)(struct ffs_data 
*ffs_data))
 EXPORT_SYMBOL(ffs_set_closed_cb);
 
 
+/* Configfs support *********************************************************/
+
+static int ffs_ready_callback(struct ffs_data *ffs)
+{
+       struct ffs_dev *ffs_dev;
+       int ret = 0;
+
+       ENTER();
+       mutex_lock(ffs_dev_lock);
+
+       ffs_dev = ffs->private_data;
+       if (!ffs_dev) {
+               ret = -EINVAL;
+               goto done;
+       }
+
+       if (WARN_ON(ffs_dev->desc_ready)) {
+               ret = -EBUSY;
+               goto done;
+       }
+       ffs_dev->desc_ready = true;
+       ffs_dev->ffs_data = ffs;
+
+done:
+       mutex_unlock(ffs_dev_lock);
+       return ret;
+}
+
+static void ffs_closed_callback(struct ffs_data *ffs)
+{
+       struct ffs_dev *ffs_dev;
+
+       ENTER();
+       mutex_lock(ffs_dev_lock);
+
+       ffs_dev = ffs->private_data;
+       if (!ffs_dev)
+               goto done;
+
+       ffs_dev->desc_ready = false;
+       if (!ffs_dev->opts || !ffs_dev->opts->func_inst.group.cg_item.ci_parent)
+               goto done;
+
+       unregister_gadget_item(ffs_dev->opts->
+                              func_inst.group.cg_item.ci_parent->ci_parent);
+
+done:
+       mutex_unlock(ffs_dev_lock);
+}
+
+static void *ffs_acquire_dev_callback(const char *dev_name)
+{
+       struct ffs_dev *ffs_dev;
+
+       ENTER();
+       mutex_lock(ffs_dev_lock);
+
+       ffs_dev = ffs_find_dev(dev_name);
+       if (!ffs_dev) {
+               ffs_dev = ERR_PTR(-ENODEV);
+               goto done;
+       }
+
+       if (ffs_dev->mounted) {
+               ffs_dev = ERR_PTR(-EBUSY);
+               goto done;
+       }
+       ffs_dev->mounted = true;
+
+done:
+       mutex_unlock(ffs_dev_lock);
+       return ffs_dev;
+}
+
+static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_fs_opts,
+                           func_inst.group);
+}
+
+static void ffs_release_dev_callback(struct ffs_data *ffs_data)
+{
+       struct ffs_dev *ffs_dev;
+
+       ENTER();
+       mutex_lock(ffs_dev_lock);
+
+       ffs_dev = ffs_data->private_data;
+       if (ffs_dev)
+               ffs_dev->mounted = false;
+
+       mutex_unlock(ffs_dev_lock);
+}
+
+static void ffs_attr_release(struct config_item *item)
+{
+       struct f_fs_opts *opts = to_ffs_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations ffs_item_ops = {
+       .release        = ffs_attr_release,
+};
+
+static struct config_item_type ffs_func_type = {
+       .ct_item_ops    = &ffs_item_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+
 /* Function registration interface ******************************************/
 
 static void do_cleanup(struct kref *ref)
@@ -2384,6 +2497,31 @@ static void ffs_free_inst(struct usb_function_instance 
*f)
        mutex_unlock(&auto_init_lock);
 }
 
+#define MAX_INST_NAME_LEN      40
+
+static int ffs_set_inst_name(struct usb_function_instance *fi, const char 
*name)
+{
+       struct f_fs_opts *opts;
+       char *ptr;
+       int name_len;
+
+       name_len = strlen(name) + 1;
+       if (name_len > MAX_INST_NAME_LEN)
+               return -ENAMETOOLONG;
+
+       ptr = kstrndup(name, name_len, GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       opts = to_f_fs_opts(fi);
+       if (opts->dev->name_allocated)
+               kfree(opts->dev->name);
+       opts->dev->name = ptr;
+       opts->dev->name_allocated = true;
+
+       return 0;
+}
+
 static struct usb_function_instance *ffs_alloc_inst(void)
 {
        struct f_fs_opts *opts;
@@ -2394,6 +2532,7 @@ static struct usb_function_instance *ffs_alloc_inst(void)
        if (!opts)
                return ERR_PTR(-ENOMEM);
 
+       opts->func_inst.set_inst_name = ffs_set_inst_name;
        opts->func_inst.free_func_inst = ffs_free_inst;
        dev = ffs_alloc_dev();
        if (IS_ERR(dev)) {
@@ -2401,10 +2540,16 @@ static struct usb_function_instance 
*ffs_alloc_inst(void)
                goto error;
        }
        opts->dev = dev;
+       dev->opts = opts;
 
        mutex_lock(&auto_init_lock);
        if (auto_init_active && do_auto_init) {
                kref_init(&auto_init_ref);
+               ffs_set_acquire_dev_cb(ffs_acquire_dev_callback);
+               ffs_set_release_dev_cb(ffs_release_dev_callback);
+               ffs_set_ready_cb(ffs_ready_callback);
+               ffs_set_closed_cb(ffs_closed_callback);
+               ffs_dev_lock = &_ffs_dev_lock;
                ret = functionfs_init(NULL);
                if (!ret)
                        do_init_kref = do_auto_init = false;
@@ -2418,8 +2563,11 @@ static struct usb_function_instance *ffs_alloc_inst(void)
        }
        mutex_unlock(&auto_init_lock);
 
-       if (!ret)
+       if (!ret) {
+               config_group_init_type_name(&opts->func_inst.group, "",
+                                           &ffs_func_type);
                return &opts->func_inst;
+       }
 error:
        ffs_free_dev(opts->dev);
        kfree(opts);
diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h
index e5eca6c..8d1d4db 100644
--- a/drivers/usb/gadget/u_fs.h
+++ b/drivers/usb/gadget/u_fs.h
@@ -19,6 +19,8 @@
 #include <linux/usb/composite.h>
 #include <linux/list.h>
 
+#define VERBOSE_DEBUG
+
 #ifdef VERBOSE_DEBUG
 #ifndef pr_vdebug
 #  define pr_vdebug pr_debug
@@ -34,12 +36,15 @@
 
 #define ENTER()    pr_vdebug("%s()\n", __func__)
 
+struct f_fs_opts;
 
 struct ffs_dev {
        const char *name;
+       bool name_allocated;
        bool mounted;
        bool desc_ready;
        struct ffs_data *ffs_data;
+       struct f_fs_opts *opts;
        struct list_head entry;
 };
 
-- 
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to