aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>2016-11-25 08:27:21 -0500
committerJiri Kosina <jkosina@suse.cz>2016-11-28 08:39:47 -0500
commit72d19459d7919f966594576bb042d15a451f27ea (patch)
tree57f4aacfa8709eb1f37dff82d51f49d82aae3363
parent5cc5084dd9afa2f9bf953b0217bdb1b7c2158be1 (diff)
HID: input: rework HID_QUIRK_MULTI_INPUT
The purpose of HID_QUIRK_MULTI_INPUT is to have an input device per report id. This is useful when the HID device presents several HID collections of different device types. The current implementation of hid-input creates one input node per id per type (input or output). This is problematic for the LEDs of a keyboard as they are often set through an output report. The current code creates one input node with all the keyboard keys, and one other with only the LEDs. To solve this, we use a two-passes way: - first, we initialize all input nodes and associate one per report id - then, we register all the input nodes Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-input.c95
-rw-r--r--include/linux/hid.h1
2 files changed, 55 insertions, 41 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index fb9ace1cef8b..55db58459531 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1468,6 +1468,31 @@ static void hidinput_cleanup_hidinput(struct hid_device *hid,
1468 kfree(hidinput); 1468 kfree(hidinput);
1469} 1469}
1470 1470
1471static struct hid_input *hidinput_match(struct hid_report *report)
1472{
1473 struct hid_device *hid = report->device;
1474 struct hid_input *hidinput;
1475
1476 list_for_each_entry(hidinput, &hid->inputs, list) {
1477 if (hidinput->report &&
1478 hidinput->report->id == report->id)
1479 return hidinput;
1480 }
1481
1482 return NULL;
1483}
1484
1485static inline void hidinput_configure_usages(struct hid_input *hidinput,
1486 struct hid_report *report)
1487{
1488 int i, j;
1489
1490 for (i = 0; i < report->maxfield; i++)
1491 for (j = 0; j < report->field[i]->maxusage; j++)
1492 hidinput_configure_usage(hidinput, report->field[i],
1493 report->field[i]->usage + j);
1494}
1495
1471/* 1496/*
1472 * Register the input device; print a message. 1497 * Register the input device; print a message.
1473 * Configure the input layer interface 1498 * Configure the input layer interface
@@ -1478,8 +1503,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
1478{ 1503{
1479 struct hid_driver *drv = hid->driver; 1504 struct hid_driver *drv = hid->driver;
1480 struct hid_report *report; 1505 struct hid_report *report;
1481 struct hid_input *hidinput = NULL; 1506 struct hid_input *next, *hidinput = NULL;
1482 int i, j, k; 1507 int i, k;
1483 1508
1484 INIT_LIST_HEAD(&hid->inputs); 1509 INIT_LIST_HEAD(&hid->inputs);
1485 INIT_WORK(&hid->led_work, hidinput_led_worker); 1510 INIT_WORK(&hid->led_work, hidinput_led_worker);
@@ -1509,43 +1534,40 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
1509 if (!report->maxfield) 1534 if (!report->maxfield)
1510 continue; 1535 continue;
1511 1536
1537 /*
1538 * Find the previous hidinput report attached
1539 * to this report id.
1540 */
1541 if (hid->quirks & HID_QUIRK_MULTI_INPUT)
1542 hidinput = hidinput_match(report);
1543
1512 if (!hidinput) { 1544 if (!hidinput) {
1513 hidinput = hidinput_allocate(hid); 1545 hidinput = hidinput_allocate(hid);
1514 if (!hidinput) 1546 if (!hidinput)
1515 goto out_unwind; 1547 goto out_unwind;
1516 } 1548 }
1517 1549
1518 for (i = 0; i < report->maxfield; i++) 1550 hidinput_configure_usages(hidinput, report);
1519 for (j = 0; j < report->field[i]->maxusage; j++)
1520 hidinput_configure_usage(hidinput, report->field[i],
1521 report->field[i]->usage + j);
1522
1523 if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
1524 !hidinput_has_been_populated(hidinput))
1525 continue;
1526 1551
1527 if (hid->quirks & HID_QUIRK_MULTI_INPUT) { 1552 if (hid->quirks & HID_QUIRK_MULTI_INPUT)
1528 /* This will leave hidinput NULL, so that it
1529 * allocates another one if we have more inputs on
1530 * the same interface. Some devices (e.g. Happ's
1531 * UGCI) cram a lot of unrelated inputs into the
1532 * same interface. */
1533 hidinput->report = report; 1553 hidinput->report = report;
1534 if (drv->input_configured &&
1535 drv->input_configured(hid, hidinput))
1536 goto out_cleanup;
1537 if (input_register_device(hidinput->input))
1538 goto out_cleanup;
1539 hidinput = NULL;
1540 }
1541 } 1554 }
1542 } 1555 }
1543 1556
1544 if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && 1557 list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
1545 !hidinput_has_been_populated(hidinput)) { 1558 if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
1546 /* no need to register an input device not populated */ 1559 !hidinput_has_been_populated(hidinput)) {
1547 hidinput_cleanup_hidinput(hid, hidinput); 1560 /* no need to register an input device not populated */
1548 hidinput = NULL; 1561 hidinput_cleanup_hidinput(hid, hidinput);
1562 continue;
1563 }
1564
1565 if (drv->input_configured &&
1566 drv->input_configured(hid, hidinput))
1567 goto out_unwind;
1568 if (input_register_device(hidinput->input))
1569 goto out_unwind;
1570 hidinput->registered = true;
1549 } 1571 }
1550 1572
1551 if (list_empty(&hid->inputs)) { 1573 if (list_empty(&hid->inputs)) {
@@ -1553,20 +1575,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
1553 goto out_unwind; 1575 goto out_unwind;
1554 } 1576 }
1555 1577
1556 if (hidinput) {
1557 if (drv->input_configured &&
1558 drv->input_configured(hid, hidinput))
1559 goto out_cleanup;
1560 if (input_register_device(hidinput->input))
1561 goto out_cleanup;
1562 }
1563
1564 return 0; 1578 return 0;
1565 1579
1566out_cleanup:
1567 list_del(&hidinput->list);
1568 input_free_device(hidinput->input);
1569 kfree(hidinput);
1570out_unwind: 1580out_unwind:
1571 /* unwind the ones we already registered */ 1581 /* unwind the ones we already registered */
1572 hidinput_disconnect(hid); 1582 hidinput_disconnect(hid);
@@ -1583,7 +1593,10 @@ void hidinput_disconnect(struct hid_device *hid)
1583 1593
1584 list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { 1594 list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
1585 list_del(&hidinput->list); 1595 list_del(&hidinput->list);
1586 input_unregister_device(hidinput->input); 1596 if (hidinput->registered)
1597 input_unregister_device(hidinput->input);
1598 else
1599 input_free_device(hidinput->input);
1587 kfree(hidinput); 1600 kfree(hidinput);
1588 } 1601 }
1589 1602
diff --git a/include/linux/hid.h b/include/linux/hid.h
index b2ec82712baa..596b9232c19e 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -479,6 +479,7 @@ struct hid_input {
479 struct list_head list; 479 struct list_head list;
480 struct hid_report *report; 480 struct hid_report *report;
481 struct input_dev *input; 481 struct input_dev *input;
482 bool registered;
482}; 483};
483 484
484enum hid_type { 485enum hid_type {