]>
Commit | Line | Data |
---|---|---|
156d8e2c SL |
1 | From 0f42c57afcb17eb28d45ee6d3eb91e4407716020 Mon Sep 17 00:00:00 2001 |
2 | From: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com> | |
3 | Date: Wed, 9 Dec 2015 11:46:25 -0800 | |
4 | Subject: Input: xpad - handle "present" and "gone" correctly | |
5 | ||
6 | [ Upstream commit 09c8b00ae3e16c8d0fd4beb2ca064502a76c0f17 ] | |
7 | ||
8 | Handle the "a new device is present" message properly by dynamically | |
9 | creating 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. | |
11 | This requires a workqueue as we are in interrupt context when we learn | |
12 | about this. | |
13 | ||
14 | Also properly disconnect any devices that we are told are removed. | |
15 | ||
16 | Signed-off-by: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com> | |
17 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
18 | Signed-off-by: Pavel Rojtberg <rojtberg@gmail.com> | |
19 | Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> | |
20 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
21 | --- | |
22 | drivers/input/joystick/xpad.c | 107 ++++++++++++++++++++++------------ | |
23 | 1 file changed, 69 insertions(+), 38 deletions(-) | |
24 | ||
25 | diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c | |
26 | index 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 | -- | |
268 | 2.17.1 | |
269 |