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> --- Documentation/ABI/testing/configfs-usb-gadget-ffs | 9 +++ drivers/usb/gadget/Kconfig | 12 +++ drivers/usb/gadget/f_fs.c | 77 +++++++++++++++++++++ drivers/usb/gadget/u_fs.h | 5 ++ 4 files changed, 103 insertions(+), 0 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 e9eaa91..8a81e98 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 ;) */ @@ -2294,6 +2295,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) @@ -2307,6 +2333,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; @@ -2316,6 +2380,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(); @@ -2325,7 +2390,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; } @@ -2466,6 +2534,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(); @@ -2554,6 +2624,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(); } diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index 9ea4215..70e3527 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -20,6 +20,8 @@ #include <linux/list.h> #include <linux/mutex.h> +#define VERBOSE_DEBUG + #ifdef VERBOSE_DEBUG #ifndef pr_vdebug # define pr_vdebug pr_debug @@ -35,13 +37,16 @@ #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); -- 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