diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_fs.c')
| -rw-r--r-- | drivers/usb/gadget/function/f_fs.c | 163 |
1 files changed, 133 insertions, 30 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 44218be1e676..43962e05eacf 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -159,7 +159,9 @@ struct ffs_epfile { struct mutex mutex; struct ffs_data *ffs; - struct ffs_ep *ep; /* P: ffs->eps_lock */ + struct ffs_ep *ep; /* P: ffs->eps_lock */ + struct ffs_epfile *epfile_in; /* P: ffs->eps_lock */ + struct ffs_epfile *epfile_out; /* P: ffs->eps_lock */ /* * Buffer for holding data from partial reads which may happen since @@ -219,12 +221,13 @@ struct ffs_epfile { struct ffs_buffer *read_buffer; #define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN)) - char name[5]; + char name[8]; unsigned char in; /* P: ffs->eps_lock */ unsigned char isoc; /* P: ffs->eps_lock */ - unsigned char _pad; + u8 zlp_enabled; /* P: ffs->eps_lock */ + bool is_rw_proxy; /* Protects dmabufs */ struct mutex dmabufs_mutex; @@ -548,6 +551,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; +retry: /* Acquire mutex */ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); if (ret < 0) @@ -582,10 +586,15 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, break; } - if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq, - ffs->ev.count)) { - ret = -EINTR; - break; + if (!ffs->ev.count) { + spin_unlock_irq(&ffs->ev.waitq.lock); + mutex_unlock(&ffs->mutex); + + if (wait_event_interruptible_exclusive(ffs->ev.waitq, + ffs->ev.count)) + return -EINTR; + + goto retry; } /* unlocks spinlock */ @@ -867,9 +876,15 @@ static void ffs_user_copy_worker(struct work_struct *work) bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD; if (io_data->read && ret > 0) { - kthread_use_mm(io_data->mm); - ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data); - kthread_unuse_mm(io_data->mm); + if (mmget_not_zero(io_data->mm)) { + kthread_use_mm(io_data->mm); + ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data); + kthread_unuse_mm(io_data->mm); + mmput(io_data->mm); + } else { + ret = -EFAULT; + } + mmdrop(io_data->mm); } io_data->kiocb->ki_complete(io_data->kiocb, ret); @@ -979,9 +994,8 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, return ret; } -static struct ffs_ep *ffs_epfile_wait_ep(struct file *file) +static struct ffs_ep *ffs_epfile_wait_ep(struct ffs_epfile *epfile, struct file *file) { - struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; int ret; @@ -1008,17 +1022,22 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) char *data = NULL; ssize_t ret, data_len = -EINVAL; int halt; + bool is_rw_proxy = epfile->is_rw_proxy; /* Are we still active? */ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) return -ENODEV; - ep = ffs_epfile_wait_ep(file); + /* Proxy to base endpoint if rw_proxy */ + if (is_rw_proxy) + epfile = io_data->read ? epfile->epfile_out : epfile->epfile_in; + + ep = ffs_epfile_wait_ep(epfile, file); if (IS_ERR(ep)) return PTR_ERR(ep); /* Do we halt? */ - halt = (!io_data->read == !epfile->in); + halt = is_rw_proxy ? 0 : (!io_data->read == !epfile->in); if (halt && epfile->isoc) return -EINVAL; @@ -1115,6 +1134,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->buf = data; req->num_sgs = 0; } + + req->zero = !io_data->read ? epfile->zlp_enabled : 0; req->length = data_len; io_data->buf = data; @@ -1166,6 +1187,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->buf = data; req->num_sgs = 0; } + + req->zero = !io_data->read ? epfile->zlp_enabled : 0; req->length = data_len; io_data->buf = data; @@ -1264,16 +1287,22 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) + if (p->aio) { + mmgrab(p->mm); kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + } res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; - if (p->aio) + if (p->aio) { + kiocb->ki_complete(kiocb, res); + mmdrop(p->mm); kfree(p); - else + return -EIOCBQUEUED; + } else { *from = p->data; + } return res; } @@ -1308,16 +1337,21 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) + if (p->aio) { + mmgrab(p->mm); kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + } res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; if (p->aio) { + kiocb->ki_complete(kiocb, res); + mmdrop(p->mm); kfree(p->to_free); kfree(p); + return -EIOCBQUEUED; } else { *to = p->data; } @@ -1643,7 +1677,7 @@ static int ffs_dmabuf_transfer(struct file *file, priv = attach->importer_priv; - ep = ffs_epfile_wait_ep(file); + ep = ffs_epfile_wait_ep(epfile, file); if (IS_ERR(ep)) { ret = PTR_ERR(ep); goto err_attachment_put; @@ -1682,13 +1716,13 @@ static int ffs_dmabuf_transfer(struct file *file, /* In the meantime, endpoint got disabled or changed. */ if (epfile->ep != ep) { ret = -ESHUTDOWN; - goto err_fence_put; + goto err_fence_free; } usb_req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC); if (!usb_req) { ret = -ENOMEM; - goto err_fence_put; + goto err_fence_free; } /* @@ -1709,6 +1743,7 @@ static int ffs_dmabuf_transfer(struct file *file, /* Now that the dma_fence is in place, queue the transfer. */ + usb_req->zero = epfile->zlp_enabled; usb_req->length = req->length; usb_req->buf = NULL; usb_req->sg = priv->sgt->sgl; @@ -1737,9 +1772,9 @@ static int ffs_dmabuf_transfer(struct file *file, return ret; -err_fence_put: +err_fence_free: spin_unlock_irq(&epfile->ffs->eps_lock); - dma_fence_put(&fence->base); + kfree(fence); err_resv_unlock: dma_resv_unlock(dmabuf->resv); err_attachment_put: @@ -1756,10 +1791,14 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; int ret; + __u32 enable_zlp = 0; if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) return -ENODEV; + if (epfile->is_rw_proxy) + return -ENOTTY; + switch (code) { case FUNCTIONFS_DMABUF_ATTACH: { @@ -1788,12 +1827,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, return ffs_dmabuf_transfer(file, &req); } + /* + * We handle this IOCTL before ffs_epfile_wait_ep() to allow userspace + * to configure ZLP behavior immediately without blocking indefinitely + * while waiting for the USB host to connect and enable the endpoint. + */ + case FUNCTIONFS_ENDPOINT_ENABLE_ZLP: + if (!epfile->in) + return -EINVAL; + + if (copy_from_user(&enable_zlp, (void __user *)value, sizeof(enable_zlp))) + return -EFAULT; + + spin_lock_irq(&epfile->ffs->eps_lock); + epfile->zlp_enabled = !!enable_zlp; + spin_unlock_irq(&epfile->ffs->eps_lock); + + return 0; default: break; } /* Wait for endpoint to be enabled */ - ep = ffs_epfile_wait_ep(file); + ep = ffs_epfile_wait_ep(epfile, file); if (IS_ERR(ep)) return PTR_ERR(ep); @@ -2191,7 +2247,7 @@ static void ffs_data_closed(struct ffs_data *ffs) if (epfiles) ffs_epfiles_destroy(ffs->sb, epfiles, - ffs->eps_count); + ffs->epfiles_count); if (ffs->setup_state == FFS_SETUP_PENDING) __ffs_ep0_stall(ffs); @@ -2250,7 +2306,7 @@ static void ffs_data_clear(struct ffs_data *ffs) * copy of epfile will save us from use-after-free. */ if (epfiles) { - ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); + ffs_epfiles_destroy(ffs->sb, epfiles, ffs->epfiles_count); ffs->epfiles = NULL; } @@ -2348,11 +2404,16 @@ static void functionfs_unbind(struct ffs_data *ffs) static int ffs_epfiles_create(struct ffs_data *ffs) { struct ffs_epfile *epfile, *epfiles; - unsigned i, count; + unsigned int i, count, epfiles_count; int err; count = ffs->eps_count; - epfiles = kzalloc_objs(*epfiles, count); + epfiles_count = count; + if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) + epfiles_count += count / 2; + ffs->epfiles_count = epfiles_count; + + epfiles = kzalloc_objs(*epfiles, epfiles_count); if (!epfiles) return -ENOMEM; @@ -2375,6 +2436,32 @@ static int ffs_epfiles_create(struct ffs_data *ffs) } } + if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) { + struct ffs_epfile *comp = epfiles + count; + + for (i = 0; i < count; i += 2, ++comp) { + struct ffs_epfile *ep1 = &epfiles[i]; + struct ffs_epfile *ep2 = &epfiles[i + 1]; + bool ep1_in = ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK; + + comp->ffs = ffs; + comp->is_rw_proxy = true; + comp->epfile_in = ep1_in ? ep1 : ep2; + comp->epfile_out = ep1_in ? ep2 : ep1; + mutex_init(&comp->mutex); + mutex_init(&comp->dmabufs_mutex); + INIT_LIST_HEAD(&comp->dmabufs); + snprintf(comp->name, sizeof(comp->name), "%s_rw", + epfiles[i].name); + err = ffs_sb_create_file(ffs->sb, comp->name, + comp, &ffs_epfile_operations); + if (err) { + ffs_epfiles_destroy(ffs->sb, epfiles, count + (i / 2)); + return err; + } + } + } + ffs->epfiles = epfiles; return 0; } @@ -2952,7 +3039,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, FUNCTIONFS_VIRTUAL_ADDR | FUNCTIONFS_EVENTFD | FUNCTIONFS_ALL_CTRL_RECIP | - FUNCTIONFS_CONFIG0_SETUP)) { + FUNCTIONFS_CONFIG0_SETUP | + FUNCTIONFS_RW_PROXY_EPS)) { ret = -ENOSYS; goto error; } @@ -3040,6 +3128,21 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, goto error; } + if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) { + if (ffs->eps_count % 2) { + ret = -EINVAL; + goto error; + } + + for (i = 1; i < ffs->eps_count; i += 2) { + if ((ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) == + (ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK)) { + ret = -EINVAL; + goto error; + } + } + } + ffs->raw_descs_data = _data; ffs->raw_descs = raw_descs; ffs->raw_descs_length = data - raw_descs; @@ -3335,7 +3438,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, struct usb_request *req; struct usb_ep *ep; u8 bEndpointAddress; - u16 wMaxPacketSize; + __le16 wMaxPacketSize; /* * We back up bEndpointAddress because autoconfig overwrites |
