| From 2ef47001b3ee3ded579b7532ebdcf8680e4d8c54 Mon Sep 17 00:00:00 2001 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Wed, 8 Nov 2017 12:23:17 -0500 |
| Subject: USB: usbfs: compute urb->actual_length for isochronous |
| |
| From: Alan Stern <stern@rowland.harvard.edu> |
| |
| commit 2ef47001b3ee3ded579b7532ebdcf8680e4d8c54 upstream. |
| |
| The USB kerneldoc says that the actual_length field "is read in |
| non-iso completion functions", but the usbfs driver uses it for all |
| URB types in processcompl(). Since not all of the host controller |
| drivers set actual_length for isochronous URBs, programs using usbfs |
| with some host controllers don't work properly. For example, Minas |
| reports that a USB camera controlled by libusb doesn't work properly |
| with a dwc2 controller. |
| |
| It doesn't seem worthwhile to change the HCDs and the documentation, |
| since the in-kernel USB class drivers evidently don't rely on |
| actual_length for isochronous transfers. The easiest solution is for |
| usbfs to calculate the actual_length value for itself, by adding up |
| the lengths of the individual packets in an isochronous transfer. |
| |
| Signed-off-by: Alan Stern <stern@rowland.harvard.edu> |
| CC: Minas Harutyunyan <Minas.Harutyunyan@synopsys.com> |
| Reported-and-tested-by: wlf <wulf@rock-chips.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/core/devio.c | 14 ++++++++++++++ |
| 1 file changed, 14 insertions(+) |
| |
| --- a/drivers/usb/core/devio.c |
| +++ b/drivers/usb/core/devio.c |
| @@ -1833,6 +1833,18 @@ static int proc_unlinkurb(struct usb_dev |
| return 0; |
| } |
| |
| +static void compute_isochronous_actual_length(struct urb *urb) |
| +{ |
| + unsigned int i; |
| + |
| + if (urb->number_of_packets > 0) { |
| + urb->actual_length = 0; |
| + for (i = 0; i < urb->number_of_packets; i++) |
| + urb->actual_length += |
| + urb->iso_frame_desc[i].actual_length; |
| + } |
| +} |
| + |
| static int processcompl(struct async *as, void __user * __user *arg) |
| { |
| struct urb *urb = as->urb; |
| @@ -1840,6 +1852,7 @@ static int processcompl(struct async *as |
| void __user *addr = as->userurb; |
| unsigned int i; |
| |
| + compute_isochronous_actual_length(urb); |
| if (as->userbuffer && urb->actual_length) { |
| if (copy_urb_data_to_user(as->userbuffer, urb)) |
| goto err_out; |
| @@ -2008,6 +2021,7 @@ static int processcompl_compat(struct as |
| void __user *addr = as->userurb; |
| unsigned int i; |
| |
| + compute_isochronous_actual_length(urb); |
| if (as->userbuffer && urb->actual_length) { |
| if (copy_urb_data_to_user(as->userbuffer, urb)) |
| return -EFAULT; |