USB: usbfs: compute urb->actual_length for isochronous
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> CC: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
d656fa32b1
commit
2ef47001b3
@@ -1820,6 +1820,18 @@ static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg)
|
|||||||
return 0;
|
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)
|
static int processcompl(struct async *as, void __user * __user *arg)
|
||||||
{
|
{
|
||||||
struct urb *urb = as->urb;
|
struct urb *urb = as->urb;
|
||||||
@@ -1827,6 +1839,7 @@ static int processcompl(struct async *as, void __user * __user *arg)
|
|||||||
void __user *addr = as->userurb;
|
void __user *addr = as->userurb;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
compute_isochronous_actual_length(urb);
|
||||||
if (as->userbuffer && urb->actual_length) {
|
if (as->userbuffer && urb->actual_length) {
|
||||||
if (copy_urb_data_to_user(as->userbuffer, urb))
|
if (copy_urb_data_to_user(as->userbuffer, urb))
|
||||||
goto err_out;
|
goto err_out;
|
||||||
@@ -1995,6 +2008,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
|
|||||||
void __user *addr = as->userurb;
|
void __user *addr = as->userurb;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
compute_isochronous_actual_length(urb);
|
||||||
if (as->userbuffer && urb->actual_length) {
|
if (as->userbuffer && urb->actual_length) {
|
||||||
if (copy_urb_data_to_user(as->userbuffer, urb))
|
if (copy_urb_data_to_user(as->userbuffer, urb))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|||||||
Reference in New Issue
Block a user