summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/function/f_fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/function/f_fs.c')
-rw-r--r--drivers/usb/gadget/function/f_fs.c163
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