Skip to content

Commit 72d1945

Browse files
bentissJiri Kosina
authored andcommitted
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>
1 parent 5cc5084 commit 72d1945

2 files changed

Lines changed: 55 additions & 41 deletions

File tree

drivers/hid/hid-input.c

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,31 @@ static void hidinput_cleanup_hidinput(struct hid_device *hid,
14681468
kfree(hidinput);
14691469
}
14701470

1471+
static 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+
1485+
static 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+
14711496
/*
14721497
* Register the input device; print a message.
14731498
* Configure the input layer interface
@@ -1478,8 +1503,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
14781503
{
14791504
struct hid_driver *drv = hid->driver;
14801505
struct hid_report *report;
1481-
struct hid_input *hidinput = NULL;
1482-
int i, j, k;
1506+
struct hid_input *next, *hidinput = NULL;
1507+
int i, k;
14831508

14841509
INIT_LIST_HEAD(&hid->inputs);
14851510
INIT_WORK(&hid->led_work, hidinput_led_worker);
@@ -1509,64 +1534,49 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
15091534
if (!report->maxfield)
15101535
continue;
15111536

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+
15121544
if (!hidinput) {
15131545
hidinput = hidinput_allocate(hid);
15141546
if (!hidinput)
15151547
goto out_unwind;
15161548
}
15171549

1518-
for (i = 0; i < report->maxfield; i++)
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;
1550+
hidinput_configure_usages(hidinput, report);
15261551

1527-
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. */
1552+
if (hid->quirks & HID_QUIRK_MULTI_INPUT)
15331553
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-
}
15411554
}
15421555
}
15431556

1544-
if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
1545-
!hidinput_has_been_populated(hidinput)) {
1546-
/* no need to register an input device not populated */
1547-
hidinput_cleanup_hidinput(hid, hidinput);
1548-
hidinput = NULL;
1557+
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
1558+
if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
1559+
!hidinput_has_been_populated(hidinput)) {
1560+
/* no need to register an input device not populated */
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;
15491571
}
15501572

15511573
if (list_empty(&hid->inputs)) {
15521574
hid_err(hid, "No inputs registered, leaving\n");
15531575
goto out_unwind;
15541576
}
15551577

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-
15641578
return 0;
15651579

1566-
out_cleanup:
1567-
list_del(&hidinput->list);
1568-
input_free_device(hidinput->input);
1569-
kfree(hidinput);
15701580
out_unwind:
15711581
/* unwind the ones we already registered */
15721582
hidinput_disconnect(hid);
@@ -1583,7 +1593,10 @@ void hidinput_disconnect(struct hid_device *hid)
15831593

15841594
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
15851595
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);
15871600
kfree(hidinput);
15881601
}
15891602

include/linux/hid.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ struct hid_input {
479479
struct list_head list;
480480
struct hid_report *report;
481481
struct input_dev *input;
482+
bool registered;
482483
};
483484

484485
enum hid_type {

0 commit comments

Comments
 (0)