Commit f18f8ed2 authored by Sarah Sharp's avatar Sarah Sharp

xhci: Fix TD size for isochronous URBs.

To calculate the TD size for a particular TRB in an isoc TD, we need
know the endpoint's max packet size.  Isochronous endpoints also encode
the number of additional service opportunities in their wMaxPacketSize
field.  The TD size calculation did not mask off those bits before using
the field.  This resulted in incorrect TD size information for
isochronous TRBs when an URB frame buffer crossed a 64KB boundary.

For example:
 - an isoc endpoint has 2 additional service opportunites and
   a max packet size of 1020 bytes
 - a frame transfer buffer contains 3060 bytes
 - one frame buffer crosses a 64KB boundary, and must be split into
   one 1276 byte TRB, and one 1784 byte TRB.

The TD size is is the number of packets that remain to be transferred
for a TD after processing all the max packet sized packets in the
current TRB and all previous TRBs.

For this TD, the number of packets to be transferred is (3060 / 1020),
or 3.  The first TRB contains 1276 bytes, which means it contains one
full packet, and a 256 byte remainder.  After processing all the max
packet-sized packets in the first TRB, the host will have 2 packets left
to transfer.

The old code would calculate the TD size for the first TRB as:

total packet count = DIV_ROUND_UP (TD length / endpoint wMaxPacketSize)
total packet count - (first TRB length / endpoint wMaxPacketSize)

The math should have been:

total packet count = DIV_ROUND_UP (3060 / 1020) = 3
3 - (1276 / 1020) = 2

Since the old code didn't mask off the additional service interval bits
from the wMaxPacketSize field, the math ended up as

total packet count = DIV_ROUND_UP (3060 / 5116) = 1
1 - (1276 / 5116) = 1

Fix this by masking off the number of additional service opportunities
in the wMaxPacketSize field.

This patch should be backported to stable kernels as old as 3.0, that
contain the commit 4da6e6f2 "xhci 1.0:
Update TD size field format."  It may not apply well to kernels older
than 3.2 because of commit 29cc8897
"USB: use usb_endpoint_maxp() instead of le16_to_cpu()".
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: stable@vger.kernel.org
parent 760973d2
...@@ -3108,7 +3108,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, ...@@ -3108,7 +3108,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
* running_total. * running_total.
*/ */
packets_transferred = (running_total + trb_buff_len) / packets_transferred = (running_total + trb_buff_len) /
usb_endpoint_maxp(&urb->ep->desc); GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
if ((total_packet_count - packets_transferred) > 31) if ((total_packet_count - packets_transferred) > 31)
return 31 << 17; return 31 << 17;
...@@ -3642,7 +3642,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ...@@ -3642,7 +3642,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
td_len = urb->iso_frame_desc[i].length; td_len = urb->iso_frame_desc[i].length;
td_remain_len = td_len; td_remain_len = td_len;
total_packet_count = DIV_ROUND_UP(td_len, total_packet_count = DIV_ROUND_UP(td_len,
usb_endpoint_maxp(&urb->ep->desc)); GET_MAX_PACKET(
usb_endpoint_maxp(&urb->ep->desc)));
/* A zero-length transfer still involves at least one packet. */ /* A zero-length transfer still involves at least one packet. */
if (total_packet_count == 0) if (total_packet_count == 0)
total_packet_count++; total_packet_count++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment