Re: [PATCH 2/2] hid: multitouch: improve custom quirks kernel parameter

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 





On Mon, Apr 18, 2022 at 5:19 AM Tao Jin <tao-j@xxxxxxxxxxx> wrote:

This allows a list of different quirks to be matched against
corresponding hardware ids in case there are multiple devices present on
the same system.

The code borrowed some idea from vfio_pci.c

I am not completely against such a parameter, but this raises a couple
of pain points:
- we are now adding string parsing in the module. I know this is
something somewhat common for dynamic quirks, but still, that is
potential code to maintain and ensure it doesn't do anything wrong
- how are we going to force people to contribute to the upstream kernel
to fix their device for anybody, not just them? Users might be tempted
to just drop the udev rule and then forget about it.

(foreword: I am deeply convinced BPF is the future, and sees nails
everywhere given that wonderful hammer).

I am trying really hard to stop creating new kernel APIs for HID. One
solution is to use BPF.

This would solve the first point, given that instead of providing a
string, we would request users to provide a BPF program, which would be
verified by the BPF verifier.
This would also add a slighter difficulty to address the second point as
writing the BPF program would be more "difficult" than running a script.

I don't have a complete working solution here, but basically the kernel
patch would be:

---
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 99eabfb4145b..b0d187e5fe70 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -1695,9 +1695,15 @@ static void mt_expired_timeout(struct timer_list *t)
 	clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
 }
+__weak noinline int mt_override_quirks(const struct hid_device_id *id)
+{
+	return 0;
+}
+ALLOW_ERROR_INJECTION(mt_override_quirks, ERRNO);
+
 static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
-	int ret, i;
+	int ret, i, override_quirks;
 	struct mt_device *td;
 	const struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
@@ -1746,6 +1752,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) timer_setup(&td->release_timer, mt_expired_timeout, 0); + override_quirks = mt_override_quirks(id);
+	if (override_quirks)
+		td->mtclass.quirks = override_quirks;
+
 	ret = hid_parse(hdev);
 	if (ret != 0)
 		return ret;

---

And then the BPF program can match against `id` fields and decide to
return or not a new quirk override.


Signed-off-by: Tao Jin <tao-j@xxxxxxxxxxx>
---
 drivers/hid/hid-multitouch.c | 32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index c6d64f8..f662960 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -398,9 +398,9 @@ static const struct mt_class mt_classes[] = {
        { }
 };

-static int override_quirks = -1;
-module_param(override_quirks, int, 0444);
-MODULE_PARM_DESC(override_quirks, "Signed integer to override quirks in mtclass, must >= 0 to enable override.");
+static char override_quirks[128] = "";
+module_param_string(override_quirks, override_quirks, sizeof(override_quirks), 0444);
+MODULE_PARM_DESC(override_quirks, "List of quirks and corresponding device ids in hex to override quirks, format is \"wanted_quirks:vendor:product\", multiple comma separated entries can be specified.");

The previous patch added this module parameter, and now this one changes
entirely its API. We better squash the inclusion of the new module
parameter into this commit so we don't have 2 APIs for the same module
parameter.

Also, for the format, we probably want
"bus:vendor:product:group:wanted_quirks", where bus and group can be
replaced by * if we don't care about them.


 static ssize_t mt_show_quirks(struct device *dev,
                           struct device_attribute *attr,
@@ -1714,6 +1714,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
        int ret, i;
        struct mt_device *td;
        const struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
+       char *p, *qk;

        for (i = 0; mt_classes[i].name ; i++) {
                if (id->driver_data == mt_classes[i].name) {
@@ -1753,9 +1754,28 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (id->group != HID_GROUP_MULTITOUCH_WIN_8)
                hdev->quirks |= HID_QUIRK_MULTI_INPUT;

-       if (override_quirks >= 0) {
-               hid_info(hdev, "overriding quirks with: %d(0x%x)", override_quirks, override_quirks);
-               td->mtclass.quirks = override_quirks;
+       p = override_quirks;
+       while ((qk = strsep(&p, ","))) {
+               __u32 wanted_quirks = 0;
+               __u32 vendor, product = HID_ANY_ID;
+               int fields;
+
+               if (!strlen(qk))
+                       continue;
+
+               fields = sscanf(qk, "%x:%x:%x", &wanted_quirks,
+                               &vendor, &product);
+
+               if (fields != 3) {
+                       continue;
+               }
+
+               if (id->vendor == vendor && id->product == product) {
+                       hid_info(hdev, "overriding quirks of %04x:%04x with: %x",
+                                       id->vendor, id->product, wanted_quirks);
+                       td->mtclass.quirks = wanted_quirks;
+                       break;
+               }

This should definitely be extracted in a separate function.

Cheers,
Benjamin

        }

        if (td->mtclass.quirks & MT_QUIRK_FORCE_MULTI_INPUT) {
--
2.35.1





[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux