summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>2013-12-03 15:15:32 +0100
committerFelipe Balbi <balbi@ti.com>2013-12-12 13:43:39 -0600
commit4b187fceec3c731815ff8e6a7317fd5ba50d1d5d (patch)
tree583fa76e4a4eab4513c38c3d05f85bc03de1f3c3
parente72c39c0692d17da4c7f08e20d6c57f7409415f7 (diff)
usb: gadget: FunctionFS: add devices management code
This will be required in order to use the new function interface (usb_get_function_instance/usb_put_function_instance) Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com> Signed-off-by: Kyunmgin Park <kyungmin.park@samsung.com> Acked-by: Michal Nazarewicz <mina86@mina86.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r--drivers/usb/gadget/f_fs.c238
-rw-r--r--drivers/usb/gadget/g_ffs.c175
-rw-r--r--drivers/usb/gadget/u_fs.h24
-rw-r--r--include/linux/usb/functionfs.h14
4 files changed, 314 insertions, 137 deletions
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index c0b4cf808638..d17930d9a843 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -90,7 +90,7 @@ enum ffs_state {
/*
* We've got descriptors and strings. We are or have called
- * functionfs_ready_callback(). functionfs_bind() may have
+ * ffs_ready(). functionfs_bind() may have
* been called but we don't know.
*
* This is the only state in which operations on epfiles may
@@ -103,7 +103,7 @@ enum ffs_state {
* we encounter an unrecoverable error. The only
* unrecoverable error is situation when after reading strings
* from user space we fail to initialise epfiles or
- * functionfs_ready_callback() returns with error (<0).
+ * ffs_ready() returns with error (<0).
*
* In this state no open(2), read(2) or write(2) (both on ep0
* as well as epfile) may succeed (at this point epfiles are
@@ -361,6 +361,15 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
const struct file_operations *fops,
struct dentry **dentry_p);
+/* Devices management *******************************************************/
+
+DEFINE_MUTEX(ffs_lock);
+
+static struct ffs_dev *ffs_find_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);
+static void ffs_closed(struct ffs_data *ffs);
/* Misc helper functions ****************************************************/
@@ -486,7 +495,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
ffs->state = FFS_ACTIVE;
mutex_unlock(&ffs->mutex);
- ret = functionfs_ready_callback(ffs);
+ ret = ffs_ready(ffs);
if (unlikely(ret < 0)) {
ffs->state = FFS_CLOSING;
return ret;
@@ -1218,7 +1227,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
return ERR_PTR(-ENOMEM);
}
- ffs_dev = functionfs_acquire_dev_callback(dev_name);
+ ffs_dev = ffs_acquire_dev(dev_name);
if (IS_ERR(ffs_dev)) {
ffs_data_put(ffs);
return ERR_CAST(ffs_dev);
@@ -1228,7 +1237,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
rv = mount_nodev(t, flags, &data, ffs_sb_fill);
if (IS_ERR(rv) && data.ffs_data) {
- functionfs_release_dev_callback(data.ffs_data);
+ ffs_release_dev(data.ffs_data);
ffs_data_put(data.ffs_data);
}
return rv;
@@ -1241,7 +1250,7 @@ ffs_fs_kill_sb(struct super_block *sb)
kill_litter_super(sb);
if (sb->s_fs_info) {
- functionfs_release_dev_callback(sb->s_fs_info);
+ ffs_release_dev(sb->s_fs_info);
ffs_data_put(sb->s_fs_info);
}
}
@@ -1354,7 +1363,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
ENTER();
if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
- functionfs_closed_callback(ffs);
+ ffs_closed(ffs);
BUG_ON(ffs->gadget);
@@ -2466,6 +2475,221 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
}
+/* Devices management *******************************************************/
+
+static LIST_HEAD(ffs_devices);
+
+static struct ffs_dev *_ffs_find_dev(const char *name)
+{
+ struct ffs_dev *dev;
+
+ list_for_each_entry(dev, &ffs_devices, entry) {
+ if (!dev->name || !name)
+ continue;
+ if (strcmp(dev->name, name) == 0)
+ return dev;
+ }
+
+ return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *ffs_get_single_dev(void)
+{
+ struct ffs_dev *dev;
+
+ if (list_is_singular(&ffs_devices)) {
+ dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
+ if (dev->single)
+ return dev;
+ }
+
+ return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *ffs_find_dev(const char *name)
+{
+ struct ffs_dev *dev;
+
+ dev = ffs_get_single_dev();
+ if (dev)
+ return dev;
+
+ return _ffs_find_dev(name);
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+struct ffs_dev *ffs_alloc_dev(void)
+{
+ struct ffs_dev *dev;
+ int ret;
+
+ if (ffs_get_single_dev())
+ return ERR_PTR(-EBUSY);
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (list_empty(&ffs_devices)) {
+ ret = functionfs_init();
+ if (ret) {
+ kfree(dev);
+ return ERR_PTR(ret);
+ }
+ }
+
+ list_add(&dev->entry, &ffs_devices);
+
+ return dev;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ * The caller is responsible for "name" being available whenever f_fs needs it
+ */
+static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+ struct ffs_dev *existing;
+
+ existing = _ffs_find_dev(name);
+ if (existing)
+ return -EBUSY;
+
+ dev->name = name;
+
+ return 0;
+}
+
+/*
+ * The caller is responsible for "name" being available whenever f_fs needs it
+ */
+int ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+ int ret;
+
+ ffs_dev_lock();
+ ret = _ffs_name_dev(dev, name);
+ ffs_dev_unlock();
+
+ return ret;
+}
+
+int ffs_single_dev(struct ffs_dev *dev)
+{
+ int ret;
+
+ ret = 0;
+ ffs_dev_lock();
+
+ if (!list_is_singular(&ffs_devices))
+ ret = -EBUSY;
+ else
+ dev->single = true;
+
+ ffs_dev_unlock();
+ return ret;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+void ffs_free_dev(struct ffs_dev *dev)
+{
+ list_del(&dev->entry);
+ kfree(dev);
+ if (list_empty(&ffs_devices))
+ functionfs_cleanup();
+}
+
+static void *ffs_acquire_dev(const char *dev_name)
+{
+ struct ffs_dev *ffs_dev;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_dev = ffs_find_dev(dev_name);
+ if (!ffs_dev)
+ ffs_dev = ERR_PTR(-ENODEV);
+ else if (ffs_dev->mounted)
+ ffs_dev = ERR_PTR(-EBUSY);
+ else
+ ffs_dev->mounted = true;
+
+ ffs_dev_unlock();
+ return ffs_dev;
+}
+
+static void ffs_release_dev(struct ffs_data *ffs_data)
+{
+ struct ffs_dev *ffs_dev;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_dev = ffs_data->private_data;
+ if (ffs_dev)
+ ffs_dev->mounted = false;
+
+ ffs_dev_unlock();
+}
+
+static int ffs_ready(struct ffs_data *ffs)
+{
+ struct ffs_dev *ffs_obj;
+ int ret = 0;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_obj = ffs->private_data;
+ if (!ffs_obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (WARN_ON(ffs_obj->desc_ready)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ ffs_obj->desc_ready = true;
+ ffs_obj->ffs_data = ffs;
+
+ if (ffs_obj->ffs_ready_callback)
+ ret = ffs_obj->ffs_ready_callback(ffs);
+
+done:
+ ffs_dev_unlock();
+ return ret;
+}
+
+static void ffs_closed(struct ffs_data *ffs)
+{
+ struct ffs_dev *ffs_obj;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_obj = ffs->private_data;
+ if (!ffs_obj)
+ goto done;
+
+ ffs_obj->desc_ready = false;
+
+ if (ffs_obj->ffs_closed_callback)
+ ffs_obj->ffs_closed_callback(ffs);
+done:
+ ffs_dev_unlock();
+}
+
/* Misc helper functions ****************************************************/
static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index 1aaa103e01c6..074df0d56255 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -157,6 +157,8 @@ struct gfs_configuration {
#endif
};
+static int functionfs_ready_callback(struct ffs_data *ffs);
+static void functionfs_closed_callback(struct ffs_data *ffs);
static int gfs_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c);
@@ -170,172 +172,113 @@ static __refdata struct usb_composite_driver gfs_driver = {
.unbind = gfs_unbind,
};
-static DEFINE_MUTEX(gfs_lock);
static unsigned int missing_funcs;
static bool gfs_registered;
static bool gfs_single_func;
-static struct ffs_dev *ffs_tab;
+static struct ffs_dev **ffs_tab;
static int __init gfs_init(void)
{
int i;
+ int ret = 0;
ENTER();
- if (!func_num) {
+ if (func_num < 2) {
gfs_single_func = true;
func_num = 1;
}
- ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL);
+ ffs_tab = kcalloc(func_num, sizeof(*ffs_tab), GFP_KERNEL);
if (!ffs_tab)
return -ENOMEM;
- if (!gfs_single_func)
- for (i = 0; i < func_num; i++)
- ffs_tab[i].name = func_names[i];
+ for (i = 0; i < func_num; i++) {
+ ffs_dev_lock();
+ ffs_tab[i] = ffs_alloc_dev();
+ ffs_dev_unlock();
+ if (IS_ERR(ffs_tab[i])) {
+ ret = PTR_ERR(ffs_tab[i]);
+ --i;
+ goto no_dev;
+ }
+ if (gfs_single_func)
+ ret = ffs_single_dev(ffs_tab[i]);
+ else
+ ret = ffs_name_dev(ffs_tab[i], func_names[i]);
+ if (ret)
+ goto no_dev;
+ ffs_tab[i]->ffs_ready_callback = functionfs_ready_callback;
+ ffs_tab[i]->ffs_closed_callback = functionfs_closed_callback;
+ }
missing_funcs = func_num;
- return functionfs_init();
+ return 0;
+no_dev:
+ ffs_dev_lock();
+ while (i >= 0)
+ ffs_free_dev(ffs_tab[i--]);
+ ffs_dev_unlock();
+ kfree(ffs_tab);
+ return ret;
}
module_init(gfs_init);
static void __exit gfs_exit(void)
{
+ int i;
+
ENTER();
- mutex_lock(&gfs_lock);
+ ffs_dev_lock();
if (gfs_registered)
usb_composite_unregister(&gfs_driver);
gfs_registered = false;
- functionfs_cleanup();
-
- mutex_unlock(&gfs_lock);
+ for (i = 0; i < func_num; i++)
+ ffs_free_dev(ffs_tab[i]);
+ ffs_dev_unlock();
kfree(ffs_tab);
}
module_exit(gfs_exit);
-static struct ffs_dev *gfs_find_dev(const char *dev_name)
-{
- int i;
-
- ENTER();
-
- if (gfs_single_func)
- return &ffs_tab[0];
-
- for (i = 0; i < func_num; i++)
- if (strcmp(ffs_tab[i].name, dev_name) == 0)
- return &ffs_tab[i];
-
- return NULL;
-}
-
+/*
+ * The caller of this function takes ffs_lock
+ */
static int functionfs_ready_callback(struct ffs_data *ffs)
{
- struct ffs_dev *ffs_obj;
- int ret;
-
- ENTER();
- mutex_lock(&gfs_lock);
+ int ret = 0;
- ffs_obj = ffs->private_data;
- if (!ffs_obj) {
- ret = -EINVAL;
- goto done;
- }
-
- if (WARN_ON(ffs_obj->desc_ready)) {
- ret = -EBUSY;
- goto done;
- }
- ffs_obj->desc_ready = true;
- ffs_obj->ffs_data = ffs;
+ if (--missing_funcs)
+ return 0;
- if (--missing_funcs) {
- ret = 0;
- goto done;
- }
+ if (gfs_registered)
+ return -EBUSY;
- if (gfs_registered) {
- ret = -EBUSY;
- goto done;
- }
gfs_registered = true;
ret = usb_composite_probe(&gfs_driver);
if (unlikely(ret < 0))
gfs_registered = false;
-
-done:
- mutex_unlock(&gfs_lock);
+
return ret;
}
+/*
+ * The caller of this function takes ffs_lock
+ */
static void functionfs_closed_callback(struct ffs_data *ffs)
{
- struct ffs_dev *ffs_obj;
-
- ENTER();
- mutex_lock(&gfs_lock);
-
- ffs_obj = ffs->private_data;
- if (!ffs_obj)
- goto done;
-
- ffs_obj->desc_ready = false;
missing_funcs++;
if (gfs_registered)
usb_composite_unregister(&gfs_driver);
gfs_registered = false;
-
-done:
- mutex_unlock(&gfs_lock);
-}
-
-static void *functionfs_acquire_dev_callback(const char *dev_name)
-{
- struct ffs_dev *ffs_dev;
-
- ENTER();
- mutex_lock(&gfs_lock);
-
- ffs_dev = gfs_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(&gfs_lock);
- return ffs_dev;
-}
-
-static void functionfs_release_dev_callback(struct ffs_data *ffs_data)
-{
- struct ffs_dev *ffs_dev;
-
- ENTER();
- mutex_lock(&gfs_lock);
-
- ffs_dev = ffs_data->private_data;
- if (ffs_dev)
- ffs_dev->mounted = false;
-
- mutex_unlock(&gfs_lock);
}
/*
- * It is assumed that gfs_bind is called from a context where gfs_lock is held
+ * It is assumed that gfs_bind is called from a context where ffs_lock is held
*/
static int gfs_bind(struct usb_composite_dev *cdev)
{
@@ -422,10 +365,10 @@ static int gfs_bind(struct usb_composite_dev *cdev)
gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
for (i = func_num; i--; ) {
- ret = functionfs_bind(ffs_tab[i].ffs_data, cdev);
+ ret = functionfs_bind(ffs_tab[i]->ffs_data, cdev);
if (unlikely(ret < 0)) {
while (++i < func_num)
- functionfs_unbind(ffs_tab[i].ffs_data);
+ functionfs_unbind(ffs_tab[i]->ffs_data);
goto error_rndis;
}
}
@@ -448,7 +391,7 @@ static int gfs_bind(struct usb_composite_dev *cdev)
error_unbind:
for (i = 0; i < func_num; i++)
- functionfs_unbind(ffs_tab[i].ffs_data);
+ functionfs_unbind(ffs_tab[i]->ffs_data);
error_rndis:
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
usb_put_function_instance(fi_rndis);
@@ -464,7 +407,7 @@ error:
}
/*
- * It is assumed that gfs_unbind is called from a context where gfs_lock is held
+ * It is assumed that gfs_unbind is called from a context where ffs_lock is held
*/
static int gfs_unbind(struct usb_composite_dev *cdev)
{
@@ -497,15 +440,15 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
* do...?
*/
for (i = func_num; i--; )
- if (ffs_tab[i].ffs_data)
- functionfs_unbind(ffs_tab[i].ffs_data);
+ if (ffs_tab[i]->ffs_data)
+ functionfs_unbind(ffs_tab[i]->ffs_data);
return 0;
}
/*
* It is assumed that gfs_do_config is called from a context where
- * gfs_lock is held
+ * ffs_lock is held
*/
static int gfs_do_config(struct usb_configuration *c)
{
@@ -529,7 +472,7 @@ static int gfs_do_config(struct usb_configuration *c)
}
for (i = 0; i < func_num; i++) {
- ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data);
+ ret = functionfs_bind_config(c->cdev, c, ffs_tab[i]->ffs_data);
if (unlikely(ret < 0))
return ret;
}
diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h
index 5d9229a01560..2d00f9d296a3 100644
--- a/drivers/usb/gadget/u_fs.h
+++ b/drivers/usb/gadget/u_fs.h
@@ -17,12 +17,36 @@
#define U_FFS_H
#include <linux/usb/composite.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
struct ffs_dev {
const char *name;
bool mounted;
bool desc_ready;
+ bool single;
struct ffs_data *ffs_data;
+ struct list_head entry;
+
+ int (*ffs_ready_callback)(struct ffs_data *ffs);
+ void (*ffs_closed_callback)(struct ffs_data *ffs);
};
+extern struct mutex ffs_lock;
+
+static inline void ffs_dev_lock(void)
+{
+ mutex_lock(&ffs_lock);
+}
+
+static inline void ffs_dev_unlock(void)
+{
+ mutex_unlock(&ffs_lock);
+}
+
+struct ffs_dev *ffs_alloc_dev(void);
+int ffs_name_dev(struct ffs_dev *dev, const char *name);
+int ffs_single_dev(struct ffs_dev *dev);
+void ffs_free_dev(struct ffs_dev *dev);
+
#endif /* U_FFS_H */
diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h
index 65d0a88dbc67..9c1e92620dfb 100644
--- a/include/linux/usb/functionfs.h
+++ b/include/linux/usb/functionfs.h
@@ -8,10 +8,6 @@ struct ffs_data;
struct usb_composite_dev;
struct usb_configuration;
-
-static int functionfs_init(void) __attribute__((warn_unused_result));
-static void functionfs_cleanup(void);
-
static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
__attribute__((warn_unused_result, nonnull));
static void functionfs_unbind(struct ffs_data *ffs)
@@ -23,14 +19,4 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev,
__attribute__((warn_unused_result, nonnull));
-static int functionfs_ready_callback(struct ffs_data *ffs)
- __attribute__((warn_unused_result, nonnull));
-static void functionfs_closed_callback(struct ffs_data *ffs)
- __attribute__((nonnull));
-static void *functionfs_acquire_dev_callback(const char *dev_name)
- __attribute__((warn_unused_result, nonnull));
-static void functionfs_release_dev_callback(struct ffs_data *ffs_data)
- __attribute__((nonnull));
-
-
#endif