Commit 4b187fce authored by Andrzej Pietrasiewicz's avatar Andrzej Pietrasiewicz Committed by Felipe Balbi

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: default avatarAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: default avatarKyunmgin Park <kyungmin.park@samsung.com>
Acked-by: default avatarMichal Nazarewicz <mina86@mina86.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent e72c39c0
...@@ -90,7 +90,7 @@ enum ffs_state { ...@@ -90,7 +90,7 @@ enum ffs_state {
/* /*
* We've got descriptors and strings. We are or have called * 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. * been called but we don't know.
* *
* This is the only state in which operations on epfiles may * This is the only state in which operations on epfiles may
...@@ -103,7 +103,7 @@ enum ffs_state { ...@@ -103,7 +103,7 @@ enum ffs_state {
* we encounter an unrecoverable error. The only * we encounter an unrecoverable error. The only
* unrecoverable error is situation when after reading strings * unrecoverable error is situation when after reading strings
* from user space we fail to initialise epfiles or * 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 * 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 * 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, ...@@ -361,6 +361,15 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
const struct file_operations *fops, const struct file_operations *fops,
struct dentry **dentry_p); 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 ****************************************************/ /* Misc helper functions ****************************************************/
...@@ -486,7 +495,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, ...@@ -486,7 +495,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
ffs->state = FFS_ACTIVE; ffs->state = FFS_ACTIVE;
mutex_unlock(&ffs->mutex); mutex_unlock(&ffs->mutex);
ret = functionfs_ready_callback(ffs); ret = ffs_ready(ffs);
if (unlikely(ret < 0)) { if (unlikely(ret < 0)) {
ffs->state = FFS_CLOSING; ffs->state = FFS_CLOSING;
return ret; return ret;
...@@ -1218,7 +1227,7 @@ ffs_fs_mount(struct file_system_type *t, int flags, ...@@ -1218,7 +1227,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
ffs_dev = functionfs_acquire_dev_callback(dev_name); ffs_dev = ffs_acquire_dev(dev_name);
if (IS_ERR(ffs_dev)) { if (IS_ERR(ffs_dev)) {
ffs_data_put(ffs); ffs_data_put(ffs);
return ERR_CAST(ffs_dev); return ERR_CAST(ffs_dev);
...@@ -1228,7 +1237,7 @@ ffs_fs_mount(struct file_system_type *t, int flags, ...@@ -1228,7 +1237,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
rv = mount_nodev(t, flags, &data, ffs_sb_fill); rv = mount_nodev(t, flags, &data, ffs_sb_fill);
if (IS_ERR(rv) && data.ffs_data) { 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); ffs_data_put(data.ffs_data);
} }
return rv; return rv;
...@@ -1241,7 +1250,7 @@ ffs_fs_kill_sb(struct super_block *sb) ...@@ -1241,7 +1250,7 @@ ffs_fs_kill_sb(struct super_block *sb)
kill_litter_super(sb); kill_litter_super(sb);
if (sb->s_fs_info) { 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); ffs_data_put(sb->s_fs_info);
} }
} }
...@@ -1354,7 +1363,7 @@ static void ffs_data_clear(struct ffs_data *ffs) ...@@ -1354,7 +1363,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
ENTER(); ENTER();
if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags)) if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
functionfs_closed_callback(ffs); ffs_closed(ffs);
BUG_ON(ffs->gadget); BUG_ON(ffs->gadget);
...@@ -2466,6 +2475,221 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf) ...@@ -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 ****************************************************/ /* Misc helper functions ****************************************************/
static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock) static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
......
...@@ -157,6 +157,8 @@ struct gfs_configuration { ...@@ -157,6 +157,8 @@ struct gfs_configuration {
#endif #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_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev); static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c); static int gfs_do_config(struct usb_configuration *c);
...@@ -170,172 +172,113 @@ static __refdata struct usb_composite_driver gfs_driver = { ...@@ -170,172 +172,113 @@ static __refdata struct usb_composite_driver gfs_driver = {
.unbind = gfs_unbind, .unbind = gfs_unbind,
}; };
static DEFINE_MUTEX(gfs_lock);
static unsigned int missing_funcs; static unsigned int missing_funcs;
static bool gfs_registered; static bool gfs_registered;
static bool gfs_single_func; static bool gfs_single_func;
static struct ffs_dev *ffs_tab; static struct ffs_dev **ffs_tab;
static int __init gfs_init(void) static int __init gfs_init(void)
{ {
int i; int i;
int ret = 0;
ENTER(); ENTER();
if (!func_num) { if (func_num < 2) {
gfs_single_func = true; gfs_single_func = true;
func_num = 1; 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) if (!ffs_tab)
return -ENOMEM; return -ENOMEM;
if (!gfs_single_func) for (i = 0; i < func_num; i++) {
for (i = 0; i < func_num; i++) ffs_dev_lock();
ffs_tab[i].name = func_names[i]; 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; 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); module_init(gfs_init);
static void __exit gfs_exit(void) static void __exit gfs_exit(void)
{ {
int i;
ENTER(); ENTER();
mutex_lock(&gfs_lock); ffs_dev_lock();
if (gfs_registered) if (gfs_registered)
usb_composite_unregister(&gfs_driver); usb_composite_unregister(&gfs_driver);
gfs_registered = false; gfs_registered = false;
functionfs_cleanup(); for (i = 0; i < func_num; i++)
ffs_free_dev(ffs_tab[i]);
mutex_unlock(&gfs_lock); ffs_dev_unlock();
kfree(ffs_tab); kfree(ffs_tab);
} }
module_exit(gfs_exit); module_exit(gfs_exit);
static struct ffs_dev *gfs_find_dev(const char *dev_name) /*
{ * The caller of this function takes ffs_lock
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;
}
static int functionfs_ready_callback(struct ffs_data *ffs) static int functionfs_ready_callback(struct ffs_data *ffs)
{ {
struct ffs_dev *ffs_obj; int ret = 0;
int ret;
ENTER();
mutex_lock(&gfs_lock);
ffs_obj = ffs->private_data; if (--missing_funcs)
if (!ffs_obj) { return 0;
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) { if (gfs_registered)
ret = 0; return -EBUSY;
goto done;
}
if (gfs_registered) {
ret = -EBUSY;
goto done;
}
gfs_registered = true; gfs_registered = true;
ret = usb_composite_probe(&gfs_driver); ret = usb_composite_probe(&gfs_driver);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
gfs_registered = false; gfs_registered = false;
done:
mutex_unlock(&gfs_lock);
return ret; return ret;
} }
/*
* The caller of this function takes ffs_lock
*/
static void functionfs_closed_callback(struct ffs_data *ffs) 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++; missing_funcs++;
if (gfs_registered) if (gfs_registered)
usb_composite_unregister(&gfs_driver); usb_composite_unregister(&gfs_driver);
gfs_registered = false; 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) static int gfs_bind(struct usb_composite_dev *cdev)
{ {
...@@ -422,10 +365,10 @@ 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; gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
for (i = func_num; i--; ) { 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)) { if (unlikely(ret < 0)) {
while (++i < func_num) while (++i < func_num)
functionfs_unbind(ffs_tab[i].ffs_data); functionfs_unbind(ffs_tab[i]->ffs_data);
goto error_rndis; goto error_rndis;
} }
} }
...@@ -448,7 +391,7 @@ static int gfs_bind(struct usb_composite_dev *cdev) ...@@ -448,7 +391,7 @@ static int gfs_bind(struct usb_composite_dev *cdev)
error_unbind: error_unbind:
for (i = 0; i < func_num; i++) for (i = 0; i < func_num; i++)
functionfs_unbind(ffs_tab[i].ffs_data); functionfs_unbind(ffs_tab[i]->ffs_data);
error_rndis: error_rndis:
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS #ifdef CONFIG_USB_FUNCTIONFS_RNDIS
usb_put_function_instance(fi_rndis); usb_put_function_instance(fi_rndis);
...@@ -464,7 +407,7 @@ static int gfs_bind(struct usb_composite_dev *cdev) ...@@ -464,7 +407,7 @@ static int gfs_bind(struct usb_composite_dev *cdev)
} }
/* /*
* 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) static int gfs_unbind(struct usb_composite_dev *cdev)
{ {
...@@ -497,15 +440,15 @@ static int gfs_unbind(struct usb_composite_dev *cdev) ...@@ -497,15 +440,15 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
* do...? * do...?
*/ */
for (i = func_num; i--; ) for (i = func_num; i--; )
if (ffs_tab[i].ffs_data) if (ffs_tab[i]->ffs_data)
functionfs_unbind(ffs_tab[i].ffs_data); functionfs_unbind(ffs_tab[i]->ffs_data);
return 0; return 0;
} }
/* /*
* It is assumed that gfs_do_config is called from a context where * 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) static int gfs_do_config(struct usb_configuration *c)
{ {
...@@ -529,7 +472,7 @@ 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++) { 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)) if (unlikely(ret < 0))
return ret; return ret;
} }
......
...@@ -17,12 +17,36 @@ ...@@ -17,12 +17,36 @@
#define U_FFS_H #define U_FFS_H
#include <linux/usb/composite.h> #include <linux/usb/composite.h>
#include <linux/list.h>
#include <linux/mutex.h>
struct ffs_dev { struct ffs_dev {
const char *name; const char *name;
bool mounted; bool mounted;
bool desc_ready; bool desc_ready;
bool single;
struct ffs_data *ffs_data; 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 */ #endif /* U_FFS_H */
...@@ -8,10 +8,6 @@ struct ffs_data; ...@@ -8,10 +8,6 @@ struct ffs_data;
struct usb_composite_dev; struct usb_composite_dev;
struct usb_configuration; 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) static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
__attribute__((warn_unused_result, nonnull)); __attribute__((warn_unused_result, nonnull));
static void functionfs_unbind(struct ffs_data *ffs) static void functionfs_unbind(struct ffs_data *ffs)
...@@ -23,14 +19,4 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev, ...@@ -23,14 +19,4 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev,
__attribute__((warn_unused_result, nonnull)); __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 #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment