Add support for using FunctionFS in configfs-based USB gadgets. Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> Acked-by: Michal Nazarewicz <mina86@xxxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html