Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+231 −7
Original line number Original line Diff line number Diff line
@@ -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 {
	 * 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,
		   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,
			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,
		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,


	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)


	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)
	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)
}
}




/* 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)
+59 −116
Original line number Original line Diff line number Diff line
@@ -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 = {
	.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();
	if (--missing_funcs)
	mutex_lock(&gfs_lock);
		return 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) {
	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)
	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)


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 @@ 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)
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)
	}
	}


	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;
	}
	}
+24 −0
Original line number Original line Diff line number Diff line
@@ -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 */
+0 −14
Original line number Original line Diff line number Diff line
@@ -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,
	__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