diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2009-05-27 18:21:56 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:46 -0400 |
commit | b18ffd49e86102a9ed0a1cc83fdafe3891e844e5 (patch) | |
tree | 78b60211d13839baaa92128e1b583dc12139392c /drivers/usb/host | |
parent | 5effabbe9e6e0089f7afdde35cb51e8c8b4cf6bc (diff) |
USB: EHCI: update toggle state for linked QHs
This patch (as1245) fixes a bug in ehci-hcd. When an URB is queued
for an endpoint whose QH is already in the LINKED state, the QH
doesn't get refreshed. As a result, if usb_clear_halt() was called
during the time that the QH was linked but idle, the data toggle value
in the QH doesn't get reset.
The symptom is that after a clear_halt, data gets lost and transfers
time out. This problem is starting to show up now because the
"ehci-hcd unlink speedups" patch causes QHs with no queued URBs to
remain linked for a suitable time.
The patch utilizes the new endpoint_reset mechanism to fix the
problem. When an endpoint is reset, the new method forcibly unlinks
the QH (if necessary) and safely updates the toggle value. This
allows qh_update() to be simplified and avoids using usb_device's
toggle bits in a rather unintuitive way.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: David Brownell <david-b@pacbell.net>
Tested-by: David <david@unsolicited.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-au1xxx.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-fsl.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 45 | ||||
-rw-r--r-- | drivers/usb/host/ehci-ixp4xx.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-orion.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-ppc-of.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-ps3.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 19 |
9 files changed, 53 insertions, 18 deletions
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index bf69f4739107..c3a778bd359c 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c | |||
@@ -97,6 +97,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = { | |||
97 | .urb_enqueue = ehci_urb_enqueue, | 97 | .urb_enqueue = ehci_urb_enqueue, |
98 | .urb_dequeue = ehci_urb_dequeue, | 98 | .urb_dequeue = ehci_urb_dequeue, |
99 | .endpoint_disable = ehci_endpoint_disable, | 99 | .endpoint_disable = ehci_endpoint_disable, |
100 | .endpoint_reset = ehci_endpoint_reset, | ||
100 | 101 | ||
101 | /* | 102 | /* |
102 | * scheduling support | 103 | * scheduling support |
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 01c3da34f678..bf86809c5120 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c | |||
@@ -309,6 +309,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { | |||
309 | .urb_enqueue = ehci_urb_enqueue, | 309 | .urb_enqueue = ehci_urb_enqueue, |
310 | .urb_dequeue = ehci_urb_dequeue, | 310 | .urb_dequeue = ehci_urb_dequeue, |
311 | .endpoint_disable = ehci_endpoint_disable, | 311 | .endpoint_disable = ehci_endpoint_disable, |
312 | .endpoint_reset = ehci_endpoint_reset, | ||
312 | 313 | ||
313 | /* | 314 | /* |
314 | * scheduling support | 315 | * scheduling support |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index a2ca9cbf2809..2b72473544d3 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -1024,6 +1024,51 @@ done: | |||
1024 | return; | 1024 | return; |
1025 | } | 1025 | } |
1026 | 1026 | ||
1027 | static void | ||
1028 | ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) | ||
1029 | { | ||
1030 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
1031 | struct ehci_qh *qh; | ||
1032 | int eptype = usb_endpoint_type(&ep->desc); | ||
1033 | |||
1034 | if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) | ||
1035 | return; | ||
1036 | |||
1037 | rescan: | ||
1038 | spin_lock_irq(&ehci->lock); | ||
1039 | qh = ep->hcpriv; | ||
1040 | |||
1041 | /* For Bulk and Interrupt endpoints we maintain the toggle state | ||
1042 | * in the hardware; the toggle bits in udev aren't used at all. | ||
1043 | * When an endpoint is reset by usb_clear_halt() we must reset | ||
1044 | * the toggle bit in the QH. | ||
1045 | */ | ||
1046 | if (qh) { | ||
1047 | if (!list_empty(&qh->qtd_list)) { | ||
1048 | WARN_ONCE(1, "clear_halt for a busy endpoint\n"); | ||
1049 | } else if (qh->qh_state == QH_STATE_IDLE) { | ||
1050 | qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); | ||
1051 | } else { | ||
1052 | /* It's not safe to write into the overlay area | ||
1053 | * while the QH is active. Unlink it first and | ||
1054 | * wait for the unlink to complete. | ||
1055 | */ | ||
1056 | if (qh->qh_state == QH_STATE_LINKED) { | ||
1057 | if (eptype == USB_ENDPOINT_XFER_BULK) { | ||
1058 | unlink_async(ehci, qh); | ||
1059 | } else { | ||
1060 | intr_deschedule(ehci, qh); | ||
1061 | (void) qh_schedule(ehci, qh); | ||
1062 | } | ||
1063 | } | ||
1064 | spin_unlock_irq(&ehci->lock); | ||
1065 | schedule_timeout_uninterruptible(1); | ||
1066 | goto rescan; | ||
1067 | } | ||
1068 | } | ||
1069 | spin_unlock_irq(&ehci->lock); | ||
1070 | } | ||
1071 | |||
1027 | static int ehci_get_frame (struct usb_hcd *hcd) | 1072 | static int ehci_get_frame (struct usb_hcd *hcd) |
1028 | { | 1073 | { |
1029 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 1074 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index 9c32063a0c2f..a44bb4a94954 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c | |||
@@ -51,6 +51,7 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = { | |||
51 | .urb_enqueue = ehci_urb_enqueue, | 51 | .urb_enqueue = ehci_urb_enqueue, |
52 | .urb_dequeue = ehci_urb_dequeue, | 52 | .urb_dequeue = ehci_urb_dequeue, |
53 | .endpoint_disable = ehci_endpoint_disable, | 53 | .endpoint_disable = ehci_endpoint_disable, |
54 | .endpoint_reset = ehci_endpoint_reset, | ||
54 | .get_frame_number = ehci_get_frame, | 55 | .get_frame_number = ehci_get_frame, |
55 | .hub_status_data = ehci_hub_status_data, | 56 | .hub_status_data = ehci_hub_status_data, |
56 | .hub_control = ehci_hub_control, | 57 | .hub_control = ehci_hub_control, |
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 17dc15407a07..770dd9aba62a 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c | |||
@@ -149,6 +149,7 @@ static const struct hc_driver ehci_orion_hc_driver = { | |||
149 | .urb_enqueue = ehci_urb_enqueue, | 149 | .urb_enqueue = ehci_urb_enqueue, |
150 | .urb_dequeue = ehci_urb_dequeue, | 150 | .urb_dequeue = ehci_urb_dequeue, |
151 | .endpoint_disable = ehci_endpoint_disable, | 151 | .endpoint_disable = ehci_endpoint_disable, |
152 | .endpoint_reset = ehci_endpoint_reset, | ||
152 | 153 | ||
153 | /* | 154 | /* |
154 | * scheduling support | 155 | * scheduling support |
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index e74948898f76..f3683e1da161 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
@@ -388,6 +388,7 @@ static const struct hc_driver ehci_pci_hc_driver = { | |||
388 | .urb_enqueue = ehci_urb_enqueue, | 388 | .urb_enqueue = ehci_urb_enqueue, |
389 | .urb_dequeue = ehci_urb_dequeue, | 389 | .urb_dequeue = ehci_urb_dequeue, |
390 | .endpoint_disable = ehci_endpoint_disable, | 390 | .endpoint_disable = ehci_endpoint_disable, |
391 | .endpoint_reset = ehci_endpoint_reset, | ||
391 | 392 | ||
392 | /* | 393 | /* |
393 | * scheduling support | 394 | * scheduling support |
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index ef732b704f53..fbd272288fc2 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c | |||
@@ -61,6 +61,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = { | |||
61 | .urb_enqueue = ehci_urb_enqueue, | 61 | .urb_enqueue = ehci_urb_enqueue, |
62 | .urb_dequeue = ehci_urb_dequeue, | 62 | .urb_dequeue = ehci_urb_dequeue, |
63 | .endpoint_disable = ehci_endpoint_disable, | 63 | .endpoint_disable = ehci_endpoint_disable, |
64 | .endpoint_reset = ehci_endpoint_reset, | ||
64 | 65 | ||
65 | /* | 66 | /* |
66 | * scheduling support | 67 | * scheduling support |
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 1ba9f9a8c308..4b4df23efddf 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c | |||
@@ -65,6 +65,7 @@ static const struct hc_driver ps3_ehci_hc_driver = { | |||
65 | .urb_enqueue = ehci_urb_enqueue, | 65 | .urb_enqueue = ehci_urb_enqueue, |
66 | .urb_dequeue = ehci_urb_dequeue, | 66 | .urb_dequeue = ehci_urb_dequeue, |
67 | .endpoint_disable = ehci_endpoint_disable, | 67 | .endpoint_disable = ehci_endpoint_disable, |
68 | .endpoint_reset = ehci_endpoint_reset, | ||
68 | .get_frame_number = ehci_get_frame, | 69 | .get_frame_number = ehci_get_frame, |
69 | .hub_status_data = ehci_hub_status_data, | 70 | .hub_status_data = ehci_hub_status_data, |
70 | .hub_control = ehci_hub_control, | 71 | .hub_control = ehci_hub_control, |
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 1976b1b3778c..3192f683f807 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
@@ -93,22 +93,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) | |||
93 | qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); | 93 | qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); |
94 | qh->hw_alt_next = EHCI_LIST_END(ehci); | 94 | qh->hw_alt_next = EHCI_LIST_END(ehci); |
95 | 95 | ||
96 | /* Except for control endpoints, we make hardware maintain data | ||
97 | * toggle (like OHCI) ... here (re)initialize the toggle in the QH, | ||
98 | * and set the pseudo-toggle in udev. Only usb_clear_halt() will | ||
99 | * ever clear it. | ||
100 | */ | ||
101 | if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { | ||
102 | unsigned is_out, epnum; | ||
103 | |||
104 | is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8)); | ||
105 | epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f; | ||
106 | if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { | ||
107 | qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); | ||
108 | usb_settoggle (qh->dev, epnum, is_out, 1); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ | 96 | /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ |
113 | wmb (); | 97 | wmb (); |
114 | qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); | 98 | qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); |
@@ -850,7 +834,6 @@ done: | |||
850 | qh->qh_state = QH_STATE_IDLE; | 834 | qh->qh_state = QH_STATE_IDLE; |
851 | qh->hw_info1 = cpu_to_hc32(ehci, info1); | 835 | qh->hw_info1 = cpu_to_hc32(ehci, info1); |
852 | qh->hw_info2 = cpu_to_hc32(ehci, info2); | 836 | qh->hw_info2 = cpu_to_hc32(ehci, info2); |
853 | usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); | ||
854 | qh_refresh (ehci, qh); | 837 | qh_refresh (ehci, qh); |
855 | return qh; | 838 | return qh; |
856 | } | 839 | } |
@@ -881,7 +864,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
881 | } | 864 | } |
882 | } | 865 | } |
883 | 866 | ||
884 | /* clear halt and/or toggle; and maybe recover from silicon quirk */ | 867 | /* clear halt and maybe recover from silicon quirk */ |
885 | if (qh->qh_state == QH_STATE_IDLE) | 868 | if (qh->qh_state == QH_STATE_IDLE) |
886 | qh_refresh (ehci, qh); | 869 | qh_refresh (ehci, qh); |
887 | 870 | ||