linux/drivers/usb/gadget/function/f_fs.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * f_fs.c -- user mode file system API for USB composite function controllers
 *
 * Copyright (C) 2010 Samsung Electronics
 * Author: Michal Nazarewicz <[email protected]>
 *
 * Based on inode.c (GadgetFS) which was:
 * Copyright (C) 2003-2004 David Brownell
 * Copyright (C) 2003 Agilent Technologies
 */


/* #define DEBUG */
/* #define VERBOSE_DEBUG */

#include <linux/blkdev.h>
#include <linux/dma-buf.h>
#include <linux/dma-fence.h>
#include <linux/dma-resv.h>
#include <linux/pagemap.h>
#include <linux/export.h>
#include <linux/fs_parser.h>
#include <linux/hid.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/sched/signal.h>
#include <linux/uio.h>
#include <linux/vmalloc.h>
#include <linux/unaligned.h>

#include <linux/usb/ccid.h>
#include <linux/usb/composite.h>
#include <linux/usb/functionfs.h>
#include <linux/usb/func_utils.h>

#include <linux/aio.h>
#include <linux/kthread.h>
#include <linux/poll.h>
#include <linux/eventfd.h>

#include "u_fs.h"
#include "u_os_desc.h"
#include "configfs.h"

#define FUNCTIONFS_MAGIC
#define MAX_ALT_SETTINGS

#define DMABUF_ENQUEUE_TIMEOUT_MS

MODULE_IMPORT_NS();

/* Reference counter handling */
static void ffs_data_get(struct ffs_data *ffs);
static void ffs_data_put(struct ffs_data *ffs);
/* Creates new ffs_data object. */
static struct ffs_data *__must_check ffs_data_new(const char *dev_name)
	__attribute__((malloc));

/* Opened counter handling. */
static void ffs_data_opened(struct ffs_data *ffs);
static void ffs_data_closed(struct ffs_data *ffs);

/* Called with ffs->mutex held; take over ownership of data. */
static int __must_check
__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
static int __must_check
__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);


/* The function structure ***************************************************/

struct ffs_ep;

struct ffs_function {};


static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
{}


static inline enum ffs_setup_state
ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
{}


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 int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
static int ffs_func_get_alt(struct usb_function *f, unsigned int intf);
static void ffs_func_disable(struct usb_function *);
static int ffs_func_setup(struct usb_function *,
			  const struct usb_ctrlrequest *);
static bool ffs_func_req_match(struct usb_function *,
			       const struct usb_ctrlrequest *,
			       bool config0);
static void ffs_func_suspend(struct usb_function *);
static void ffs_func_resume(struct usb_function *);


static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);


/* The endpoints structures *************************************************/

struct ffs_ep {};

struct ffs_dmabuf_priv {};

struct ffs_dma_fence {};

struct ffs_epfile {};

struct ffs_buffer {};

/*  ffs_io_data structure ***************************************************/

struct ffs_io_data {};

struct ffs_desc_helper {};

static int  __must_check ffs_epfiles_create(struct ffs_data *ffs);
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);

static struct dentry *
ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
		   const struct file_operations *fops);

/* Devices management *******************************************************/

DEFINE_MUTEX();
EXPORT_SYMBOL_GPL();

static struct ffs_dev *_ffs_find_dev(const char *name);
static struct ffs_dev *_ffs_alloc_dev(void);
static void _ffs_free_dev(struct ffs_dev *dev);
static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data);
static void ffs_release_dev(struct ffs_dev *ffs_dev);
static int ffs_ready(struct ffs_data *ffs);
static void ffs_closed(struct ffs_data *ffs);

/* Misc helper functions ****************************************************/

static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
	__attribute__((warn_unused_result, nonnull));
static char *ffs_prepare_buffer(const char __user *buf, size_t len)
	__attribute__((warn_unused_result, nonnull));


/* Control file aka ep0 *****************************************************/

static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
{}

static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
	__releases(&ffs->ev.waitq.lock)
{}

static int __ffs_ep0_stall(struct ffs_data *ffs)
{}

static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
			     size_t len, loff_t *ptr)
{}

/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
				     size_t n)
	__releases(&ffs->ev.waitq.lock)
{}

static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
			    size_t len, loff_t *ptr)
{}

static int ffs_ep0_open(struct inode *inode, struct file *file)
{}

static int ffs_ep0_release(struct inode *inode, struct file *file)
{}

static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
{}

static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait)
{}

static const struct file_operations ffs_ep0_operations =;


/* "Normal" endpoints operations ********************************************/

static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
{}

static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter)
{}

/*
 * allocate a virtually contiguous buffer and create a scatterlist describing it
 * @sg_table	- pointer to a place to be filled with sg_table contents
 * @size	- required buffer size
 */
static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz)
{}

static inline void *ffs_alloc_buffer(struct ffs_io_data *io_data,
	size_t data_len)
{}

static inline void ffs_free_buffer(struct ffs_io_data *io_data)
{}

static void ffs_user_copy_worker(struct work_struct *work)
{}

static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
					 struct usb_request *req)
{}

static void __ffs_epfile_read_buffer_free(struct ffs_epfile *epfile)
{}

/* Assumes epfile->mutex is held. */
static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
					  struct iov_iter *iter)
{}

/* Assumes epfile->mutex is held. */
static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
				      void *data, int data_len,
				      struct iov_iter *iter)
{}

static struct ffs_ep *ffs_epfile_wait_ep(struct file *file)
{}

static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
{}

static int
ffs_epfile_open(struct inode *inode, struct file *file)
{}

static int ffs_aio_cancel(struct kiocb *kiocb)
{}

static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
{}

static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
{}

static void ffs_dmabuf_release(struct kref *ref)
{}

static void ffs_dmabuf_get(struct dma_buf_attachment *attach)
{}

static void ffs_dmabuf_put(struct dma_buf_attachment *attach)
{}

static int
ffs_epfile_release(struct inode *inode, struct file *file)
{}

static void ffs_dmabuf_cleanup(struct work_struct *work)
{}

static void ffs_dmabuf_signal_done(struct ffs_dma_fence *dma_fence, int ret)
{}

static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep,
					  struct usb_request *req)
{}

static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence)
{}

static const char *ffs_dmabuf_get_timeline_name(struct dma_fence *fence)
{}

static void ffs_dmabuf_fence_release(struct dma_fence *fence)
{}

static const struct dma_fence_ops ffs_dmabuf_fence_ops =;

static int ffs_dma_resv_lock(struct dma_buf *dmabuf, bool nonblock)
{}

static struct dma_buf_attachment *
ffs_dmabuf_find_attachment(struct ffs_epfile *epfile, struct dma_buf *dmabuf)
{}

static int ffs_dmabuf_attach(struct file *file, int fd)
{}

static int ffs_dmabuf_detach(struct file *file, int fd)
{}

static int ffs_dmabuf_transfer(struct file *file,
			       const struct usb_ffs_dmabuf_transfer_req *req)
{}

static long ffs_epfile_ioctl(struct file *file, unsigned code,
			     unsigned long value)
{}

static const struct file_operations ffs_epfile_operations =;


/* File system and super block operations ***********************************/

/*
 * Mounting the file system creates a controller file, used first for
 * function configuration then later for event monitoring.
 */

static struct inode *__must_check
ffs_sb_make_inode(struct super_block *sb, void *data,
		  const struct file_operations *fops,
		  const struct inode_operations *iops,
		  struct ffs_file_perms *perms)
{}

/* Create "regular" file */
static struct dentry *ffs_sb_create_file(struct super_block *sb,
					const char *name, void *data,
					const struct file_operations *fops)
{}

/* Super block */
static const struct super_operations ffs_sb_operations =;

struct ffs_sb_fill_data {};

static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
{}

enum {};

static const struct fs_parameter_spec ffs_fs_fs_parameters[] =;

static int ffs_fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{}

/*
 * Set up the superblock for a mount.
 */
static int ffs_fs_get_tree(struct fs_context *fc)
{}

static void ffs_fs_free_fc(struct fs_context *fc)
{}

static const struct fs_context_operations ffs_fs_context_ops =;

static int ffs_fs_init_fs_context(struct fs_context *fc)
{}

static void
ffs_fs_kill_sb(struct super_block *sb)
{}

static struct file_system_type ffs_fs_type =;
MODULE_ALIAS_FS();


/* Driver's main init/cleanup functions *************************************/

static int functionfs_init(void)
{}

static void functionfs_cleanup(void)
{}


/* ffs_data and ffs_function construction and destruction code **************/

static void ffs_data_clear(struct ffs_data *ffs);
static void ffs_data_reset(struct ffs_data *ffs);

static void ffs_data_get(struct ffs_data *ffs)
{}

static void ffs_data_opened(struct ffs_data *ffs)
{}

static void ffs_data_put(struct ffs_data *ffs)
{}

static void ffs_data_closed(struct ffs_data *ffs)
{}

static struct ffs_data *ffs_data_new(const char *dev_name)
{}

static void ffs_data_clear(struct ffs_data *ffs)
{}

static void ffs_data_reset(struct ffs_data *ffs)
{}


static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
{}

static void functionfs_unbind(struct ffs_data *ffs)
{}

static int ffs_epfiles_create(struct ffs_data *ffs)
{}

static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
{}

static void ffs_func_eps_disable(struct ffs_function *func)
{}

static int ffs_func_eps_enable(struct ffs_function *func)
{}


/* Parsing and building descriptors and strings *****************************/

/*
 * This validates if data pointed by data is a valid USB descriptor as
 * well as record how many interfaces, endpoints and strings are
 * required by given configuration.  Returns address after the
 * descriptor or NULL if data is invalid.
 */

enum ffs_entity_type {};

enum ffs_os_desc_type {};

ffs_entity_callback;

ffs_os_desc_callback;

static int __must_check ffs_do_single_desc(char *data, unsigned len,
					   ffs_entity_callback entity,
					   void *priv, int *current_class, int *current_subclass)
{}

