aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2011-06-15 22:57:46 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-06-17 14:28:20 -0400
commitb3df3f9c7df9a8d85e03e158d35487618a160901 (patch)
tree5ecd3c7b9cf576bb1bef357db98075d183f85f1c /drivers/usb/host
parentc877b3b2ad5cb9d4fe523c5496185cc328ff3ae9 (diff)
xhci: Always set urb->status to zero for isoc endpoints.
When the xHCI driver encounters a Missed Service Interval event for an isochronous endpoint ring, it means the host controller skipped over one or more isochronous TDs. For TD that is skipped, skip_isoc_td() is called. This sets the frame descriptor status to -EXDEV, and also sets the value stored in the int pointed to by status to -EXDEV. If the isochronous TD happens to be the last TD in an URB, handle_tx_event() will use the status variable to give back the URB to the USB core. That means drivers will see urb->status as -EXDEV. It turns out that EHCI, UHCI, and OHCI always set urb->status to zero for an isochronous urb, regardless of what the frame status is. See itd_complete() in ehci-sched.c: } else { /* URB was too late */ desc->status = -EXDEV; } } /* handle completion now? */ if (likely ((urb_index + 1) != urb->number_of_packets)) goto done; /* ASSERT: it's really the last itd for this urb list_for_each_entry (itd, &stream->td_list, itd_list) BUG_ON (itd->urb == urb); */ /* give urb back to the driver; completion often (re)submits */ dev = urb->dev; ehci_urb_done(ehci, urb, 0); ehci_urb_done() completes the URB with the status of the third argument, which is always zero in this case. It turns out that many USB webcam drivers, such as uvcvideo, cannot handle urb->status set to a non-zero value. They will not resubmit their isochronous URBs in that case, and userspace will see a frozen video. Change the xHCI driver to be consistent with the EHCI and UHCI driver, and always set urb->status to 0 for isochronous URBs. This patch should be backported to kernels as old as 2.6.36 Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: "Xu, Andiry" <Andiry.Xu@amd.com> Cc: stable@kernel.org
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/xhci-ring.c11
1 files changed, 6 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 436332aa341b..70cacbbe7fb9 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1768,9 +1768,6 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
1768 } 1768 }
1769 } 1769 }
1770 1770
1771 if ((idx == urb_priv->length - 1) && *status == -EINPROGRESS)
1772 *status = 0;
1773
1774 return finish_td(xhci, td, event_trb, event, ep, status, false); 1771 return finish_td(xhci, td, event_trb, event, ep, status, false);
1775} 1772}
1776 1773
@@ -1788,8 +1785,7 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
1788 idx = urb_priv->td_cnt; 1785 idx = urb_priv->td_cnt;
1789 frame = &td->urb->iso_frame_desc[idx]; 1786 frame = &td->urb->iso_frame_desc[idx];
1790 1787
1791 /* The transfer is partly done */ 1788 /* The transfer is partly done. */
1792 *status = -EXDEV;
1793 frame->status = -EXDEV; 1789 frame->status = -EXDEV;
1794 1790
1795 /* calc actual length */ 1791 /* calc actual length */
@@ -2177,6 +2173,11 @@ cleanup:
2177 urb->transfer_buffer_length, 2173 urb->transfer_buffer_length,
2178 status); 2174 status);
2179 spin_unlock(&xhci->lock); 2175 spin_unlock(&xhci->lock);
2176 /* EHCI, UHCI, and OHCI always unconditionally set the
2177 * urb->status of an isochronous endpoint to 0.
2178 */
2179 if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
2180 status = 0;
2180 usb_hcd_giveback_urb(bus_to_hcd(urb->dev->bus), urb, status); 2181 usb_hcd_giveback_urb(bus_to_hcd(urb->dev->bus), urb, status);
2181 spin_lock(&xhci->lock); 2182 spin_lock(&xhci->lock);
2182 } 2183 }