diff options
| author | Hans de Goede <hdegoede@redhat.com> | 2013-10-09 11:19:31 -0400 |
|---|---|---|
| committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2014-03-04 18:38:05 -0500 |
| commit | bcf7f6e39335af4f03da8c26a98185fd49754fcc (patch) | |
| tree | 61a8b40af9264409c66ced8ac324e3f3274f00ff /drivers/usb | |
| parent | 2fec32b06e374642802f7fb4f5350317cd14732b (diff) | |
usbfs: Add support for allocating / freeing streams
This allows userspace to use bulk-streams, just like in kernel drivers, see
Documentation/usb/bulk-streams.txt for details on the in kernel API. This
is exported pretty much one on one to userspace.
To use streams an app must first make a USBDEVFS_ALLOC_STREAMS ioctl,
on success this will return the number of streams available (which may be
less then requested). If there are n streams the app can then submit
usbdevfs_urb-s with their stream_id member set to 1-n to use a specific
stream. IE if USBDEVFS_ALLOC_STREAMS returns 4 then stream_id 1-4 can be
used.
When the app is done using streams it should call USBDEVFS_FREE_STREAMS
Note applications are advised to use libusb rather then using the
usbdevfs api directly. The latest version of libusb has support for streams.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb')
| -rw-r--r-- | drivers/usb/core/devio.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 502974b4deb5..12401ee4ba0e 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
| @@ -778,6 +778,79 @@ static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev, | |||
| 778 | return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK]; | 778 | return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK]; |
| 779 | } | 779 | } |
| 780 | 780 | ||
| 781 | static int parse_usbdevfs_streams(struct dev_state *ps, | ||
| 782 | struct usbdevfs_streams __user *streams, | ||
| 783 | unsigned int *num_streams_ret, | ||
| 784 | unsigned int *num_eps_ret, | ||
| 785 | struct usb_host_endpoint ***eps_ret, | ||
| 786 | struct usb_interface **intf_ret) | ||
| 787 | { | ||
| 788 | unsigned int i, num_streams, num_eps; | ||
| 789 | struct usb_host_endpoint **eps; | ||
| 790 | struct usb_interface *intf = NULL; | ||
| 791 | unsigned char ep; | ||
| 792 | int ifnum, ret; | ||
| 793 | |||
| 794 | if (get_user(num_streams, &streams->num_streams) || | ||
| 795 | get_user(num_eps, &streams->num_eps)) | ||
| 796 | return -EFAULT; | ||
| 797 | |||
| 798 | if (num_eps < 1 || num_eps > USB_MAXENDPOINTS) | ||
| 799 | return -EINVAL; | ||
| 800 | |||
| 801 | /* The XHCI controller allows max 2 ^ 16 streams */ | ||
| 802 | if (num_streams_ret && (num_streams < 2 || num_streams > 65536)) | ||
| 803 | return -EINVAL; | ||
| 804 | |||
| 805 | eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL); | ||
| 806 | if (!eps) | ||
| 807 | return -ENOMEM; | ||
| 808 | |||
| 809 | for (i = 0; i < num_eps; i++) { | ||
| 810 | if (get_user(ep, &streams->eps[i])) { | ||
| 811 | ret = -EFAULT; | ||
| 812 | goto error; | ||
| 813 | } | ||
| 814 | eps[i] = ep_to_host_endpoint(ps->dev, ep); | ||
| 815 | if (!eps[i]) { | ||
| 816 | ret = -EINVAL; | ||
| 817 | goto error; | ||
| 818 | } | ||
| 819 | |||
| 820 | /* usb_alloc/free_streams operate on an usb_interface */ | ||
| 821 | ifnum = findintfep(ps->dev, ep); | ||
| 822 | if (ifnum < 0) { | ||
| 823 | ret = ifnum; | ||
| 824 | goto error; | ||
| 825 | } | ||
| 826 | |||
| 827 | if (i == 0) { | ||
| 828 | ret = checkintf(ps, ifnum); | ||
| 829 | if (ret < 0) | ||
| 830 | goto error; | ||
| 831 | intf = usb_ifnum_to_if(ps->dev, ifnum); | ||
| 832 | } else { | ||
| 833 | /* Verify all eps belong to the same interface */ | ||
| 834 | if (ifnum != intf->altsetting->desc.bInterfaceNumber) { | ||
| 835 | ret = -EINVAL; | ||
| 836 | goto error; | ||
| 837 | } | ||
| 838 | } | ||
| 839 | } | ||
| 840 | |||
| 841 | if (num_streams_ret) | ||
| 842 | *num_streams_ret = num_streams; | ||
| 843 | *num_eps_ret = num_eps; | ||
| 844 | *eps_ret = eps; | ||
| 845 | *intf_ret = intf; | ||
| 846 | |||
| 847 | return 0; | ||
| 848 | |||
| 849 | error: | ||
| 850 | kfree(eps); | ||
| 851 | return ret; | ||
| 852 | } | ||
| 853 | |||
| 781 | static int match_devt(struct device *dev, void *data) | 854 | static int match_devt(struct device *dev, void *data) |
| 782 | { | 855 | { |
| 783 | return dev->devt == (dev_t) (unsigned long) data; | 856 | return dev->devt == (dev_t) (unsigned long) data; |
| @@ -2009,6 +2082,45 @@ static int proc_disconnect_claim(struct dev_state *ps, void __user *arg) | |||
| 2009 | return claimintf(ps, dc.interface); | 2082 | return claimintf(ps, dc.interface); |
| 2010 | } | 2083 | } |
| 2011 | 2084 | ||
| 2085 | static int proc_alloc_streams(struct dev_state *ps, void __user *arg) | ||
| 2086 | { | ||
| 2087 | unsigned num_streams, num_eps; | ||
| 2088 | struct usb_host_endpoint **eps; | ||
| 2089 | struct usb_interface *intf; | ||
| 2090 | int r; | ||
| 2091 | |||
| 2092 | r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps, | ||
| 2093 | &eps, &intf); | ||
| 2094 | if (r) | ||
| 2095 | return r; | ||
| 2096 | |||
| 2097 | destroy_async_on_interface(ps, | ||
| 2098 | intf->altsetting[0].desc.bInterfaceNumber); | ||
| 2099 | |||
| 2100 | r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL); | ||
| 2101 | kfree(eps); | ||
| 2102 | return r; | ||
| 2103 | } | ||
| 2104 | |||
| 2105 | static int proc_free_streams(struct dev_state *ps, void __user *arg) | ||
| 2106 | { | ||
| 2107 | unsigned num_eps; | ||
| 2108 | struct usb_host_endpoint **eps; | ||
| 2109 | struct usb_interface *intf; | ||
| 2110 | int r; | ||
| 2111 | |||
| 2112 | r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf); | ||
| 2113 | if (r) | ||
| 2114 | return r; | ||
| 2115 | |||
| 2116 | destroy_async_on_interface(ps, | ||
| 2117 | intf->altsetting[0].desc.bInterfaceNumber); | ||
| 2118 | |||
| 2119 | r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL); | ||
| 2120 | kfree(eps); | ||
| 2121 | return r; | ||
| 2122 | } | ||
| 2123 | |||
| 2012 | /* | 2124 | /* |
| 2013 | * NOTE: All requests here that have interface numbers as parameters | 2125 | * NOTE: All requests here that have interface numbers as parameters |
| 2014 | * are assuming that somehow the configuration has been prevented from | 2126 | * are assuming that somehow the configuration has been prevented from |
| @@ -2185,6 +2297,12 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 2185 | case USBDEVFS_DISCONNECT_CLAIM: | 2297 | case USBDEVFS_DISCONNECT_CLAIM: |
| 2186 | ret = proc_disconnect_claim(ps, p); | 2298 | ret = proc_disconnect_claim(ps, p); |
| 2187 | break; | 2299 | break; |
| 2300 | case USBDEVFS_ALLOC_STREAMS: | ||
| 2301 | ret = proc_alloc_streams(ps, p); | ||
| 2302 | break; | ||
| 2303 | case USBDEVFS_FREE_STREAMS: | ||
| 2304 | ret = proc_free_streams(ps, p); | ||
| 2305 | break; | ||
| 2188 | } | 2306 | } |
| 2189 | usb_unlock_device(dev); | 2307 | usb_unlock_device(dev); |
| 2190 | if (ret >= 0) | 2308 | if (ret >= 0) |
