aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2013-01-11 16:36:35 -0500
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2013-01-24 12:53:37 -0500
commitf18f8ed2a9adc41c2d9294b85b6af115829d2af1 (patch)
treed454da8e14353c853ce0d8a79280edd8951a7c0d /drivers/usb
parent760973d2a74b93eb1697981f7448f0e62767cfc4 (diff)
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 4da6e6f247a2601ab9f1e63424e4d944ed4124f3 "xhci 1.0: Update TD size field format." It may not apply well to kernels older than 3.2 because of commit 29cc88979a8818cd8c5019426e945aed118b400e "USB: use usb_endpoint_maxp() instead of le16_to_cpu()". Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci-ring.c5
1 files changed, 3 insertions, 2 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index d1ff13370d33..80ef717ec5b8 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3108,7 +3108,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
3108 * running_total. 3108 * running_total.
3109 */ 3109 */
3110 packets_transferred = (running_total + trb_buff_len) / 3110 packets_transferred = (running_total + trb_buff_len) /
3111 usb_endpoint_maxp(&urb->ep->desc); 3111 GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
3112 3112
3113 if ((total_packet_count - packets_transferred) > 31) 3113 if ((total_packet_count - packets_transferred) > 31)
3114 return 31 << 17; 3114 return 31 << 17;
@@ -3642,7 +3642,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
3642 td_len = urb->iso_frame_desc[i].length; 3642 td_len = urb->iso_frame_desc[i].length;
3643 td_remain_len = td_len; 3643 td_remain_len = td_len;
3644 total_packet_count = DIV_ROUND_UP(td_len, 3644 total_packet_count = DIV_ROUND_UP(td_len,
3645 usb_endpoint_maxp(&urb->ep->desc)); 3645 GET_MAX_PACKET(
3646 usb_endpoint_maxp(&urb->ep->desc)));
3646 /* A zero-length transfer still involves at least one packet. */ 3647 /* A zero-length transfer still involves at least one packet. */
3647 if (total_packet_count == 0) 3648 if (total_packet_count == 0)
3648 total_packet_count++; 3649 total_packet_count++;