static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
				     ffs_entity_callback entity, void *priv)
{}

static int __ffs_data_do_entity(enum ffs_entity_type type,
				u8 *valuep, struct usb_descriptor_header *desc,
				void *priv)
{}

static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
				   struct usb_os_desc_header *desc)
{}

/*
 * Process all extended compatibility/extended property descriptors
 * of a feature descriptor
 */
static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
					      enum ffs_os_desc_type type,
					      u16 feature_count,
					      ffs_os_desc_callback entity,
					      void *priv,
					      struct usb_os_desc_header *h)
{}

/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
static int __must_check ffs_do_os_descs(unsigned count,
					char *data, unsigned len,
					ffs_os_desc_callback entity, void *priv)
{}

/*
 * Validate contents of the buffer from userspace related to OS descriptors.
 */
static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
				 struct usb_os_desc_header *h, void *data,
				 unsigned len, void *priv)
{}

static int __ffs_data_got_descs(struct ffs_data *ffs,
				char *const _data, size_t len)
{}

static int __ffs_data_got_strings(struct ffs_data *ffs,
				  char *const _data, size_t len)
{}


/* Events handling and management *******************************************/

static void __ffs_event_add(struct ffs_data *ffs,
			    enum usb_functionfs_event_type type)
{}

static void ffs_event_add(struct ffs_data *ffs,
			  enum usb_functionfs_event_type type)
{}

/* Bind/unbind USB function hooks *******************************************/

static int ffs_ep_addr2idx(struct ffs_data *ffs, u8 endpoint_address)
{}

static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
				    struct usb_descriptor_header *desc,
				    void *priv)
{}

static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
				   struct usb_descriptor_header *desc,
				   void *priv)
{}

static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
				      struct usb_os_desc_header *h, void *data,
				      unsigned len, void *priv)
{}

static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
						struct usb_configuration *c)
{}

static int _ffs_func_bind(struct usb_configuration *c,
			  struct usb_function *f)
{}

static int ffs_func_bind(struct usb_configuration *c,
			 struct usb_function *f)
{}


/* Other USB function hooks *************************************************/

static void ffs_reset_work(struct work_struct *work)
{}

static int ffs_func_get_alt(struct usb_function *f,
			    unsigned int interface)
{}

static int ffs_func_set_alt(struct usb_function *f,
			    unsigned interface, unsigned alt)
{}

static void ffs_func_disable(struct usb_function *f)
{}

static int ffs_func_setup(struct usb_function *f,
			  const struct usb_ctrlrequest *creq)
{}

static bool ffs_func_req_match(struct usb_function *f,
			       const struct usb_ctrlrequest *creq,
			       bool config0)
{}

static void ffs_func_suspend(struct usb_function *f)
{}

static void ffs_func_resume(struct usb_function *f)
{}


/* Endpoint and interface numbers reverse mapping ***************************/

static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
{}

static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
{}


/* Devices management *******************************************************/

static LIST_HEAD(ffs_devices);

static struct ffs_dev *_ffs_do_find_dev(const char *name)
{}

/*
 * ffs_lock must be taken by the caller of this function
 */
static struct ffs_dev *_ffs_get_single_dev(void)
{}

/*
 * ffs_lock must be taken by the caller of this function
 */
static struct ffs_dev *_ffs_find_dev(const char *name)
{}

/* Configfs support *********************************************************/

static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
{}

static ssize_t f_fs_opts_ready_show(struct config_item *item, char *page)
{}

CONFIGFS_ATTR_RO();

static struct configfs_attribute *ffs_attrs[] =;

static void ffs_attr_release(struct config_item *item)
{}

static struct configfs_item_operations ffs_item_ops =;

static const struct config_item_type ffs_func_type =;


/* Function registration interface ******************************************/

static void ffs_free_inst(struct usb_function_instance *f)
{}

static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
{}

static struct usb_function_instance *ffs_alloc_inst(void)
{}

static void ffs_free(struct usb_function *f)
{}

static void ffs_func_unbind(struct usb_configuration *c,
			    struct usb_function *f)
{}

static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
{}

/*
 * ffs_lock must be taken by the caller of this function
 */
static struct ffs_dev *_ffs_alloc_dev(void)
{}

int ffs_name_dev(struct ffs_dev *dev, const char *name)
{}
EXPORT_SYMBOL_GPL();

int ffs_single_dev(struct ffs_dev *dev)
{}
EXPORT_SYMBOL_GPL();

/*
 * ffs_lock must be taken by the caller of this function
 */
static void _ffs_free_dev(struct ffs_dev *dev)
{}

static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data)
{}

static void ffs_release_dev(struct ffs_dev *ffs_dev)
{}

static int ffs_ready(struct ffs_data *ffs)
{}

static void ffs_closed(struct ffs_data *ffs)
{}

/* Misc helper functions ****************************************************/

static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
{}

static char *ffs_prepare_buffer(const char __user *buf, size_t len)
{}

DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
MODULE_DESCRIPTION();
MODULE_LICENSE();
MODULE_AUTHOR();