aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/patches-3.10/140-revert_usb_unbind_interfaces.patch
blob: db1bc1ae19947e17a3d31ea49b1ae06f484cd2dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
commit 032f44791457d9aa50c6a194a2d475f07e311afd
Author: Felix Fietkau <nbd@openwrt.org>
Date:   Wed Jul 9 12:08:23 2014 +0200

    Revert "USB: unbind all interfaces before rebinding any"
    
    This reverts commit 59f0103d74e4a32cbaa054d5011ea287fcfb83e4.
    The commit has been found to cause USB regressions on AR933x and
    BCM4705.

--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -953,7 +953,8 @@ EXPORT_SYMBOL_GPL(usb_deregister);
  * it doesn't support pre_reset/post_reset/reset_resume or
  * because it doesn't support suspend/resume.
  *
- * The caller must hold @intf's device's lock, but not @intf's lock.
+ * The caller must hold @intf's device's lock, but not its pm_mutex
+ * and not @intf->dev.sem.
  */
 void usb_forced_unbind_intf(struct usb_interface *intf)
 {
@@ -966,37 +967,16 @@ void usb_forced_unbind_intf(struct usb_i
 	intf->needs_binding = 1;
 }
 
-/*
- * Unbind drivers for @udev's marked interfaces.  These interfaces have
- * the needs_binding flag set, for example by usb_resume_interface().
- *
- * The caller must hold @udev's device lock.
- */
-static void unbind_marked_interfaces(struct usb_device *udev)
-{
-	struct usb_host_config	*config;
-	int			i;
-	struct usb_interface	*intf;
-
-	config = udev->actconfig;
-	if (config) {
-		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
-			intf = config->interface[i];
-			if (intf->dev.driver && intf->needs_binding)
-				usb_forced_unbind_intf(intf);
-		}
-	}
-}
-
 /* Delayed forced unbinding of a USB interface driver and scan
  * for rebinding.
  *
- * The caller must hold @intf's device's lock, but not @intf's lock.
+ * The caller must hold @intf's device's lock, but not its pm_mutex
+ * and not @intf->dev.sem.
  *
  * Note: Rebinds will be skipped if a system sleep transition is in
  * progress and the PM "complete" callback hasn't occurred yet.
  */
-static void usb_rebind_intf(struct usb_interface *intf)
+void usb_rebind_intf(struct usb_interface *intf)
 {
 	int rc;
 
@@ -1013,66 +993,68 @@ static void usb_rebind_intf(struct usb_i
 	}
 }
 
-/*
- * Rebind drivers to @udev's marked interfaces.  These interfaces have
- * the needs_binding flag set.
+#ifdef CONFIG_PM
+
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+ * There is no check for reset_resume here because it can be determined
+ * only during resume whether reset_resume is needed.
  *
  * The caller must hold @udev's device lock.
  */
-static void rebind_marked_interfaces(struct usb_device *udev)
+static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
 {
 	struct usb_host_config	*config;
 	int			i;
 	struct usb_interface	*intf;
+	struct usb_driver	*drv;
 
 	config = udev->actconfig;
 	if (config) {
 		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
 			intf = config->interface[i];
-			if (intf->needs_binding)
-				usb_rebind_intf(intf);
+
+			if (intf->dev.driver) {
+				drv = to_usb_driver(intf->dev.driver);
+				if (!drv->suspend || !drv->resume)
+					usb_forced_unbind_intf(intf);
+			}
 		}
 	}
 }
 
-/*
- * Unbind all of @udev's marked interfaces and then rebind all of them.
- * This ordering is necessary because some drivers claim several interfaces
- * when they are first probed.
+/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
+ * These interfaces have the needs_binding flag set by usb_resume_interface().
  *
  * The caller must hold @udev's device lock.
  */
-void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
+static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
 {
-	unbind_marked_interfaces(udev);
-	rebind_marked_interfaces(udev);
-}
+	struct usb_host_config	*config;
+	int			i;
+	struct usb_interface	*intf;
 
-#ifdef CONFIG_PM
+	config = udev->actconfig;
+	if (config) {
+		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+			intf = config->interface[i];
+			if (intf->dev.driver && intf->needs_binding)
+				usb_forced_unbind_intf(intf);
+		}
+	}
+}
 
-/* Unbind drivers for @udev's interfaces that don't support suspend/resume
- * There is no check for reset_resume here because it can be determined
- * only during resume whether reset_resume is needed.
- *
- * The caller must hold @udev's device lock.
- */
-static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
+static void do_rebind_interfaces(struct usb_device *udev)
 {
 	struct usb_host_config	*config;
 	int			i;
 	struct usb_interface	*intf;
-	struct usb_driver	*drv;
 
 	config = udev->actconfig;
 	if (config) {
 		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
 			intf = config->interface[i];
-
-			if (intf->dev.driver) {
-				drv = to_usb_driver(intf->dev.driver);
-				if (!drv->suspend || !drv->resume)
-					usb_forced_unbind_intf(intf);
-			}
+			if (intf->needs_binding)
+				usb_rebind_intf(intf);
 		}
 	}
 }
@@ -1397,7 +1379,7 @@ int usb_resume_complete(struct device *d
 	 * whose needs_binding flag is set
 	 */
 	if (udev->state != USB_STATE_NOTATTACHED)
-		rebind_marked_interfaces(udev);
+		do_rebind_interfaces(udev);
 	return 0;
 }
 
@@ -1419,7 +1401,7 @@ int usb_resume(struct device *dev, pm_me
 		pm_runtime_disable(dev);
 		pm_runtime_set_active(dev);
 		pm_runtime_enable(dev);
-		unbind_marked_interfaces(udev);
+		unbind_no_reset_resume_drivers_interfaces(udev);
 	}
 
 	/* Avoid PM error messages for devices disconnected while suspended
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -5263,11 +5263,10 @@ int usb_reset_device(struct usb_device *
 				else if (cintf->condition ==
 						USB_INTERFACE_BOUND)
 					rebind = 1;
-				if (rebind)
-					cintf->needs_binding = 1;
 			}
+			if (ret == 0 && rebind)
+				usb_rebind_intf(cintf);
 		}
-		usb_unbind_and_rebind_marked_interfaces(udev);
 	}
 
 	usb_autosuspend_device(udev);
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct
 extern int usb_match_device(struct usb_device *dev,
 			    const struct usb_device_id *id);
 extern void usb_forced_unbind_intf(struct usb_interface *intf);
-extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
+extern void usb_rebind_intf(struct usb_interface *intf);
 
 extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
 		struct dev_state *owner);