aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2012-05-08 12:22:49 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-05-17 13:36:57 -0400
commit1530bbc6272d9da1e39ef8e06190d42c13a02733 (patch)
tree1e9f51163f475d4f52f98428d24c794b88acfd7d /drivers/usb
parentdb2c8624847b0b67c04c3d6271823f3fee671e26 (diff)
xhci: Add new short TX quirk for Fresco Logic host.
Sergio reported that when he recorded audio from a USB headset mic plugged into the USB 3.0 port on his ASUS N53SV-DH72, the audio sounded "robotic". When plugged into the USB 2.0 port under EHCI on the same laptop, the audio sounded fine. The device is: Bus 002 Device 004: ID 046d:0a0c Logitech, Inc. Clear Chat Comfort USB Headset The problem was tracked down to the Fresco Logic xHCI host controller not correctly reporting short transfers on isochronous IN endpoints. The driver would submit a 96 byte transfer, the device would only send 88 or 90 bytes, and the xHCI host would report the transfer had a "successful" completion code, with an untransferred buffer length of 8 or 6 bytes. The successful completion code and non-zero untransferred length is a contradiction. The xHCI host is supposed to only mark a transfer as successful if all the bytes are transferred. Otherwise, the transfer should be marked with a short packet completion code. Without the EHCI bus trace, we wouldn't know whether the xHCI driver should trust the completion code or the untransferred length. With it, we know to trust the untransferred length. Add a new xHCI quirk for the Fresco Logic host controller. If a transfer is reported as successful, but the untransferred length is non-zero, print a warning. For the Fresco Logic host, change the completion code to COMP_SHORT_TX and process the transfer like a short transfer. This should be backported to stable kernels that contain the commit f5182b4155b9d686c5540a6822486400e34ddd98 "xhci: Disable MSI for some Fresco Logic hosts." That commit was marked for stable kernels as old as 2.6.36. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Reported-by: Sergio Correia <lists@uece.net> Tested-by: Sergio Correia <lists@uece.net> Cc: stable@vger.kernel.org Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci-pci.c1
-rw-r--r--drivers/usb/host/xhci-ring.c20
-rw-r--r--drivers/usb/host/xhci.h1
3 files changed, 19 insertions, 3 deletions
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 7a856a767e77..19e89216436e 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -72,6 +72,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
72 xhci_dbg(xhci, "QUIRK: Fresco Logic revision %u " 72 xhci_dbg(xhci, "QUIRK: Fresco Logic revision %u "
73 "has broken MSI implementation\n", 73 "has broken MSI implementation\n",
74 pdev->revision); 74 pdev->revision);
75 xhci->quirks |= XHCI_TRUST_TX_LENGTH;
75 } 76 }
76 77
77 if (pdev->vendor == PCI_VENDOR_ID_NEC) 78 if (pdev->vendor == PCI_VENDOR_ID_NEC)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 329fd2a98dd6..c60617bb8c2e 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1787,8 +1787,12 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
1787 /* handle completion code */ 1787 /* handle completion code */
1788 switch (trb_comp_code) { 1788 switch (trb_comp_code) {
1789 case COMP_SUCCESS: 1789 case COMP_SUCCESS:
1790 frame->status = 0; 1790 if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {
1791 break; 1791 frame->status = 0;
1792 break;
1793 }
1794 if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
1795 trb_comp_code = COMP_SHORT_TX;
1792 case COMP_SHORT_TX: 1796 case COMP_SHORT_TX:
1793 frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ? 1797 frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
1794 -EREMOTEIO : 0; 1798 -EREMOTEIO : 0;
@@ -1885,13 +1889,16 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
1885 switch (trb_comp_code) { 1889 switch (trb_comp_code) {
1886 case COMP_SUCCESS: 1890 case COMP_SUCCESS:
1887 /* Double check that the HW transferred everything. */ 1891 /* Double check that the HW transferred everything. */
1888 if (event_trb != td->last_trb) { 1892 if (event_trb != td->last_trb ||
1893 TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
1889 xhci_warn(xhci, "WARN Successful completion " 1894 xhci_warn(xhci, "WARN Successful completion "
1890 "on short TX\n"); 1895 "on short TX\n");
1891 if (td->urb->transfer_flags & URB_SHORT_NOT_OK) 1896 if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
1892 *status = -EREMOTEIO; 1897 *status = -EREMOTEIO;
1893 else 1898 else
1894 *status = 0; 1899 *status = 0;
1900 if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
1901 trb_comp_code = COMP_SHORT_TX;
1895 } else { 1902 } else {
1896 *status = 0; 1903 *status = 0;
1897 } 1904 }
@@ -2050,6 +2057,13 @@ static int handle_tx_event(struct xhci_hcd *xhci,
2050 * transfer type 2057 * transfer type
2051 */ 2058 */
2052 case COMP_SUCCESS: 2059 case COMP_SUCCESS:
2060 if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
2061 break;
2062 if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
2063 trb_comp_code = COMP_SHORT_TX;
2064 else
2065 xhci_warn(xhci, "WARN Successful completion on short TX: "
2066 "needs XHCI_TRUST_TX_LENGTH quirk?\n");
2053 case COMP_SHORT_TX: 2067 case COMP_SHORT_TX:
2054 break; 2068 break;
2055 case COMP_STOP: 2069 case COMP_STOP:
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index ce1edd7246aa..ac142760fd3b 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1481,6 +1481,7 @@ struct xhci_hcd {
1481#define XHCI_RESET_ON_RESUME (1 << 7) 1481#define XHCI_RESET_ON_RESUME (1 << 7)
1482#define XHCI_SW_BW_CHECKING (1 << 8) 1482#define XHCI_SW_BW_CHECKING (1 << 8)
1483#define XHCI_AMD_0x96_HOST (1 << 9) 1483#define XHCI_AMD_0x96_HOST (1 << 9)
1484#define XHCI_TRUST_TX_LENGTH (1 << 10)
1484 unsigned int num_active_eps; 1485 unsigned int num_active_eps;
1485 unsigned int limit_active_eps; 1486 unsigned int limit_active_eps;
1486 /* There are two roothubs to keep track of bus suspend info for */ 1487 /* There are two roothubs to keep track of bus suspend info for */