aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorPierre-Loup A. Griffais <pgriffais@valvesoftware.com>2015-12-09 14:46:25 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-12-16 20:11:30 -0500
commit09c8b00ae3e16c8d0fd4beb2ca064502a76c0f17 (patch)
tree2f20cb330eabda919f60791416c31df86a217447 /drivers/input
parent93a017aa2f77291752e637bfd83f2459dba714cb (diff)
Input: xpad - handle "present" and "gone" correctly
Handle the "a new device is present" message properly by dynamically creating the input device at this point in time. This means we now do not "preallocate" all 4 devices when a single wireless base station is seen. This requires a workqueue as we are in interrupt context when we learn about this. Also properly disconnect any devices that we are told are removed. Signed-off-by: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Pavel Rojtberg <rojtberg@gmail.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/joystick/xpad.c107
1 files changed, 69 insertions, 38 deletions
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index c44cbd40f3fa..1d51d24c03d1 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -76,6 +76,8 @@
76 */ 76 */
77 77
78#include <linux/kernel.h> 78#include <linux/kernel.h>
79#include <linux/input.h>
80#include <linux/rcupdate.h>
79#include <linux/slab.h> 81#include <linux/slab.h>
80#include <linux/stat.h> 82#include <linux/stat.h>
81#include <linux/module.h> 83#include <linux/module.h>
@@ -319,10 +321,12 @@ MODULE_DEVICE_TABLE(usb, xpad_table);
319 321
320struct usb_xpad { 322struct usb_xpad {
321 struct input_dev *dev; /* input device interface */ 323 struct input_dev *dev; /* input device interface */
324 struct input_dev __rcu *x360w_dev;
322 struct usb_device *udev; /* usb device */ 325 struct usb_device *udev; /* usb device */
323 struct usb_interface *intf; /* usb interface */ 326 struct usb_interface *intf; /* usb interface */
324 327
325 int pad_present; 328 bool pad_present;
329 bool input_created;
326 330
327 struct urb *irq_in; /* urb for interrupt in report */ 331 struct urb *irq_in; /* urb for interrupt in report */
328 unsigned char *idata; /* input data */ 332 unsigned char *idata; /* input data */
@@ -343,8 +347,12 @@ struct usb_xpad {
343 int xtype; /* type of xbox device */ 347 int xtype; /* type of xbox device */
344 int pad_nr; /* the order x360 pads were attached */ 348 int pad_nr; /* the order x360 pads were attached */
345 const char *name; /* name of the device */ 349 const char *name; /* name of the device */
350 struct work_struct work; /* init/remove device from callback */
346}; 351};
347 352
353static int xpad_init_input(struct usb_xpad *xpad);
354static void xpad_deinit_input(struct usb_xpad *xpad);
355
348/* 356/*
349 * xpad_process_packet 357 * xpad_process_packet
350 * 358 *
@@ -424,11 +432,9 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
424 * http://www.free60.org/wiki/Gamepad 432 * http://www.free60.org/wiki/Gamepad
425 */ 433 */
426 434
427static void xpad360_process_packet(struct usb_xpad *xpad, 435static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
428 u16 cmd, unsigned char *data) 436 u16 cmd, unsigned char *data)
429{ 437{
430 struct input_dev *dev = xpad->dev;
431
432 /* digital pad */ 438 /* digital pad */
433 if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { 439 if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
434 /* dpad as buttons (left, right, up, down) */ 440 /* dpad as buttons (left, right, up, down) */
@@ -495,7 +501,30 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
495 input_sync(dev); 501 input_sync(dev);
496} 502}
497 503
498static void xpad_identify_controller(struct usb_xpad *xpad); 504static void xpad_presence_work(struct work_struct *work)
505{
506 struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
507 int error;
508
509 if (xpad->pad_present) {
510 error = xpad_init_input(xpad);
511 if (error) {
512 /* complain only, not much else we can do here */
513 dev_err(&xpad->dev->dev,
514 "unable to init device: %d\n", error);
515 } else {
516 rcu_assign_pointer(xpad->x360w_dev, xpad->dev);
517 }
518 } else {
519 RCU_INIT_POINTER(xpad->x360w_dev, NULL);
520 synchronize_rcu();
521 /*
522 * Now that we are sure xpad360w_process_packet is not
523 * using input device we can get rid of it.
524 */
525 xpad_deinit_input(xpad);
526 }
527}
499 528
500/* 529/*
501 * xpad360w_process_packet 530 * xpad360w_process_packet
@@ -513,24 +542,28 @@ static void xpad_identify_controller(struct usb_xpad *xpad);
513 */ 542 */
514static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) 543static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
515{ 544{
545 struct input_dev *dev;
546 bool present;
547
516 /* Presence change */ 548 /* Presence change */
517 if (data[0] & 0x08) { 549 if (data[0] & 0x08) {
518 if (data[1] & 0x80) { 550 present = (data[1] & 0x80) != 0;
519 xpad->pad_present = 1; 551
520 /* 552 if (xpad->pad_present != present) {
521 * Light up the segment corresponding to 553 xpad->pad_present = present;
522 * controller number. 554 schedule_work(&xpad->work);
523 */ 555 }
524 xpad_identify_controller(xpad);
525 } else
526 xpad->pad_present = 0;
527 } 556 }
528 557
529 /* Valid pad data */ 558 /* Valid pad data */
530 if (data[1] != 0x1) 559 if (data[1] != 0x1)
531 return; 560 return;
532 561
533 xpad360_process_packet(xpad, cmd, &data[4]); 562 rcu_read_lock();
563 dev = rcu_dereference(xpad->x360w_dev);
564 if (dev)
565 xpad360_process_packet(xpad, dev, cmd, &data[4]);
566 rcu_read_unlock();
534} 567}
535 568
536/* 569/*
@@ -659,7 +692,7 @@ static void xpad_irq_in(struct urb *urb)
659 692
660 switch (xpad->xtype) { 693 switch (xpad->xtype) {
661 case XTYPE_XBOX360: 694 case XTYPE_XBOX360:
662 xpad360_process_packet(xpad, 0, xpad->idata); 695 xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
663 break; 696 break;
664 case XTYPE_XBOX360W: 697 case XTYPE_XBOX360W:
665 xpad360w_process_packet(xpad, 0, xpad->idata); 698 xpad360w_process_packet(xpad, 0, xpad->idata);
@@ -1001,14 +1034,7 @@ static int xpad_led_probe(struct usb_xpad *xpad)
1001 if (error) 1034 if (error)
1002 goto err_free_id; 1035 goto err_free_id;
1003 1036
1004 if (xpad->xtype == XTYPE_XBOX360) { 1037 xpad_identify_controller(xpad);
1005 /*
1006 * Light up the segment corresponding to controller
1007 * number on wired devices. On wireless we'll do that
1008 * when they respond to "presence" packet.
1009 */
1010 xpad_identify_controller(xpad);
1011 }
1012 1038
1013 return 0; 1039 return 0;
1014 1040
@@ -1097,8 +1123,11 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
1097 1123
1098static void xpad_deinit_input(struct usb_xpad *xpad) 1124static void xpad_deinit_input(struct usb_xpad *xpad)
1099{ 1125{
1100 xpad_led_disconnect(xpad); 1126 if (xpad->input_created) {
1101 input_unregister_device(xpad->dev); 1127 xpad->input_created = false;
1128 xpad_led_disconnect(xpad);
1129 input_unregister_device(xpad->dev);
1130 }
1102} 1131}
1103 1132
1104static int xpad_init_input(struct usb_xpad *xpad) 1133static int xpad_init_input(struct usb_xpad *xpad)
@@ -1181,6 +1210,7 @@ static int xpad_init_input(struct usb_xpad *xpad)
1181 if (error) 1210 if (error)
1182 goto err_disconnect_led; 1211 goto err_disconnect_led;
1183 1212
1213 xpad->input_created = true;
1184 return 0; 1214 return 0;
1185 1215
1186err_disconnect_led: 1216err_disconnect_led:
@@ -1241,6 +1271,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
1241 xpad->mapping = xpad_device[i].mapping; 1271 xpad->mapping = xpad_device[i].mapping;
1242 xpad->xtype = xpad_device[i].xtype; 1272 xpad->xtype = xpad_device[i].xtype;
1243 xpad->name = xpad_device[i].name; 1273 xpad->name = xpad_device[i].name;
1274 INIT_WORK(&xpad->work, xpad_presence_work);
1244 1275
1245 if (xpad->xtype == XTYPE_UNKNOWN) { 1276 if (xpad->xtype == XTYPE_UNKNOWN) {
1246 if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) { 1277 if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
@@ -1277,10 +1308,6 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
1277 1308
1278 usb_set_intfdata(intf, xpad); 1309 usb_set_intfdata(intf, xpad);
1279 1310
1280 error = xpad_init_input(xpad);
1281 if (error)
1282 goto err_deinit_output;
1283
1284 if (xpad->xtype == XTYPE_XBOX360W) { 1311 if (xpad->xtype == XTYPE_XBOX360W) {
1285 /* 1312 /*
1286 * Submit the int URB immediately rather than waiting for open 1313 * Submit the int URB immediately rather than waiting for open
@@ -1292,7 +1319,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
1292 xpad->irq_in->dev = xpad->udev; 1319 xpad->irq_in->dev = xpad->udev;
1293 error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); 1320 error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
1294 if (error) 1321 if (error)
1295 goto err_deinit_input; 1322 goto err_deinit_output;
1296 1323
1297 /* 1324 /*
1298 * Send presence packet. 1325 * Send presence packet.
@@ -1304,13 +1331,15 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
1304 error = xpad_inquiry_pad_presence(xpad); 1331 error = xpad_inquiry_pad_presence(xpad);
1305 if (error) 1332 if (error)
1306 goto err_kill_in_urb; 1333 goto err_kill_in_urb;
1334 } else {
1335 error = xpad_init_input(xpad);
1336 if (error)
1337 goto err_deinit_output;
1307 } 1338 }
1308 return 0; 1339 return 0;
1309 1340
1310err_kill_in_urb: 1341err_kill_in_urb:
1311 usb_kill_urb(xpad->irq_in); 1342 usb_kill_urb(xpad->irq_in);
1312err_deinit_input:
1313 xpad_deinit_input(xpad);
1314err_deinit_output: 1343err_deinit_output:
1315 xpad_deinit_output(xpad); 1344 xpad_deinit_output(xpad);
1316err_free_in_urb: 1345err_free_in_urb:
@@ -1327,17 +1356,19 @@ static void xpad_disconnect(struct usb_interface *intf)
1327{ 1356{
1328 struct usb_xpad *xpad = usb_get_intfdata (intf); 1357 struct usb_xpad *xpad = usb_get_intfdata (intf);
1329 1358
1330 xpad_deinit_input(xpad); 1359 if (xpad->xtype == XTYPE_XBOX360W)
1331 xpad_deinit_output(xpad);
1332
1333 if (xpad->xtype == XTYPE_XBOX360W) {
1334 usb_kill_urb(xpad->irq_in); 1360 usb_kill_urb(xpad->irq_in);
1335 } 1361
1362 cancel_work_sync(&xpad->work);
1363
1364 xpad_deinit_input(xpad);
1336 1365
1337 usb_free_urb(xpad->irq_in); 1366 usb_free_urb(xpad->irq_in);
1338 usb_free_coherent(xpad->udev, XPAD_PKT_LEN, 1367 usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
1339 xpad->idata, xpad->idata_dma); 1368 xpad->idata, xpad->idata_dma);
1340 1369
1370 xpad_deinit_output(xpad);
1371
1341 kfree(xpad); 1372 kfree(xpad);
1342 1373
1343 usb_set_intfdata(intf, NULL); 1374 usb_set_intfdata(intf, NULL);