summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-ffs9
-rw-r--r--drivers/usb/gadget/Kconfig12
-rw-r--r--drivers/usb/gadget/f_fs.c80
-rw-r--r--drivers/usb/gadget/u_fs.h3
4 files changed, 103 insertions, 1 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-ffs b/Documentation/ABI/testing/configfs-usb-gadget-ffs
new file mode 100644
index 000000000000..14343e237e83
--- /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 97eb540ddef2..0ae2e6559397 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -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
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index 9c8c74c25f1e..12a64e1c31ef 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 @@ 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();
}
diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h
index 09313750f913..bc2d3718219b 100644
--- a/drivers/usb/gadget/u_fs.h
+++ b/drivers/usb/gadget/u_fs.h
@@ -35,13 +35,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);