diff options
Diffstat (limited to 'drivers/usb/gadget/inode.c')
-rw-r--r-- | drivers/usb/gadget/inode.c | 82 |
1 files changed, 56 insertions, 26 deletions
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 4655522a08d9..86924f9cdd7e 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c | |||
@@ -342,7 +342,7 @@ fail: | |||
342 | static ssize_t | 342 | static ssize_t |
343 | ep_io (struct ep_data *epdata, void *buf, unsigned len) | 343 | ep_io (struct ep_data *epdata, void *buf, unsigned len) |
344 | { | 344 | { |
345 | DECLARE_COMPLETION (done); | 345 | DECLARE_COMPLETION_ONSTACK (done); |
346 | int value; | 346 | int value; |
347 | 347 | ||
348 | spin_lock_irq (&epdata->dev->lock); | 348 | spin_lock_irq (&epdata->dev->lock); |
@@ -533,7 +533,8 @@ struct kiocb_priv { | |||
533 | struct usb_request *req; | 533 | struct usb_request *req; |
534 | struct ep_data *epdata; | 534 | struct ep_data *epdata; |
535 | void *buf; | 535 | void *buf; |
536 | char __user *ubuf; /* NULL for writes */ | 536 | const struct iovec *iv; |
537 | unsigned long nr_segs; | ||
537 | unsigned actual; | 538 | unsigned actual; |
538 | }; | 539 | }; |
539 | 540 | ||
@@ -561,17 +562,32 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) | |||
561 | static ssize_t ep_aio_read_retry(struct kiocb *iocb) | 562 | static ssize_t ep_aio_read_retry(struct kiocb *iocb) |
562 | { | 563 | { |
563 | struct kiocb_priv *priv = iocb->private; | 564 | struct kiocb_priv *priv = iocb->private; |
564 | ssize_t status = priv->actual; | 565 | ssize_t len, total; |
565 | 566 | int i; | |
566 | /* we "retry" to get the right mm context for this: */ | 567 | |
567 | status = copy_to_user(priv->ubuf, priv->buf, priv->actual); | 568 | /* we "retry" to get the right mm context for this: */ |
568 | if (unlikely(0 != status)) | 569 | |
569 | status = -EFAULT; | 570 | /* copy stuff into user buffers */ |
570 | else | 571 | total = priv->actual; |
571 | status = priv->actual; | 572 | len = 0; |
572 | kfree(priv->buf); | 573 | for (i=0; i < priv->nr_segs; i++) { |
573 | kfree(priv); | 574 | ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total); |
574 | return status; | 575 | |
576 | if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) { | ||
577 | if (len == 0) | ||
578 | len = -EFAULT; | ||
579 | break; | ||
580 | } | ||
581 | |||
582 | total -= this; | ||
583 | len += this; | ||
584 | if (total == 0) | ||
585 | break; | ||
586 | } | ||
587 | kfree(priv->buf); | ||
588 | kfree(priv); | ||
589 | aio_put_req(iocb); | ||
590 | return len; | ||
575 | } | 591 | } |
576 | 592 | ||
577 | static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) | 593 | static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) |
@@ -584,7 +600,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) | |||
584 | spin_lock(&epdata->dev->lock); | 600 | spin_lock(&epdata->dev->lock); |
585 | priv->req = NULL; | 601 | priv->req = NULL; |
586 | priv->epdata = NULL; | 602 | priv->epdata = NULL; |
587 | if (priv->ubuf == NULL | 603 | if (priv->iv == NULL |
588 | || unlikely(req->actual == 0) | 604 | || unlikely(req->actual == 0) |
589 | || unlikely(kiocbIsCancelled(iocb))) { | 605 | || unlikely(kiocbIsCancelled(iocb))) { |
590 | kfree(req->buf); | 606 | kfree(req->buf); |
@@ -619,7 +635,8 @@ ep_aio_rwtail( | |||
619 | char *buf, | 635 | char *buf, |
620 | size_t len, | 636 | size_t len, |
621 | struct ep_data *epdata, | 637 | struct ep_data *epdata, |
622 | char __user *ubuf | 638 | const struct iovec *iv, |
639 | unsigned long nr_segs | ||
623 | ) | 640 | ) |
624 | { | 641 | { |
625 | struct kiocb_priv *priv; | 642 | struct kiocb_priv *priv; |
@@ -634,7 +651,8 @@ fail: | |||
634 | return value; | 651 | return value; |
635 | } | 652 | } |
636 | iocb->private = priv; | 653 | iocb->private = priv; |
637 | priv->ubuf = ubuf; | 654 | priv->iv = iv; |
655 | priv->nr_segs = nr_segs; | ||
638 | 656 | ||
639 | value = get_ready_ep(iocb->ki_filp->f_flags, epdata); | 657 | value = get_ready_ep(iocb->ki_filp->f_flags, epdata); |
640 | if (unlikely(value < 0)) { | 658 | if (unlikely(value < 0)) { |
@@ -674,41 +692,53 @@ fail: | |||
674 | kfree(priv); | 692 | kfree(priv); |
675 | put_ep(epdata); | 693 | put_ep(epdata); |
676 | } else | 694 | } else |
677 | value = (ubuf ? -EIOCBRETRY : -EIOCBQUEUED); | 695 | value = (iv ? -EIOCBRETRY : -EIOCBQUEUED); |
678 | return value; | 696 | return value; |
679 | } | 697 | } |
680 | 698 | ||
681 | static ssize_t | 699 | static ssize_t |
682 | ep_aio_read(struct kiocb *iocb, char __user *ubuf, size_t len, loff_t o) | 700 | ep_aio_read(struct kiocb *iocb, const struct iovec *iov, |
701 | unsigned long nr_segs, loff_t o) | ||
683 | { | 702 | { |
684 | struct ep_data *epdata = iocb->ki_filp->private_data; | 703 | struct ep_data *epdata = iocb->ki_filp->private_data; |
685 | char *buf; | 704 | char *buf; |
686 | 705 | ||
687 | if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN)) | 706 | if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN)) |
688 | return -EINVAL; | 707 | return -EINVAL; |
689 | buf = kmalloc(len, GFP_KERNEL); | 708 | |
709 | buf = kmalloc(iocb->ki_left, GFP_KERNEL); | ||
690 | if (unlikely(!buf)) | 710 | if (unlikely(!buf)) |
691 | return -ENOMEM; | 711 | return -ENOMEM; |
712 | |||
692 | iocb->ki_retry = ep_aio_read_retry; | 713 | iocb->ki_retry = ep_aio_read_retry; |
693 | return ep_aio_rwtail(iocb, buf, len, epdata, ubuf); | 714 | return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs); |
694 | } | 715 | } |
695 | 716 | ||
696 | static ssize_t | 717 | static ssize_t |
697 | ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o) | 718 | ep_aio_write(struct kiocb *iocb, const struct iovec *iov, |
719 | unsigned long nr_segs, loff_t o) | ||
698 | { | 720 | { |
699 | struct ep_data *epdata = iocb->ki_filp->private_data; | 721 | struct ep_data *epdata = iocb->ki_filp->private_data; |
700 | char *buf; | 722 | char *buf; |
723 | size_t len = 0; | ||
724 | int i = 0; | ||
701 | 725 | ||
702 | if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN))) | 726 | if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN))) |
703 | return -EINVAL; | 727 | return -EINVAL; |
704 | buf = kmalloc(len, GFP_KERNEL); | 728 | |
729 | buf = kmalloc(iocb->ki_left, GFP_KERNEL); | ||
705 | if (unlikely(!buf)) | 730 | if (unlikely(!buf)) |
706 | return -ENOMEM; | 731 | return -ENOMEM; |
707 | if (unlikely(copy_from_user(buf, ubuf, len) != 0)) { | 732 | |
708 | kfree(buf); | 733 | for (i=0; i < nr_segs; i++) { |
709 | return -EFAULT; | 734 | if (unlikely(copy_from_user(&buf[len], iov[i].iov_base, |
735 | iov[i].iov_len) != 0)) { | ||
736 | kfree(buf); | ||
737 | return -EFAULT; | ||
738 | } | ||
739 | len += iov[i].iov_len; | ||
710 | } | 740 | } |
711 | return ep_aio_rwtail(iocb, buf, len, epdata, NULL); | 741 | return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0); |
712 | } | 742 | } |
713 | 743 | ||
714 | /*----------------------------------------------------------------------*/ | 744 | /*----------------------------------------------------------------------*/ |