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/core/devio.c | |
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/core/devio.c')
-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) |