summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/legacy/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/legacy/inode.c')
-rw-r--r--drivers/usb/gadget/legacy/inode.c250
1 files changed, 148 insertions, 102 deletions
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 37ca0e669bd8..62566a8e7451 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/fs_context.h>
#include <linux/pagemap.h>
#include <linux/uts.h>
#include <linux/wait.h>
@@ -19,8 +20,9 @@
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/poll.h>
-#include <linux/mmu_context.h>
+#include <linux/kthread.h>
#include <linux/aio.h>
#include <linux/uio.h>
#include <linux/refcount.h>
@@ -30,6 +32,12 @@
#include <linux/usb/gadgetfs.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h> /* for USB_GADGET_DELAYED_STATUS */
+
+/* Undef helpers from linux/usb/composite.h as gadgetfs redefines them */
+#undef DBG
+#undef ERROR
+#undef INFO
/*
@@ -109,6 +117,8 @@ enum ep0_state {
/* enough for the whole queue: most events invalidate others */
#define N_EVENT 5
+#define RBUF_SIZE 256
+
struct dev_data {
spinlock_t lock;
refcount_t count;
@@ -140,10 +150,9 @@ struct dev_data {
void *buf;
wait_queue_head_t wait;
struct super_block *sb;
- struct dentry *dentry;
/* except this scratch i/o buffer for ep0 */
- u8 rbuf [256];
+ u8 rbuf[RBUF_SIZE];
};
static inline void get_dev (struct dev_data *data)
@@ -198,7 +207,6 @@ struct ep_data {
struct usb_endpoint_descriptor desc, hs_desc;
struct list_head epfiles;
wait_queue_head_t wait;
- struct dentry *dentry;
};
static inline void get_ep (struct ep_data *data)
@@ -226,6 +234,7 @@ static void put_ep (struct ep_data *data)
*/
static const char *CHIP;
+static DEFINE_MUTEX(sb_mutex); /* Serialize superblock operations */
/*----------------------------------------------------------------------*/
@@ -311,7 +320,7 @@ nonblock:
case STATE_EP_READY: /* not configured yet */
if (is_write)
return 0;
- // FALLTHRU
+ fallthrough;
case STATE_EP_UNBOUND: /* clean disconnect */
break;
// case STATE_EP_DISABLED: /* "can't happen" */
@@ -343,7 +352,7 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len)
spin_unlock_irq (&epdata->dev->lock);
if (likely (value == 0)) {
- value = wait_event_interruptible (done.wait, done.done);
+ value = wait_for_completion_interruptible(&done);
if (value != 0) {
spin_lock_irq (&epdata->dev->lock);
if (likely (epdata->ep != NULL)) {
@@ -352,13 +361,14 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len)
usb_ep_dequeue (epdata->ep, epdata->req);
spin_unlock_irq (&epdata->dev->lock);
- wait_event (done.wait, done.done);
+ wait_for_completion(&done);
if (epdata->status == -ECONNRESET)
epdata->status = -EINTR;
} else {
spin_unlock_irq (&epdata->dev->lock);
DBG (epdata->dev, "endpoint gone\n");
+ wait_for_completion(&done);
epdata->status = -ENODEV;
}
}
@@ -461,14 +471,14 @@ static void ep_user_copy_worker(struct work_struct *work)
struct kiocb *iocb = priv->iocb;
size_t ret;
- use_mm(mm);
+ kthread_use_mm(mm);
ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
- unuse_mm(mm);
+ kthread_unuse_mm(mm);
if (!ret)
ret = -EFAULT;
/* completing the iocb can drop the ctx and mm, don't touch mm after */
- iocb->ki_complete(iocb, ret, ret);
+ iocb->ki_complete(iocb, ret);
kfree(priv->buf);
kfree(priv->to_free);
@@ -495,10 +505,8 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
kfree(priv->to_free);
kfree(priv);
iocb->private = NULL;
- /* aio_complete() reports bytes-transferred _and_ faults */
-
- iocb->ki_complete(iocb, req->actual ? req->actual : req->status,
- req->status);
+ iocb->ki_complete(iocb,
+ req->actual ? req->actual : (long)req->status);
} else {
/* ep_copy_to_user() won't report both; we hide some faults */
if (unlikely(0 != req->status))
@@ -611,7 +619,7 @@ ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
if (!priv)
goto fail;
priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
- if (!priv->to_free) {
+ if (!iter_is_ubuf(&priv->to) && !priv->to_free) {
kfree(priv);
goto fail;
}
@@ -696,7 +704,6 @@ static const struct file_operations ep_io_operations = {
.open = ep_open,
.release = ep_release,
- .llseek = no_llseek,
.unlocked_ioctl = ep_ioctl,
.read_iter = ep_read_iter,
.write_iter = ep_write_iter,
@@ -1083,7 +1090,7 @@ next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type)
case GADGETFS_DISCONNECT:
if (dev->state == STATE_DEV_SETUP)
dev->setup_abort = 1;
- // FALL THROUGH
+ fallthrough;
case GADGETFS_CONNECT:
dev->ev_next = 0;
break;
@@ -1174,7 +1181,7 @@ ep0_fasync (int f, struct file *fd, int on)
{
struct dev_data *dev = fd->private_data;
// caller must F_SETOWN before signal delivery happens
- VDEBUG (dev, "%s %s\n", __func__, on ? "on" : "off");
+ VDEBUG(dev, "%s %s\n", __func__, str_on_off(on));
return fasync_helper (f, fd, on, &dev->fasync);
}
@@ -1212,36 +1219,36 @@ dev_release (struct inode *inode, struct file *fd)
static __poll_t
ep0_poll (struct file *fd, poll_table *wait)
{
- struct dev_data *dev = fd->private_data;
- __poll_t mask = 0;
+ struct dev_data *dev = fd->private_data;
+ __poll_t mask = 0;
if (dev->state <= STATE_DEV_OPENED)
return DEFAULT_POLLMASK;
- poll_wait(fd, &dev->wait, wait);
+ poll_wait(fd, &dev->wait, wait);
- spin_lock_irq (&dev->lock);
+ spin_lock_irq(&dev->lock);
- /* report fd mode change before acting on it */
- if (dev->setup_abort) {
- dev->setup_abort = 0;
- mask = EPOLLHUP;
- goto out;
- }
+ /* report fd mode change before acting on it */
+ if (dev->setup_abort) {
+ dev->setup_abort = 0;
+ mask = EPOLLHUP;
+ goto out;
+ }
- if (dev->state == STATE_DEV_SETUP) {
- if (dev->setup_in || dev->setup_can_stall)
- mask = EPOLLOUT;
- } else {
- if (dev->ev_next != 0)
- mask = EPOLLIN;
- }
+ if (dev->state == STATE_DEV_SETUP) {
+ if (dev->setup_in || dev->setup_can_stall)
+ mask = EPOLLOUT;
+ } else {
+ if (dev->ev_next != 0)
+ mask = EPOLLIN;
+ }
out:
- spin_unlock_irq(&dev->lock);
- return mask;
+ spin_unlock_irq(&dev->lock);
+ return mask;
}
-static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
+static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value)
{
struct dev_data *dev = fd->private_data;
struct usb_gadget *gadget = dev->gadget;
@@ -1332,6 +1339,18 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ if (w_length > RBUF_SIZE) {
+ if (ctrl->bRequestType & USB_DIR_IN) {
+ /* Cast away the const, we are going to overwrite on purpose. */
+ __le16 *temp = (__le16 *)&ctrl->wLength;
+
+ *temp = cpu_to_le16(RBUF_SIZE);
+ w_length = RBUF_SIZE;
+ } else {
+ return value;
+ }
+ }
+
spin_lock (&dev->lock);
dev->setup_abort = 0;
if (dev->state == STATE_DEV_UNCONNECTED) {
@@ -1360,7 +1379,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->buf = dev->rbuf;
req->context = NULL;
- value = -EOPNOTSUPP;
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
@@ -1381,7 +1399,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
make_qualifier (dev);
break;
case USB_DT_OTHER_SPEED_CONFIG:
- // FALLTHROUGH
case USB_DT_CONFIG:
value = config_buf (dev,
w_value >> 8,
@@ -1498,7 +1515,16 @@ delegate:
event->u.setup = *ctrl;
ep0_readable (dev);
spin_unlock (&dev->lock);
- return 0;
+ /*
+ * Return USB_GADGET_DELAYED_STATUS as a workaround to
+ * stop some UDC drivers (e.g. dwc3) from automatically
+ * proceeding with the status stage for 0-length
+ * transfers.
+ * Should be removed once all UDC drivers are fixed to
+ * always delay the status stage until a response is
+ * queued to EP0.
+ */
+ return w_length == 0 ? USB_GADGET_DELAYED_STATUS : 0;
}
}
@@ -1533,18 +1559,12 @@ static void destroy_ep_files (struct dev_data *dev)
spin_lock_irq (&dev->lock);
while (!list_empty(&dev->epfiles)) {
struct ep_data *ep;
- struct inode *parent;
- struct dentry *dentry;
/* break link to FS */
ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
list_del_init (&ep->epfiles);
spin_unlock_irq (&dev->lock);
- dentry = ep->dentry;
- ep->dentry = NULL;
- parent = d_inode(dentry->d_parent);
-
/* break link to controller */
mutex_lock(&ep->lock);
if (ep->state == STATE_EP_ENABLED)
@@ -1555,13 +1575,11 @@ static void destroy_ep_files (struct dev_data *dev)
mutex_unlock(&ep->lock);
wake_up (&ep->wait);
- put_ep (ep);
/* break link to dcache */
- inode_lock(parent);
- d_delete (dentry);
- dput (dentry);
- inode_unlock(parent);
+ simple_remove_by_name(dev->sb->s_root, ep->name, NULL);
+
+ put_ep (ep);
spin_lock_irq (&dev->lock);
}
@@ -1569,14 +1587,14 @@ static void destroy_ep_files (struct dev_data *dev)
}
-static struct dentry *
-gadgetfs_create_file (struct super_block *sb, char const *name,
+static int gadgetfs_create_file (struct super_block *sb, char const *name,
void *data, const struct file_operations *fops);
static int activate_ep_files (struct dev_data *dev)
{
struct usb_ep *ep;
struct ep_data *data;
+ int err;
gadget_for_each_ep (ep, dev->gadget) {
@@ -1587,7 +1605,7 @@ static int activate_ep_files (struct dev_data *dev)
mutex_init(&data->lock);
init_waitqueue_head (&data->wait);
- strncpy (data->name, ep->name, sizeof (data->name) - 1);
+ strscpy(data->name, ep->name);
refcount_set (&data->count, 1);
data->dev = dev;
get_dev (dev);
@@ -1599,9 +1617,9 @@ static int activate_ep_files (struct dev_data *dev)
if (!data->req)
goto enomem1;
- data->dentry = gadgetfs_create_file (dev->sb, data->name,
+ err = gadgetfs_create_file (dev->sb, data->name,
data, &ep_io_operations);
- if (!data->dentry)
+ if (err)
goto enomem2;
list_add_tail (&data->epfiles, &dev->epfiles);
}
@@ -1718,7 +1736,7 @@ gadgetfs_suspend (struct usb_gadget *gadget)
case STATE_DEV_UNCONNECTED:
next_event (dev, GADGETFS_SUSPEND);
ep0_readable (dev);
- /* FALLTHROUGH */
+ fallthrough;
default:
break;
}
@@ -1735,7 +1753,7 @@ static struct usb_gadget_driver gadgetfs_driver = {
.suspend = gadgetfs_suspend,
.driver = {
- .name = (char *) shortname,
+ .name = shortname,
},
};
@@ -1783,7 +1801,7 @@ static ssize_t
dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
{
struct dev_data *dev = fd->private_data;
- ssize_t value = len, length = len;
+ ssize_t value, length = len;
unsigned total;
u32 tag;
char *kbuf;
@@ -1815,8 +1833,9 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
spin_lock_irq (&dev->lock);
value = -EINVAL;
if (dev->buf) {
+ spin_unlock_irq(&dev->lock);
kfree(kbuf);
- goto fail;
+ return value;
}
dev->buf = kbuf;
@@ -1861,10 +1880,10 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
else
gadgetfs_driver.max_speed = USB_SPEED_FULL;
- value = usb_gadget_probe_driver(&gadgetfs_driver);
+ value = usb_gadget_register_driver(&gadgetfs_driver);
if (value != 0) {
- kfree (dev->buf);
- dev->buf = NULL;
+ spin_lock_irq(&dev->lock);
+ goto fail;
} else {
/* at this point "good" hardware has for the first time
* let the USB the host see us. alternatively, if users
@@ -1881,6 +1900,9 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
return value;
fail:
+ dev->config = NULL;
+ dev->hs_config = NULL;
+ dev->dev = NULL;
spin_unlock_irq (&dev->lock);
pr_debug ("%s: %s fail %zd, %p\n", shortname, __func__, value, dev);
kfree (dev->buf);
@@ -1889,7 +1911,7 @@ fail:
}
static int
-dev_open (struct inode *inode, struct file *fd)
+gadget_dev_open (struct inode *inode, struct file *fd)
{
struct dev_data *dev = inode->i_private;
int value = -EBUSY;
@@ -1907,14 +1929,13 @@ dev_open (struct inode *inode, struct file *fd)
}
static const struct file_operations ep0_operations = {
- .llseek = no_llseek,
- .open = dev_open,
+ .open = gadget_dev_open,
.read = ep0_read,
.write = dev_config,
.fasync = ep0_fasync,
.poll = ep0_poll,
- .unlocked_ioctl = dev_ioctl,
+ .unlocked_ioctl = gadget_dev_ioctl,
.release = dev_release,
};
@@ -1952,8 +1973,7 @@ gadgetfs_make_inode (struct super_block *sb,
inode->i_mode = mode;
inode->i_uid = make_kuid(&init_user_ns, default_uid);
inode->i_gid = make_kgid(&init_user_ns, default_gid);
- inode->i_atime = inode->i_mtime = inode->i_ctime
- = current_time(inode);
+ simple_inode_init_ts(inode);
inode->i_private = data;
inode->i_fop = fops;
}
@@ -1963,44 +1983,53 @@ gadgetfs_make_inode (struct super_block *sb,
/* creates in fs root directory, so non-renamable and non-linkable.
* so inode and dentry are paired, until device reconfig.
*/
-static struct dentry *
-gadgetfs_create_file (struct super_block *sb, char const *name,
+static int gadgetfs_create_file (struct super_block *sb, char const *name,
void *data, const struct file_operations *fops)
{
struct dentry *dentry;
struct inode *inode;
- dentry = d_alloc_name(sb->s_root, name);
- if (!dentry)
- return NULL;
-
inode = gadgetfs_make_inode (sb, data, fops,
S_IFREG | (default_perm & S_IRWXUGO));
- if (!inode) {
- dput(dentry);
- return NULL;
+ if (!inode)
+ return -ENOMEM;
+
+ dentry = simple_start_creating(sb->s_root, name);
+ if (IS_ERR(dentry)) {
+ iput(inode);
+ return PTR_ERR(dentry);
}
- d_add (dentry, inode);
- return dentry;
+
+ d_make_persistent(dentry, inode);
+
+ simple_done_creating(dentry);
+ return 0;
}
static const struct super_operations gadget_fs_operations = {
.statfs = simple_statfs,
- .drop_inode = generic_delete_inode,
+ .drop_inode = inode_just_drop,
};
static int
-gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
+gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
{
struct inode *inode;
struct dev_data *dev;
+ int rc;
- if (the_device)
- return -ESRCH;
+ mutex_lock(&sb_mutex);
+
+ if (the_device) {
+ rc = -ESRCH;
+ goto Done;
+ }
CHIP = usb_get_gadget_udc_name();
- if (!CHIP)
- return -ENODEV;
+ if (!CHIP) {
+ rc = -ENODEV;
+ goto Done;
+ }
/* superblock */
sb->s_blocksize = PAGE_SIZE;
@@ -2027,8 +2056,8 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
goto Enomem;
dev->sb = sb;
- dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
- if (!dev->dentry) {
+ rc = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
+ if (rc) {
put_dev(dev);
goto Enomem;
}
@@ -2037,30 +2066,47 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
* from binding to a controller.
*/
the_device = dev;
- return 0;
+ rc = 0;
+ goto Done;
-Enomem:
- return -ENOMEM;
+ Enomem:
+ kfree(CHIP);
+ CHIP = NULL;
+ rc = -ENOMEM;
+
+ Done:
+ mutex_unlock(&sb_mutex);
+ return rc;
}
/* "mount -t gadgetfs path /dev/gadget" ends up here */
-static struct dentry *
-gadgetfs_mount (struct file_system_type *t, int flags,
- const char *path, void *opts)
+static int gadgetfs_get_tree(struct fs_context *fc)
{
- return mount_single (t, flags, opts, gadgetfs_fill_super);
+ return get_tree_single(fc, gadgetfs_fill_super);
+}
+
+static const struct fs_context_operations gadgetfs_context_ops = {
+ .get_tree = gadgetfs_get_tree,
+};
+
+static int gadgetfs_init_fs_context(struct fs_context *fc)
+{
+ fc->ops = &gadgetfs_context_ops;
+ return 0;
}
static void
gadgetfs_kill_sb (struct super_block *sb)
{
- kill_litter_super (sb);
+ mutex_lock(&sb_mutex);
+ kill_anon_super (sb);
if (the_device) {
put_dev (the_device);
the_device = NULL;
}
kfree(CHIP);
CHIP = NULL;
+ mutex_unlock(&sb_mutex);
}
/*----------------------------------------------------------------------*/
@@ -2068,14 +2114,14 @@ gadgetfs_kill_sb (struct super_block *sb)
static struct file_system_type gadgetfs_type = {
.owner = THIS_MODULE,
.name = shortname,
- .mount = gadgetfs_mount,
+ .init_fs_context = gadgetfs_init_fs_context,
.kill_sb = gadgetfs_kill_sb,
};
MODULE_ALIAS_FS("gadgetfs");
/*----------------------------------------------------------------------*/
-static int __init init (void)
+static int __init gadgetfs_init (void)
{
int status;
@@ -2085,12 +2131,12 @@ static int __init init (void)
shortname, driver_desc);
return status;
}
-module_init (init);
+module_init (gadgetfs_init);
-static void __exit cleanup (void)
+static void __exit gadgetfs_cleanup (void)
{
pr_debug ("unregister %s\n", shortname);
unregister_filesystem (&gadgetfs_type);
}
-module_exit (cleanup);
+module_exit (gadgetfs_cleanup);