static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
{
struct ffs_epfile *epfile = file->private_data;
+ struct usb_request *req;
struct ffs_ep *ep;
char *data = NULL;
ssize_t ret, data_len = -EINVAL;
if (!halt) {
/*
* if we _do_ wait above, the epfile->ffs->gadget might be NULL
- * before the waiting completes, so do not assign to 'gadget' earlier
+ * before the waiting completes, so do not assign to 'gadget'
+ * earlier
*/
struct usb_gadget *gadget = epfile->ffs->gadget;
size_t copied;
if (epfile->ep != ep) {
/* In the meantime, endpoint got disabled or changed. */
ret = -ESHUTDOWN;
- goto error_lock;
} else if (halt) {
/* Halt */
if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
usb_ep_set_halt(ep->ep);
- spin_unlock_irq(&epfile->ffs->eps_lock);
ret = -EBADMSG;
- } else {
- /* Fire the request */
- struct usb_request *req;
-
+ } else if (unlikely(data_len == -EINVAL)) {
/*
* Sanity Check: even though data_len can't be used
* uninitialized at the time I write this comment, some
* For such reason, we're adding this redundant sanity check
* here.
*/
- if (unlikely(data_len == -EINVAL)) {
- WARN(1, "%s: data_len == -EINVAL\n", __func__);
- ret = -EINVAL;
- goto error_lock;
- }
-
- if (io_data->aio) {
- req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
- if (unlikely(!req)) {
- ret = -ENOMEM;
- goto error_lock;
- }
-
- req->buf = data;
- req->length = data_len;
+ WARN(1, "%s: data_len == -EINVAL\n", __func__);
+ ret = -EINVAL;
+ } else if (!io_data->aio) {
+ DECLARE_COMPLETION_ONSTACK(done);
- io_data->buf = data;
- io_data->ep = ep->ep;
- io_data->req = req;
- io_data->ffs = epfile->ffs;
+ req = ep->req;
+ req->buf = data;
+ req->length = data_len;
- req->context = io_data;
- req->complete = ffs_epfile_async_io_complete;
+ req->context = &done;
+ req->complete = ffs_epfile_io_complete;
- ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
- if (unlikely(ret)) {
- usb_ep_free_request(ep->ep, req);
- goto error_lock;
- }
- ret = -EIOCBQUEUED;
+ ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+ if (unlikely(ret < 0))
+ goto error_lock;
- spin_unlock_irq(&epfile->ffs->eps_lock);
- } else {
- DECLARE_COMPLETION_ONSTACK(done);
+ spin_unlock_irq(&epfile->ffs->eps_lock);
- req = ep->req;
- req->buf = data;
- req->length = data_len;
+ if (unlikely(wait_for_completion_interruptible(&done))) {
+ ret = -EINTR;
+ usb_ep_dequeue(ep->ep, req);
+ goto error_mutex;
+ }
- req->context = &done;
- req->complete = ffs_epfile_io_complete;
+ /*
+ * XXX We may end up silently droping data here. Since data_len
+ * (i.e. req->length) may be bigger than len (after being
+ * rounded up to maxpacketsize), we may end up with more data
+ * then user space has space for.
+ */
+ ret = ep->status;
+ if (io_data->read && ret > 0) {
+ ret = copy_to_iter(data, ret, &io_data->data);
+ if (!ret)
+ ret = -EFAULT;
+ }
+ goto error_mutex;
+ } else if (!(req = usb_ep_alloc_request(ep->ep, GFP_KERNEL))) {
+ ret = -ENOMEM;
+ } else {
+ req->buf = data;
+ req->length = data_len;
- ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+ io_data->buf = data;
+ io_data->ep = ep->ep;
+ io_data->req = req;
+ io_data->ffs = epfile->ffs;
- spin_unlock_irq(&epfile->ffs->eps_lock);
+ req->context = io_data;
+ req->complete = ffs_epfile_async_io_complete;
- if (unlikely(ret < 0)) {
- /* nop */
- } else if (unlikely(
- wait_for_completion_interruptible(&done))) {
- ret = -EINTR;
- usb_ep_dequeue(ep->ep, req);
- } else {
- /*
- * XXX We may end up silently droping data
- * here. Since data_len (i.e. req->length) may
- * be bigger than len (after being rounded up
- * to maxpacketsize), we may end up with more
- * data then user space has space for.
- */
- ret = ep->status;
- if (io_data->read && ret > 0) {
- ret = copy_to_iter(data, ret, &io_data->data);
- if (!ret)
- ret = -EFAULT;
- }
- }
- kfree(data);
+ ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+ if (unlikely(ret)) {
+ usb_ep_free_request(ep->ep, req);
+ goto error_lock;
}
- }
- mutex_unlock(&epfile->mutex);
- return ret;
+ ret = -EIOCBQUEUED;
+ /*
+ * Do not kfree the buffer in this function. It will be freed
+ * by ffs_user_copy_worker.
+ */
+ data = NULL;
+ }
error_lock:
spin_unlock_irq(&epfile->ffs->eps_lock);
+error_mutex:
mutex_unlock(&epfile->mutex);
error:
kfree(data);