Add support for using FunctionFS in configfs-based USB gadgets.
Signed-off-by: Andrzej Pietrasiewicz <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
Documentation/ABI/testing/configfs-usb-gadget-ffs | 9 ++
drivers/usb/gadget/f_fs.c | 154 ++++++++++++++++++++-
drivers/usb/gadget/u_fs.h | 5 +
3 files changed, 161 insertions(+), 7 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..8803bc0
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-ffs
@@ -0,0 +1,9 @@
+What: /config/usb-gadget/gadget/functions/ffs.name
+Date: Oct 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/f_fs.c b/drivers/usb/gadget/f_fs.c
index fbceb47..6a3619a 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 ;) */
@@ -151,6 +152,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);
@@ -2361,6 +2363,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)
@@ -2383,6 +2496,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;
@@ -2393,6 +2531,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)) {
@@ -2400,17 +2539,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_set_release_dev_cb();
- * ffs_set_ready_cb();
- * ffs_set_closed_cb();
- */
- /* fails if callbacks not set */
+ 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) {
mutex_unlock(&auto_init_lock);
@@ -2427,6 +2565,8 @@ static struct usb_function_instance *ffs_alloc_inst(void)
}
mutex_unlock(&auto_init_lock);
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &ffs_func_type);
return &opts->func_inst;
error:
ffs_free_dev(opts->dev);
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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html