This is required in order to integrate configfs support. f_fs needs to be a separately compiled module and so it needs to use the new interface. Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> Acked-by: Michal Nazarewicz <mina86@xxxxxxxxxx> --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_fs.c | 568 +++++++++++++++++++++++++--------------- drivers/usb/gadget/g_ffs.c | 3 +- drivers/usb/gadget/u_fs.h | 221 ++++++++++++++++ include/linux/usb/functionfs.h | 5 +- 6 files changed, 586 insertions(+), 216 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 0d117e2..66444d0 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -539,6 +539,9 @@ config USB_F_RNDIS config USB_F_MASS_STORAGE tristate +config USB_F_FS + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index fd9fe1f..6bb1155 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -61,6 +61,8 @@ usb_f_rndis-y := f_rndis.o rndis.o obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o usb_f_mass_storage-y := f_mass_storage.o storage_common.o obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o +usb_f_fs-y := f_fs.o +obj-$(CONFIG_USB_F_FS) += usb_f_fs.o # # USB gadget drivers diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index bfbb580..469cb86 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -22,6 +22,7 @@ #include <linux/pagemap.h> #include <linux/export.h> #include <linux/hid.h> +#include <linux/module.h> #include <asm/unaligned.h> #include <linux/usb/composite.h> @@ -57,210 +58,6 @@ #define vla_ptr(ptr, groupname, name) \ ((void *) ((char *)ptr + groupname##_##name##__offset)) -/* Debugging ****************************************************************/ - -#ifdef VERBOSE_DEBUG -#ifndef pr_vdebug -# define pr_vdebug pr_debug -#endif /* pr_vdebug */ -# define ffs_dump_mem(prefix, ptr, len) \ - print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len) -#else -#ifndef pr_vdebug -# define pr_vdebug(...) do { } while (0) -#endif /* pr_vdebug */ -# define ffs_dump_mem(prefix, ptr, len) do { } while (0) -#endif /* VERBOSE_DEBUG */ - -#define ENTER() pr_vdebug("%s()\n", __func__) - - -/* The data structure and setup file ****************************************/ - -enum ffs_state { - /* - * Waiting for descriptors and strings. - * - * In this state no open(2), read(2) or write(2) on epfiles - * may succeed (which should not be the problem as there - * should be no such files opened in the first place). - */ - FFS_READ_DESCRIPTORS, - FFS_READ_STRINGS, - - /* - * We've got descriptors and strings. We are or have called - * functionfs_ready_callback(). functionfs_bind() may have - * been called but we don't know. - * - * This is the only state in which operations on epfiles may - * succeed. - */ - FFS_ACTIVE, - - /* - * All endpoints have been closed. This state is also set if - * 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). - * - * 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 - * unlinked and all closed so this is not a problem; ep0 is - * also closed but ep0 file exists and so open(2) on ep0 must - * fail). - */ - FFS_CLOSING -}; - - -enum ffs_setup_state { - /* There is no setup request pending. */ - FFS_NO_SETUP, - /* - * User has read events and there was a setup request event - * there. The next read/write on ep0 will handle the - * request. - */ - FFS_SETUP_PENDING, - /* - * There was event pending but before user space handled it - * some other event was introduced which canceled existing - * setup. If this state is set read/write on ep0 return - * -EIDRM. This state is only set when adding event. - */ - FFS_SETUP_CANCELED -}; - - - -struct ffs_epfile; -struct ffs_function; - -struct ffs_data { - struct usb_gadget *gadget; - - /* - * Protect access read/write operations, only one read/write - * at a time. As a consequence protects ep0req and company. - * While setup request is being processed (queued) this is - * held. - */ - struct mutex mutex; - - /* - * Protect access to endpoint related structures (basically - * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for - * endpoint zero. - */ - spinlock_t eps_lock; - - /* - * XXX REVISIT do we need our own request? Since we are not - * handling setup requests immediately user space may be so - * slow that another setup will be sent to the gadget but this - * time not to us but another function and then there could be - * a race. Is that the case? Or maybe we can use cdev->req - * after all, maybe we just need some spinlock for that? - */ - struct usb_request *ep0req; /* P: mutex */ - struct completion ep0req_completion; /* P: mutex */ - int ep0req_status; /* P: mutex */ - - /* reference counter */ - atomic_t ref; - /* how many files are opened (EP0 and others) */ - atomic_t opened; - - /* EP0 state */ - enum ffs_state state; - - /* - * Possible transitions: - * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock - * happens only in ep0 read which is P: mutex - * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock - * happens only in ep0 i/o which is P: mutex - * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock - * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg - */ - enum ffs_setup_state setup_state; - -#define FFS_SETUP_STATE(ffs) \ - ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ - FFS_SETUP_CANCELED, FFS_NO_SETUP)) - - /* Events & such. */ - struct { - u8 types[4]; - unsigned short count; - /* XXX REVISIT need to update it in some places, or do we? */ - unsigned short can_stall; - struct usb_ctrlrequest setup; - - wait_queue_head_t waitq; - } ev; /* the whole structure, P: ev.waitq.lock */ - - /* Flags */ - unsigned long flags; -#define FFS_FL_CALL_CLOSED_CALLBACK 0 -#define FFS_FL_BOUND 1 - - /* Active function */ - struct ffs_function *func; - - /* - * Device name, write once when file system is mounted. - * Intended for user to read if she wants. - */ - const char *dev_name; - /* Private data for our user (ie. gadget). Managed by user. */ - void *private_data; - - /* filled by __ffs_data_got_descs() */ - /* - * Real descriptors are 16 bytes after raw_descs (so you need - * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the - * first full speed descriptor). raw_descs_length and - * raw_fs_descs_length do not have those 16 bytes added. - */ - const void *raw_descs; - unsigned raw_descs_length; - unsigned raw_fs_descs_length; - unsigned fs_descs_count; - unsigned hs_descs_count; - - unsigned short strings_count; - unsigned short interfaces_count; - unsigned short eps_count; - unsigned short _pad1; - - /* filled by __ffs_data_got_strings() */ - /* ids in stringtabs are set in functionfs_bind() */ - const void *raw_strings; - struct usb_gadget_strings **stringtabs; - - /* - * File system's super block, write once when file system is - * mounted. - */ - struct super_block *sb; - - /* File permissions, written once when fs is mounted */ - struct ffs_file_perms { - umode_t mode; - kuid_t uid; - kgid_t gid; - } file_perms; - - /* - * The endpoint files, filled by ffs_epfiles_create(), - * destroyed by ffs_epfiles_destroy(). - */ - struct ffs_epfile *epfiles; -}; - /* Reference counter handling */ static void ffs_data_get(struct ffs_data *ffs); static void ffs_data_put(struct ffs_data *ffs); @@ -300,15 +97,19 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f) return container_of(f, struct ffs_function, function); } +#ifdef USB_FFS_INCLUDED static void ffs_func_free(struct ffs_function *func); +#endif static void ffs_func_eps_disable(struct ffs_function *func); static int __must_check ffs_func_eps_enable(struct ffs_function *func); static int ffs_func_bind(struct usb_configuration *, struct usb_function *); -static void ffs_func_unbind(struct usb_configuration *, +#ifdef USB_FFS_INCLUDED +static void old_ffs_func_unbind(struct usb_configuration *, struct usb_function *); +#endif static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned); static void ffs_func_disable(struct usb_function *); static int ffs_func_setup(struct usb_function *, @@ -365,7 +166,23 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data, static struct ffs_dev *ffs_alloc_dev(void); static void ffs_free_dev(struct ffs_dev *dev); -static struct ffs_dev *ffs_find_dev(const char *name); + +#ifndef USB_FFS_INCLUDED +static struct mutex *ffs_dev_lock; + +static void *(*ffs_acquire_dev_cb)(const char *name); +static void (*ffs_release_dev_cb)(struct ffs_data *ffs_data); +static int (*ffs_ready_cb)(struct ffs_data *ffs_data); +static void (*ffs_closed_cb)(struct ffs_data *ffs_data); + +static bool auto_init_active = true; +static bool do_auto_init = true; +static bool do_init_kref = true; +static struct kref auto_init_ref; +static DEFINE_MUTEX(auto_init_lock); + +static struct module *ffs_owner; +#endif /* Misc helper functions ****************************************************/ @@ -491,7 +308,11 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, ffs->state = FFS_ACTIVE; mutex_unlock(&ffs->mutex); +#ifdef USB_FFS_INCLUDED ret = functionfs_ready_callback(ffs); +#else + ret = ffs_ready_cb(ffs); +#endif if (unlikely(ret < 0)) { ffs->state = FFS_CLOSING; return ret; @@ -1222,18 +1043,40 @@ ffs_fs_mount(struct file_system_type *t, int flags, return ERR_PTR(-ENOMEM); } +#ifdef USB_FFS_INCLUDED ffs_dev = functionfs_acquire_dev_callback(dev_name); if (IS_ERR(ffs_dev)) { ffs_data_put(ffs); return ERR_CAST(ffs_dev); } +#else + if (ffs_owner && !try_module_get(ffs_owner)) { + ffs_data_put(ffs); + return ERR_PTR(-ENODEV); + } + ffs_dev = ffs_acquire_dev_cb(dev_name); + if (IS_ERR(ffs_dev)) { + if (ffs_owner) + module_put(ffs_owner); + ffs_data_put(ffs); + + return ERR_CAST(ffs_dev); + } +#endif ffs->private_data = ffs_dev; data.ffs_data = ffs; rv = mount_nodev(t, flags, &data, ffs_sb_fill); if (IS_ERR(rv) && data.ffs_data) { +#ifdef USB_FFS_INCLUDED functionfs_release_dev_callback(data.ffs_data); ffs_data_put(data.ffs_data); +#else + ffs_release_dev_cb(data.ffs_data); + ffs_data_put(data.ffs_data); + if (ffs_owner) + module_put(ffs_owner); +#endif } return rv; } @@ -1245,8 +1088,15 @@ ffs_fs_kill_sb(struct super_block *sb) kill_litter_super(sb); if (sb->s_fs_info) { +#ifdef USB_FFS_INCLUDED functionfs_release_dev_callback(sb->s_fs_info); ffs_data_put(sb->s_fs_info); +#else + ffs_release_dev_cb(sb->s_fs_info); + ffs_data_put(sb->s_fs_info); + if (ffs_owner) + module_put(ffs_owner); +#endif } } @@ -1261,12 +1111,22 @@ MODULE_ALIAS_FS("functionfs"); /* Driver's main init/cleanup functions *************************************/ -static int functionfs_init(void) +int functionfs_init(struct module *owner) { int ret; ENTER(); +#ifndef USB_FFS_INCLUDED + if (!ffs_acquire_dev_cb || !ffs_release_dev_cb || + !ffs_ready_cb || !ffs_closed_cb) { + ret = -ENODEV; + pr_err("filesystem callbacks not set (%d)\n", ret); + return ret; + } + ffs_owner = owner; +#endif + ret = register_filesystem(&ffs_fs_type); if (likely(!ret)) pr_info("file system registered\n"); @@ -1275,14 +1135,27 @@ static int functionfs_init(void) return ret; } +#ifndef USB_FFS_INCLUDED +EXPORT_SYMBOL(functionfs_init); +#endif -static void functionfs_cleanup(void) +void functionfs_cleanup(void) { +#ifndef USB_FFS_INCLUDED + ffs_owner = NULL; + do_auto_init = true; + auto_init_active = true; +#endif + ENTER(); pr_info("unloading\n"); + unregister_filesystem(&ffs_fs_type); } +#ifndef USB_FFS_INCLUDED +EXPORT_SYMBOL(functionfs_cleanup); +#endif /* ffs_data and ffs_function construction and destruction code **************/ @@ -1358,7 +1231,11 @@ static void ffs_data_clear(struct ffs_data *ffs) ENTER(); if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags)) +#ifdef USB_FFS_INCLUDED functionfs_closed_callback(ffs); +#else + ffs_closed_cb(ffs); +#endif BUG_ON(ffs->gadget); @@ -1494,6 +1371,8 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) kfree(epfiles); } +#ifdef USB_FFS_INCLUDED + static int functionfs_bind_config(struct usb_composite_dev *cdev, struct usb_configuration *c, struct ffs_data *ffs) @@ -1511,7 +1390,7 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev, func->function.strings = ffs->stringtabs; func->function.bind = ffs_func_bind; - func->function.unbind = ffs_func_unbind; + func->function.unbind = old_ffs_func_unbind; func->function.set_alt = ffs_func_set_alt; func->function.disable = ffs_func_disable; func->function.setup = ffs_func_setup; @@ -1556,10 +1435,11 @@ static void ffs_func_free(struct ffs_function *func) * only one free is required. Descriptors are also allocated * in the same chunk. */ - kfree(func); } +#endif + static void ffs_func_eps_disable(struct ffs_function *func) { struct ffs_ep *ep = func->eps; @@ -2222,8 +2102,65 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; } -static int ffs_func_bind(struct usb_configuration *c, - struct usb_function *f) +static inline void mutex_lock_if_nonnull(struct mutex *mutex) +{ + if (mutex) + mutex_lock(mutex); +} + +#ifndef USB_FFS_INCLUDED +static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, + struct usb_configuration *c) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct f_fs_opts *ffs_opts = + container_of(f->fi, struct f_fs_opts, func_inst); + int ret; + + ENTER(); + + /* + * Legacy gadget triggers binding in functionfs_ready_callback, + * which already uses locking; taking the same lock here would + * cause a deadlock. + * + * Configfs-enabled gadgets however do need ffs_dev_lock. + */ + if (ffs_dev_lock) + mutex_lock(ffs_dev_lock); + ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV; + if (ffs_dev_lock) + mutex_unlock(ffs_dev_lock); + if (ret) + return ERR_PTR(ret); + + func->conf = c; + func->gadget = c->cdev->gadget; + + func->ffs = ffs_opts->dev->ffs_data; + ffs_data_get(func->ffs); + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to ffs_opts->bound access + */ + if (!ffs_opts->refcnt) { + ret = functionfs_bind(func->ffs, c->cdev); + if (ret) + return ERR_PTR(ret); + } + ffs_opts->refcnt++; + func->function.strings = ffs_opts->dev->ffs_data->stringtabs; + + return ffs_opts; +} +#endif + +static int _ffs_func_bind(struct usb_configuration *c, + struct usb_function *f) { struct ffs_function *func = ffs_func_from_usb(f); struct ffs_data *ffs = func->ffs; @@ -2323,10 +2260,25 @@ error: return ret; } +static int ffs_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ +#ifndef USB_FFS_INCLUDED + struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c); + + if (IS_ERR(ffs_opts)) + return PTR_ERR(ffs_opts); +#endif + + return _ffs_func_bind(c, f); +} + /* Other USB function hooks *************************************************/ -static void ffs_func_unbind(struct usb_configuration *c, +#ifdef USB_FFS_INCLUDED + +static void old_ffs_func_unbind(struct usb_configuration *c, struct usb_function *f) { struct ffs_function *func = ffs_func_from_usb(f); @@ -2344,6 +2296,8 @@ static void ffs_func_unbind(struct usb_configuration *c, ffs_func_free(func); } +#endif + static int ffs_func_set_alt(struct usb_function *f, unsigned interface, unsigned alt) { @@ -2494,7 +2448,7 @@ static void ffs_free_dev(struct ffs_dev *dev) kfree(dev); } -static struct ffs_dev *ffs_find_dev(const char *name) +struct ffs_dev *ffs_find_dev(const char *name) { struct ffs_dev *dev; @@ -2504,6 +2458,186 @@ static struct ffs_dev *ffs_find_dev(const char *name) return NULL; } +#ifndef USB_FFS_INCLUDED +EXPORT_SYMBOL(ffs_find_dev); + +bool ffs_set_auto_init(bool flag) +{ + bool tmp; + + mutex_lock(&auto_init_lock); + tmp = auto_init_active; + auto_init_active = flag; + mutex_unlock(&auto_init_lock); + + return tmp; +} +EXPORT_SYMBOL(ffs_set_auto_init); + +void ffs_set_acquire_dev_cb(void *(*cb)(const char *name)) +{ + ffs_acquire_dev_cb = cb; +} +EXPORT_SYMBOL(ffs_set_acquire_dev_cb); + +void ffs_set_release_dev_cb(void (*cb)(struct ffs_data *ffs_data)) +{ + ffs_release_dev_cb = cb; +} +EXPORT_SYMBOL(ffs_set_release_dev_cb); + +void ffs_set_ready_cb(int (*cb)(struct ffs_data *ffs_data)) +{ + ffs_ready_cb = cb; +} +EXPORT_SYMBOL(ffs_set_ready_cb); + +void ffs_set_closed_cb(void (*cb)(struct ffs_data *ffs_data)) +{ + ffs_closed_cb = cb; +} +EXPORT_SYMBOL(ffs_set_closed_cb); +#endif + + +/* Function registration interface ******************************************/ + +#ifndef USB_FFS_INCLUDED + +static void do_cleanup(struct kref *ref) +{ + if (auto_init_active) + functionfs_cleanup(); + + do_init_kref = true; +} + +static void ffs_free_inst(struct usb_function_instance *f) +{ + struct f_fs_opts *opts; + + opts = to_f_fs_opts(f); + ffs_free_dev(opts->dev); + kfree(opts); + mutex_lock(&auto_init_lock); + kref_put(&auto_init_ref, do_cleanup); + mutex_unlock(&auto_init_lock); +} + +static struct usb_function_instance *ffs_alloc_inst(void) +{ + struct f_fs_opts *opts; + struct ffs_dev *dev; + int ret = 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = ffs_free_inst; + dev = ffs_alloc_dev(); + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + goto error; + } + opts->dev = dev; + + mutex_lock(&auto_init_lock); + if (auto_init_active && do_auto_init) { + kref_init(&auto_init_ref); + ret = functionfs_init(NULL); + if (!ret) + do_init_kref = do_auto_init = false; + } else { + if (do_init_kref) { + kref_init(&auto_init_ref); + do_init_kref = false; + } else { + kref_get(&auto_init_ref); + } + } + mutex_unlock(&auto_init_lock); + + if (!ret) + return &opts->func_inst; +error: + ffs_free_dev(opts->dev); + kfree(opts); + return ERR_PTR(ret); +} + +static void ffs_free(struct usb_function *f) +{ + kfree(ffs_func_from_usb(f)); +} + +static void ffs_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + struct f_fs_opts *opts = + container_of(f->fi, struct f_fs_opts, func_inst); + struct ffs_ep *ep = func->eps; + unsigned count = ffs->eps_count; + unsigned long flags; + + ENTER(); + if (ffs->func == func) { + ffs_func_eps_disable(func); + ffs->func = NULL; + } + + if (!--opts->refcnt) + functionfs_unbind(ffs); + + /* cleanup after autoconfig */ + spin_lock_irqsave(&func->ffs->eps_lock, flags); + do { + if (ep->ep && ep->req) + usb_ep_free_request(ep->ep, ep->req); + ep->req = NULL; + ++ep; + } while (--count); + spin_unlock_irqrestore(&func->ffs->eps_lock, flags); + kfree(func->eps); + func->eps = NULL; + /* + * eps, descriptors and interfaces_nums are allocated in the + * same chunk so only one free is required. + */ + func->function.fs_descriptors = NULL; + func->function.hs_descriptors = NULL; + func->interfaces_nums = NULL; + + ffs_event_add(ffs, FUNCTIONFS_UNBIND); +} + +static struct usb_function *ffs_alloc(struct usb_function_instance *fi) +{ + struct ffs_function *func; + + ENTER(); + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (unlikely(!func)) + return ERR_PTR(-ENOMEM); + + func->function.name = "Function FS Gadget"; + + func->function.bind = ffs_func_bind; + func->function.unbind = ffs_func_unbind; + func->function.set_alt = ffs_func_set_alt; + func->function.disable = ffs_func_disable; + func->function.setup = ffs_func_setup; + func->function.suspend = ffs_func_suspend; + func->function.resume = ffs_func_resume; + func->function.free_func = ffs_free; + + return &func->function; +} + +#endif /* Misc helper functions ****************************************************/ @@ -2536,3 +2670,9 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) return data; } + +#ifndef USB_FFS_INCLUDED +DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Nazarewicz"); +#endif diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 48b0940..caf0035 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -54,6 +54,7 @@ static struct usb_function *f_rndis; # endif #endif +#define USB_FFS_INCLUDED #include "f_fs.c" #define DRIVER_NAME "g_ffs" @@ -204,7 +205,7 @@ static int __init gfs_init(void) missing_funcs = func_num; - return functionfs_init(); + return functionfs_init(NULL); no_dev: while (--i >= 0) ffs_free_dev(ffs_tab[i]); diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index 12f6970..478a7ae 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -19,6 +19,22 @@ #include <linux/usb/composite.h> #include <linux/list.h> +#ifdef VERBOSE_DEBUG +#ifndef pr_vdebug +# define pr_vdebug pr_debug +#endif /* pr_vdebug */ +# define ffs_dump_mem(prefix, ptr, len) \ + print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len) +#else +#ifndef pr_vdebug +# define pr_vdebug(...) do { } while (0) +#endif /* pr_vdebug */ +# define ffs_dump_mem(prefix, ptr, len) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define ENTER() pr_vdebug("%s()\n", __func__) + + struct ffs_dev { const char *name; bool mounted; @@ -27,4 +43,209 @@ struct ffs_dev { struct list_head entry; }; +struct ffs_epfile; +struct ffs_function; + +enum ffs_state { + /* + * Waiting for descriptors and strings. + * + * In this state no open(2), read(2) or write(2) on epfiles + * may succeed (which should not be the problem as there + * should be no such files opened in the first place). + */ + FFS_READ_DESCRIPTORS, + FFS_READ_STRINGS, + + /* + * We've got descriptors and strings. We are or have called + * functionfs_ready_callback(). functionfs_bind() may have + * been called but we don't know. + * + * This is the only state in which operations on epfiles may + * succeed. + */ + FFS_ACTIVE, + + /* + * All endpoints have been closed. This state is also set if + * 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). + * + * 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 + * unlinked and all closed so this is not a problem; ep0 is + * also closed but ep0 file exists and so open(2) on ep0 must + * fail). + */ + FFS_CLOSING +}; + +enum ffs_setup_state { + /* There is no setup request pending. */ + FFS_NO_SETUP, + /* + * User has read events and there was a setup request event + * there. The next read/write on ep0 will handle the + * request. + */ + FFS_SETUP_PENDING, + /* + * There was event pending but before user space handled it + * some other event was introduced which canceled existing + * setup. If this state is set read/write on ep0 return + * -EIDRM. This state is only set when adding event. + */ + FFS_SETUP_CANCELED +}; + +struct ffs_data { + struct usb_gadget *gadget; + + /* + * Protect access read/write operations, only one read/write + * at a time. As a consequence protects ep0req and company. + * While setup request is being processed (queued) this is + * held. + */ + struct mutex mutex; + + /* + * Protect access to endpoint related structures (basically + * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for + * endpoint zero. + */ + spinlock_t eps_lock; + + /* + * XXX REVISIT do we need our own request? Since we are not + * handling setup requests immediately user space may be so + * slow that another setup will be sent to the gadget but this + * time not to us but another function and then there could be + * a race. Is that the case? Or maybe we can use cdev->req + * after all, maybe we just need some spinlock for that? + */ + struct usb_request *ep0req; /* P: mutex */ + struct completion ep0req_completion; /* P: mutex */ + int ep0req_status; /* P: mutex */ + + /* reference counter */ + atomic_t ref; + /* how many files are opened (EP0 and others) */ + atomic_t opened; + + /* EP0 state */ + enum ffs_state state; + + /* + * Possible transitions: + * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock + * happens only in ep0 read which is P: mutex + * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock + * happens only in ep0 i/o which is P: mutex + * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock + * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg + */ + enum ffs_setup_state setup_state; + +#define FFS_SETUP_STATE(ffs) \ + ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ + FFS_SETUP_CANCELED, FFS_NO_SETUP)) + + /* Events & such. */ + struct { + u8 types[4]; + unsigned short count; + /* XXX REVISIT need to update it in some places, or do we? */ + unsigned short can_stall; + struct usb_ctrlrequest setup; + + wait_queue_head_t waitq; + } ev; /* the whole structure, P: ev.waitq.lock */ + + /* Flags */ + unsigned long flags; +#define FFS_FL_CALL_CLOSED_CALLBACK 0 +#define FFS_FL_BOUND 1 + + /* Active function */ + struct ffs_function *func; + + /* + * Device name, write once when file system is mounted. + * Intended for user to read if she wants. + */ + const char *dev_name; + /* Private data for our user (ie. gadget). Managed by user. */ + void *private_data; + + /* filled by __ffs_data_got_descs() */ + /* + * Real descriptors are 16 bytes after raw_descs (so you need + * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the + * first full speed descriptor). raw_descs_length and + * raw_fs_descs_length do not have those 16 bytes added. + */ + const void *raw_descs; + unsigned raw_descs_length; + unsigned raw_fs_descs_length; + unsigned fs_descs_count; + unsigned hs_descs_count; + + unsigned short strings_count; + unsigned short interfaces_count; + unsigned short eps_count; + unsigned short _pad1; + + /* filled by __ffs_data_got_strings() */ + /* ids in stringtabs are set in functionfs_bind() */ + const void *raw_strings; + struct usb_gadget_strings **stringtabs; + + /* + * File system's super block, write once when file system is + * mounted. + */ + struct super_block *sb; + + /* File permissions, written once when fs is mounted */ + struct ffs_file_perms { + umode_t mode; + kuid_t uid; + kgid_t gid; + } file_perms; + + /* + * The endpoint files, filled by ffs_epfiles_create(), + * destroyed by ffs_epfiles_destroy(). + */ + struct ffs_epfile *epfiles; +}; + + +#ifndef USB_FFS_INCLUDED +struct f_fs_opts { + struct usb_function_instance func_inst; + struct ffs_dev *dev; + unsigned refcnt; +}; + +static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi) +{ + return container_of(fi, struct f_fs_opts, func_inst); +} + +void ffs_set_acquire_dev_cb(void *(*cb)(const char *name)); +void ffs_set_release_dev_cb(void (*cb)(struct ffs_data *ffs_data)); +void ffs_set_ready_cb(int (*cb)(struct ffs_data *ffs_data)); +void ffs_set_closed_cb(void (*cb)(struct ffs_data *ffs_data)); + +int functionfs_init(struct module *owner); +void functionfs_cleanup(void); +struct ffs_dev *ffs_find_dev(const char *name); +bool ffs_set_auto_init(bool flag); +#endif + #endif /* U_FFS_H */ diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h index 65d0a88..5a1374a 100644 --- a/include/linux/usb/functionfs.h +++ b/include/linux/usb/functionfs.h @@ -3,13 +3,15 @@ #include <uapi/linux/usb/functionfs.h> +#ifdef USB_FFS_INCLUDED struct ffs_data; struct usb_composite_dev; struct usb_configuration; -static int functionfs_init(void) __attribute__((warn_unused_result)); +static int functionfs_init(struct module *owner) + __attribute__((warn_unused_result)); static void functionfs_cleanup(void); static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) @@ -32,5 +34,6 @@ static void *functionfs_acquire_dev_callback(const char *dev_name) static void functionfs_release_dev_callback(struct ffs_data *ffs_data) __attribute__((nonnull)); +#endif #endif -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html