]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/4.4.166/input-xpad-handle-present-and-gone-correctly.patch
drop queue-4.14/mips-make-sure-dt-memory-regions-are-valid.patch
[thirdparty/kernel/stable-queue.git] / releases / 4.4.166 / input-xpad-handle-present-and-gone-correctly.patch
CommitLineData
156d8e2c
SL
1From 0f42c57afcb17eb28d45ee6d3eb91e4407716020 Mon Sep 17 00:00:00 2001
2From: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
3Date: Wed, 9 Dec 2015 11:46:25 -0800
4Subject: Input: xpad - handle "present" and "gone" correctly
5
6[ Upstream commit 09c8b00ae3e16c8d0fd4beb2ca064502a76c0f17 ]
7
8Handle the "a new device is present" message properly by dynamically
9creating the input device at this point in time. This means we now do not
10"preallocate" all 4 devices when a single wireless base station is seen.
11This requires a workqueue as we are in interrupt context when we learn
12about this.
13
14Also properly disconnect any devices that we are told are removed.
15
16Signed-off-by: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
17Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
18Signed-off-by: Pavel Rojtberg <rojtberg@gmail.com>
19Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
20Signed-off-by: Sasha Levin <sashal@kernel.org>
21---
22 drivers/input/joystick/xpad.c | 107 ++++++++++++++++++++++------------
23 1 file changed, 69 insertions(+), 38 deletions(-)
24
25diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
26index 2f16d07db8ef..88de7b17bf7d 100644
27--- a/drivers/input/joystick/xpad.c
28+++ b/drivers/input/joystick/xpad.c
29@@ -76,6 +76,8 @@
30 */
31
32 #include <linux/kernel.h>
33+#include <linux/input.h>
34+#include <linux/rcupdate.h>
35 #include <linux/slab.h>
36 #include <linux/stat.h>
37 #include <linux/module.h>
38@@ -334,10 +336,12 @@ struct xpad_output_packet {
39
40 struct usb_xpad {
41 struct input_dev *dev; /* input device interface */
42+ struct input_dev __rcu *x360w_dev;
43 struct usb_device *udev; /* usb device */
44 struct usb_interface *intf; /* usb interface */
45
46- int pad_present;
47+ bool pad_present;
48+ bool input_created;
49
50 struct urb *irq_in; /* urb for interrupt in report */
51 unsigned char *idata; /* input data */
52@@ -362,8 +366,12 @@ struct usb_xpad {
53 int xtype; /* type of xbox device */
54 int pad_nr; /* the order x360 pads were attached */
55 const char *name; /* name of the device */
56+ struct work_struct work; /* init/remove device from callback */
57 };
58
59+static int xpad_init_input(struct usb_xpad *xpad);
60+static void xpad_deinit_input(struct usb_xpad *xpad);
61+
62 /*
63 * xpad_process_packet
64 *
65@@ -443,11 +451,9 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
66 * http://www.free60.org/wiki/Gamepad
67 */
68
69-static void xpad360_process_packet(struct usb_xpad *xpad,
70+static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
71 u16 cmd, unsigned char *data)
72 {
73- struct input_dev *dev = xpad->dev;
74-
75 /* digital pad */
76 if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
77 /* dpad as buttons (left, right, up, down) */
78@@ -514,7 +520,30 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
79 input_sync(dev);
80 }
81
82-static void xpad_identify_controller(struct usb_xpad *xpad);
83+static void xpad_presence_work(struct work_struct *work)
84+{
85+ struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
86+ int error;
87+
88+ if (xpad->pad_present) {
89+ error = xpad_init_input(xpad);
90+ if (error) {
91+ /* complain only, not much else we can do here */
92+ dev_err(&xpad->dev->dev,
93+ "unable to init device: %d\n", error);
94+ } else {
95+ rcu_assign_pointer(xpad->x360w_dev, xpad->dev);
96+ }
97+ } else {
98+ RCU_INIT_POINTER(xpad->x360w_dev, NULL);
99+ synchronize_rcu();
100+ /*
101+ * Now that we are sure xpad360w_process_packet is not
102+ * using input device we can get rid of it.
103+ */
104+ xpad_deinit_input(xpad);
105+ }
106+}
107
108 /*
109 * xpad360w_process_packet
110@@ -532,24 +561,28 @@ static void xpad_identify_controller(struct usb_xpad *xpad);
111 */
112 static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
113 {
114+ struct input_dev *dev;
115+ bool present;
116+
117 /* Presence change */
118 if (data[0] & 0x08) {
119- if (data[1] & 0x80) {
120- xpad->pad_present = 1;
121- /*
122- * Light up the segment corresponding to
123- * controller number.
124- */
125- xpad_identify_controller(xpad);
126- } else
127- xpad->pad_present = 0;
128+ present = (data[1] & 0x80) != 0;
129+
130+ if (xpad->pad_present != present) {
131+ xpad->pad_present = present;
132+ schedule_work(&xpad->work);
133+ }
134 }
135
136 /* Valid pad data */
137 if (data[1] != 0x1)
138 return;
139
140- xpad360_process_packet(xpad, cmd, &data[4]);
141+ rcu_read_lock();
142+ dev = rcu_dereference(xpad->x360w_dev);
143+ if (dev)
144+ xpad360_process_packet(xpad, dev, cmd, &data[4]);
145+ rcu_read_unlock();
146 }
147
148 /*
149@@ -678,7 +711,7 @@ static void xpad_irq_in(struct urb *urb)
150
151 switch (xpad->xtype) {
152 case XTYPE_XBOX360:
153- xpad360_process_packet(xpad, 0, xpad->idata);
154+ xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
155 break;
156 case XTYPE_XBOX360W:
157 xpad360w_process_packet(xpad, 0, xpad->idata);
158@@ -1132,14 +1165,7 @@ static int xpad_led_probe(struct usb_xpad *xpad)
159 if (error)
160 goto err_free_id;
161
162- if (xpad->xtype == XTYPE_XBOX360) {
163- /*
164- * Light up the segment corresponding to controller
165- * number on wired devices. On wireless we'll do that
166- * when they respond to "presence" packet.
167- */
168- xpad_identify_controller(xpad);
169- }
170+ xpad_identify_controller(xpad);
171
172 return 0;
173
174@@ -1223,8 +1249,11 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
175
176 static void xpad_deinit_input(struct usb_xpad *xpad)
177 {
178- xpad_led_disconnect(xpad);
179- input_unregister_device(xpad->dev);
180+ if (xpad->input_created) {
181+ xpad->input_created = false;
182+ xpad_led_disconnect(xpad);
183+ input_unregister_device(xpad->dev);
184+ }
185 }
186
187 static int xpad_init_input(struct usb_xpad *xpad)
188@@ -1313,6 +1342,7 @@ static int xpad_init_input(struct usb_xpad *xpad)
189 if (error)
190 goto err_disconnect_led;
191
192+ xpad->input_created = true;
193 return 0;
194
195 err_disconnect_led:
196@@ -1366,6 +1396,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
197 xpad->mapping = xpad_device[i].mapping;
198 xpad->xtype = xpad_device[i].xtype;
199 xpad->name = xpad_device[i].name;
200+ INIT_WORK(&xpad->work, xpad_presence_work);
201
202 if (xpad->xtype == XTYPE_UNKNOWN) {
203 if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
204@@ -1415,10 +1446,6 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
205
206 usb_set_intfdata(intf, xpad);
207
208- error = xpad_init_input(xpad);
209- if (error)
210- goto err_deinit_output;
211-
212 if (xpad->xtype == XTYPE_XBOX360W) {
213 /*
214 * Submit the int URB immediately rather than waiting for open
215@@ -1430,7 +1457,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
216 xpad->irq_in->dev = xpad->udev;
217 error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
218 if (error)
219- goto err_deinit_input;
220+ goto err_deinit_output;
221
222 /*
223 * Send presence packet.
224@@ -1442,13 +1469,15 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
225 error = xpad_inquiry_pad_presence(xpad);
226 if (error)
227 goto err_kill_in_urb;
228+ } else {
229+ error = xpad_init_input(xpad);
230+ if (error)
231+ goto err_deinit_output;
232 }
233 return 0;
234
235 err_kill_in_urb:
236 usb_kill_urb(xpad->irq_in);
237-err_deinit_input:
238- xpad_deinit_input(xpad);
239 err_deinit_output:
240 xpad_deinit_output(xpad);
241 err_free_in_urb:
242@@ -1465,17 +1494,19 @@ static void xpad_disconnect(struct usb_interface *intf)
243 {
244 struct usb_xpad *xpad = usb_get_intfdata (intf);
245
246- xpad_deinit_input(xpad);
247- xpad_deinit_output(xpad);
248-
249- if (xpad->xtype == XTYPE_XBOX360W) {
250+ if (xpad->xtype == XTYPE_XBOX360W)
251 usb_kill_urb(xpad->irq_in);
252- }
253+
254+ cancel_work_sync(&xpad->work);
255+
256+ xpad_deinit_input(xpad);
257
258 usb_free_urb(xpad->irq_in);
259 usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
260 xpad->idata, xpad->idata_dma);
261
262+ xpad_deinit_output(xpad);
263+
264 kfree(xpad);
265
266 usb_set_intfdata(intf, NULL);
267--
2682.17.1
269