Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a5d1442 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

USB: usbfs: compute urb->actual_length for isochronous



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: default avatarAlan Stern <stern@rowland.harvard.edu>
CC: Minas Harutyunyan <Minas.Harutyunyan@synopsys.com>
Reported-and-tested-by: default avatarwlf <wulf@rock-chips.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8cf8ebdc
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -1649,6 +1649,18 @@ static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg)
	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;
@@ -1656,6 +1668,7 @@ static int processcompl(struct async *as, void __user * __user *arg)
	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;
@@ -1825,6 +1838,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
	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;