Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usbmon: correct length for isochronous

Usually the usbmon returns the amount of data specified in
urb->transfer_buffer_length for output submissions and urb->actual_length
for input callbacks. However, for Isochronous input transfers, this is
not enough, since the returned data buffer may contain "holes".

One easy way to fix this is to use urb->transfer_buffer_length,
but this often transfers a whole lot of unused data, so we find
how much was actually used instead.

Original patch by Márton Németh. See also kernel bug 22182.

Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Márton Németh <nm127@freemail.hu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Pete Zaitcev and committed by
Greg Kroah-Hartman
b17ea167 4f683843

+31 -3
+31 -3
drivers/usb/mon/mon_bin.c
··· 437 437 return length; 438 438 } 439 439 440 + /* 441 + * This is the look-ahead pass in case of 'C Zi', when actual_length cannot 442 + * be used to determine the length of the whole contiguous buffer. 443 + */ 444 + static unsigned int mon_bin_collate_isodesc(const struct mon_reader_bin *rp, 445 + struct urb *urb, unsigned int ndesc) 446 + { 447 + struct usb_iso_packet_descriptor *fp; 448 + unsigned int length; 449 + 450 + length = 0; 451 + fp = urb->iso_frame_desc; 452 + while (ndesc-- != 0) { 453 + if (fp->actual_length != 0) { 454 + if (fp->offset + fp->actual_length > length) 455 + length = fp->offset + fp->actual_length; 456 + } 457 + fp++; 458 + } 459 + return length; 460 + } 461 + 440 462 static void mon_bin_get_isodesc(const struct mon_reader_bin *rp, 441 463 unsigned int offset, struct urb *urb, char ev_type, unsigned int ndesc) 442 464 { ··· 501 479 /* 502 480 * Find the maximum allowable length, then allocate space. 503 481 */ 482 + urb_length = (ev_type == 'S') ? 483 + urb->transfer_buffer_length : urb->actual_length; 484 + length = urb_length; 485 + 504 486 if (usb_endpoint_xfer_isoc(epd)) { 505 487 if (urb->number_of_packets < 0) { 506 488 ndesc = 0; ··· 513 487 } else { 514 488 ndesc = urb->number_of_packets; 515 489 } 490 + if (ev_type == 'C' && usb_urb_dir_in(urb)) 491 + length = mon_bin_collate_isodesc(rp, urb, ndesc); 516 492 } else { 517 493 ndesc = 0; 518 494 } 519 495 lendesc = ndesc*sizeof(struct mon_bin_isodesc); 520 496 521 - urb_length = (ev_type == 'S') ? 522 - urb->transfer_buffer_length : urb->actual_length; 523 - length = urb_length; 497 + /* not an issue unless there's a subtle bug in a HCD somewhere */ 498 + if (length >= urb->transfer_buffer_length) 499 + length = urb->transfer_buffer_length; 524 500 525 501 if (length >= rp->b_size/5) 526 502 length = rp->b_size/5;