usb: gadget: FunctionFS: add configfs support
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Tue, 3 Dec 2013 14:15:36 +0000 (15:15 +0100)
committerFelipe Balbi <balbi@ti.com>
Thu, 12 Dec 2013 19:43:40 +0000 (13:43 -0600)
Add support for using FunctionFS in configfs-based USB gadgets.

[ balbi@ti.com : removed redefinition of VERBOSE_DEBUG and few
trailing whitespaces ]

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Documentation/ABI/testing/configfs-usb-gadget-ffs [new file with mode: 0644]
drivers/usb/gadget/Kconfig
drivers/usb/gadget/f_fs.c
drivers/usb/gadget/u_fs.h

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-ffs b/Documentation/ABI/testing/configfs-usb-gadget-ffs
new file mode 100644 (file)
index 0000000..14343e2
--- /dev/null
@@ -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.
index 97eb540ddef22a2a37e4a3d0dc254f9fca7340bc..0ae2e655939728cc242ffd71bbe883daa9a880ad 100644 (file)
@@ -701,6 +701,18 @@ config USB_CONFIGFS_F_LB_SS
          test software, like the "usbtest" driver, to put your hardware
          and its driver through a basic set of functional tests.
 
+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
index 9c8c74c25f1e1bc5aa2309c679d6a787aadf7e57..12a64e1c31ef0d4f7b791cbbc6e92f02a9a01ff7 100644 (file)
@@ -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 @@ DEFINE_MUTEX(ffs_lock);
 EXPORT_SYMBOL(ffs_lock);
 
 static struct ffs_dev *ffs_find_dev(const char *name);
+static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
 static void *ffs_acquire_dev(const char *dev_name);
 static void ffs_release_dev(struct ffs_data *ffs_data);
 static int ffs_ready(struct ffs_data *ffs);
@@ -2261,7 +2263,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name)
                if (strcmp(dev->name, name) == 0)
                        return dev;
        }
-       
+
        return NULL;
 }
 
@@ -2295,6 +2297,31 @@ static struct ffs_dev *ffs_find_dev(const char *name)
        return _ffs_find_dev(name);
 }
 
+/* Configfs support *********************************************************/
+
+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_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 ffs_free_inst(struct usb_function_instance *f)
@@ -2308,6 +2335,44 @@ static void ffs_free_inst(struct usb_function_instance *f)
        kfree(opts);
 }
 
+#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;
+       const char *tmp;
+       int name_len, ret;
+
+       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);
+       tmp = NULL;
+
+       ffs_dev_lock();
+
+       tmp = opts->dev->name_allocated ? opts->dev->name : NULL;
+       ret = _ffs_name_dev(opts->dev, ptr);
+       if (ret) {
+               kfree(ptr);
+               ffs_dev_unlock();
+               return ret;
+       }
+       opts->dev->name_allocated = true;
+
+       ffs_dev_unlock();
+
+       kfree(tmp);
+
+       return 0;
+}
+
 static struct usb_function_instance *ffs_alloc_inst(void)
 {
        struct f_fs_opts *opts;
@@ -2317,6 +2382,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;
        ffs_dev_lock();
        dev = ffs_alloc_dev();
@@ -2326,7 +2392,10 @@ static struct usb_function_instance *ffs_alloc_inst(void)
                return ERR_CAST(dev);
        }
        opts->dev = dev;
+       dev->opts = opts;
 
+       config_group_init_type_name(&opts->func_inst.group, "",
+                                   &ffs_func_type);
        return &opts->func_inst;
 }
 
@@ -2484,6 +2553,8 @@ EXPORT_SYMBOL(ffs_single_dev);
 void ffs_free_dev(struct ffs_dev *dev)
 {
        list_del(&dev->entry);
+       if (dev->name_allocated)
+               kfree(dev->name);
        kfree(dev);
        if (list_empty(&ffs_devices))
                functionfs_cleanup();
@@ -2572,6 +2643,13 @@ static void ffs_closed(struct ffs_data *ffs)
 
        if (ffs_obj->ffs_closed_callback)
                ffs_obj->ffs_closed_callback(ffs);
+
+       if (!ffs_obj->opts || ffs_obj->opts->no_configfs
+           || !ffs_obj->opts->func_inst.group.cg_item.ci_parent)
+               goto done;
+
+       unregister_gadget_item(ffs_obj->opts->
+                              func_inst.group.cg_item.ci_parent->ci_parent);
 done:
        ffs_dev_unlock();
 }
index 09313750f913237ad4844f6107f95bf500afc1f7..bc2d3718219b9ec60a39e7cc0c3ce2ca4cff8a02 100644 (file)
 
 #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;
        bool single;
        struct ffs_data *ffs_data;
+       struct f_fs_opts *opts;
        struct list_head entry;
 
        int (*ffs_ready_callback)(struct ffs_data *ffs);