aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2011-08-12 13:23:01 -0400
committerHerton Ronaldo Krzesinski <herton.krzesinski@canonical.com>2011-10-17 13:32:28 -0400
commit01e0b73fc9beabb699c77434e9a3bc26fcd79728 (patch)
treebf5b448b350496a0fbbf5d22005cd792f6cd7606 /drivers/usb
parent394415603cdca33c4c1a3f872759c9c6bbc5d266 (diff)
xhci: Handle zero-length isochronous packets.
BugLink: http://bugs.launchpad.net/bugs/868628 commit 48df4a6fd8c40c0bbcbca2044f5f2bc75dcf6db1 upstream. For a long time, the xHCI driver has had this note: /* FIXME: Ignoring zero-length packets, can those happen? */ It turns out that, yes, there are drivers that need to queue zero-length transfers for isochronous OUT transfers. Without this patch, users will see kernel hang messages when a driver attempts to enqueue an isochronous URB with a zero length transfer (because count_isoc_trbs_needed will return zero for that TD, xhci_td->last_trb will never be set, and updating the dequeue pointer will cause an infinite loop). Matěj ran into this issue when using an NI Audio4DJ USB soundcard with the snd-usb-caiaq driver. See https://bugzilla.kernel.org/show_bug.cgi?id=40702 Fix count_isoc_trbs_needed() to return 1 for zero-length transfers (thanks Alan on the math help). Update the various TRB field calculations to deal with zero-length transfers. We're still transferring one packet with a zero-length data payload, so the total_packet_count should be 1. The Transfer Burst Count (TBC) and Transfer Last Burst Packet Count (TLBPC) fields should be set to zero. This patch should be backported to kernels as old as 2.6.36. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Tested-by: Matěj Laitl <matej@laitl.cz> Cc: Daniel Mack <zonque@gmail.com> Cc: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci-ring.c21
1 files changed, 11 insertions, 10 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 58b5579551e..d0871ea687d 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2692,6 +2692,10 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
2692{ 2692{
2693 int packets_transferred; 2693 int packets_transferred;
2694 2694
2695 /* One TRB with a zero-length data packet. */
2696 if (running_total == 0 && trb_buff_len == 0)
2697 return 0;
2698
2695 /* All the TRB queueing functions don't count the current TRB in 2699 /* All the TRB queueing functions don't count the current TRB in
2696 * running_total. 2700 * running_total.
2697 */ 2701 */
@@ -3133,20 +3137,15 @@ static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
3133 struct urb *urb, int i) 3137 struct urb *urb, int i)
3134{ 3138{
3135 int num_trbs = 0; 3139 int num_trbs = 0;
3136 u64 addr, td_len, running_total; 3140 u64 addr, td_len;
3137 3141
3138 addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset); 3142 addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
3139 td_len = urb->iso_frame_desc[i].length; 3143 td_len = urb->iso_frame_desc[i].length;
3140 3144
3141 running_total = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1)); 3145 num_trbs = DIV_ROUND_UP(td_len + (addr & (TRB_MAX_BUFF_SIZE - 1)),
3142 running_total &= TRB_MAX_BUFF_SIZE - 1; 3146 TRB_MAX_BUFF_SIZE);
3143 if (running_total != 0) 3147 if (num_trbs == 0)
3144 num_trbs++;
3145
3146 while (running_total < td_len) {
3147 num_trbs++; 3148 num_trbs++;
3148 running_total += TRB_MAX_BUFF_SIZE;
3149 }
3150 3149
3151 return num_trbs; 3150 return num_trbs;
3152} 3151}
@@ -3258,9 +3257,11 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
3258 addr = start_addr + urb->iso_frame_desc[i].offset; 3257 addr = start_addr + urb->iso_frame_desc[i].offset;
3259 td_len = urb->iso_frame_desc[i].length; 3258 td_len = urb->iso_frame_desc[i].length;
3260 td_remain_len = td_len; 3259 td_remain_len = td_len;
3261 /* FIXME: Ignoring zero-length packets, can those happen? */
3262 total_packet_count = roundup(td_len, 3260 total_packet_count = roundup(td_len,
3263 le16_to_cpu(urb->ep->desc.wMaxPacketSize)); 3261 le16_to_cpu(urb->ep->desc.wMaxPacketSize));
3262 /* A zero-length transfer still involves at least one packet. */
3263 if (total_packet_count == 0)
3264 total_packet_count++;
3264 burst_count = xhci_get_burst_count(xhci, urb->dev, urb, 3265 burst_count = xhci_get_burst_count(xhci, urb->dev, urb,
3265 total_packet_count); 3266 total_packet_count);
3266 residue = xhci_get_last_burst_packet_count(xhci, 3267 residue = xhci_get_last_burst_packet_count(xhci,