From d6f83894110de247a81392ab7ef89e5498df7e80 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 21 Jun 2018 19:27:16 -0700 Subject: HID: i2c-hid: Use devm to allocate i2c_hid struct Use devm here to save some lines and prepare for bulk regulator usage in this driver. Otherwise, when we devm bulk get regulators we'll free the containing i2c_hid structure and try to put regulator pointers from freed memory. Cc: Benjamin Tissoires Cc: Hans de Goede Cc: Andy Shevchenko Cc: Dmitry Torokhov Cc: Doug Anderson Signed-off-by: Stephen Boyd Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index c1652bb7bd15..c7d6738dc524 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1002,18 +1002,18 @@ static int i2c_hid_probe(struct i2c_client *client, return client->irq; } - ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL); + ihid = devm_kzalloc(&client->dev, sizeof(*ihid), GFP_KERNEL); if (!ihid) return -ENOMEM; if (client->dev.of_node) { ret = i2c_hid_of_probe(client, &ihid->pdata); if (ret) - goto err; + return ret; } else if (!platform_data) { ret = i2c_hid_acpi_pdata(client, &ihid->pdata); if (ret) - goto err; + return ret; } else { ihid->pdata = *platform_data; } @@ -1126,7 +1126,6 @@ err_regulator: err: i2c_hid_free_buffers(ihid); - kfree(ihid); return ret; } @@ -1150,8 +1149,6 @@ static int i2c_hid_remove(struct i2c_client *client) regulator_disable(ihid->pdata.supply); - kfree(ihid); - return 0; } -- cgit v1.2.3 From 6136f97cd2dd2b5a88d62bd73cc663a803124c09 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 21 Jun 2018 19:27:17 -0700 Subject: HID: i2c-hid: Add vddl regulator control Some wacom w9013 devices have a vddl supply for "low valtage" requirements. Add support in this driver to turn on this low voltage supply. We can also drop a handful of error messages because the regulator core is already printing an error when bulk regulators fail to enable or disable. Cc: Benjamin Tissoires Cc: Hans de Goede Cc: Andy Shevchenko Cc: Dmitry Torokhov Cc: Doug Anderson Acked-by: Rob Herring Cc: Signed-off-by: Stephen Boyd Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- .../devicetree/bindings/input/hid-over-i2c.txt | 3 +- drivers/hid/i2c-hid/i2c-hid.c | 48 +++++++++++----------- include/linux/platform_data/i2c-hid.h | 7 ++-- 3 files changed, 29 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/input/hid-over-i2c.txt b/Documentation/devicetree/bindings/input/hid-over-i2c.txt index 4d3da9d91de4..89e6ab89ba38 100644 --- a/Documentation/devicetree/bindings/input/hid-over-i2c.txt +++ b/Documentation/devicetree/bindings/input/hid-over-i2c.txt @@ -26,7 +26,8 @@ device-specific compatible properties, which should be used in addition to the - compatible: * "wacom,w9013" (Wacom W9013 digitizer). Supports: - - vdd-supply + - vdd-supply (3.3V) + - vddl-supply (1.8V) - post-power-on-delay-ms - vdd-supply: phandle of the regulator that provides the supply voltage. diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index c7d6738dc524..4f532d9238fb 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1021,21 +1021,20 @@ static int i2c_hid_probe(struct i2c_client *client, /* Parse platform agnostic common properties from ACPI / device tree */ i2c_hid_fwnode_probe(client, &ihid->pdata); - ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(ihid->pdata.supply)) { - ret = PTR_ERR(ihid->pdata.supply); - if (ret != -EPROBE_DEFER) - dev_err(&client->dev, "Failed to get regulator: %d\n", - ret); - goto err; - } + ihid->pdata.supplies[0].supply = "vdd"; + ihid->pdata.supplies[1].supply = "vddl"; + + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); + if (ret) + return ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); + if (ret < 0) + return ret; - ret = regulator_enable(ihid->pdata.supply); - if (ret < 0) { - dev_err(&client->dev, "Failed to enable regulator: %d\n", - ret); - goto err; - } if (ihid->pdata.post_power_delay_ms) msleep(ihid->pdata.post_power_delay_ms); @@ -1122,9 +1121,8 @@ err_pm: pm_runtime_disable(&client->dev); err_regulator: - regulator_disable(ihid->pdata.supply); - -err: + regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); i2c_hid_free_buffers(ihid); return ret; } @@ -1147,7 +1145,8 @@ static int i2c_hid_remove(struct i2c_client *client) if (ihid->bufsize) i2c_hid_free_buffers(ihid); - regulator_disable(ihid->pdata.supply); + regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); return 0; } @@ -1198,9 +1197,8 @@ static int i2c_hid_suspend(struct device *dev) hid_warn(hid, "Failed to enable irq wake: %d\n", wake_status); } else { - ret = regulator_disable(ihid->pdata.supply); - if (ret < 0) - hid_warn(hid, "Failed to disable supply: %d\n", ret); + regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); } return 0; @@ -1215,9 +1213,11 @@ static int i2c_hid_resume(struct device *dev) int wake_status; if (!device_may_wakeup(&client->dev)) { - ret = regulator_enable(ihid->pdata.supply); - if (ret < 0) - hid_warn(hid, "Failed to enable supply: %d\n", ret); + ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); + if (ret) + hid_warn(hid, "Failed to enable supplies: %d\n", ret); + if (ihid->pdata.post_power_delay_ms) msleep(ihid->pdata.post_power_delay_ms); } else if (ihid->irq_wake_enabled) { diff --git a/include/linux/platform_data/i2c-hid.h b/include/linux/platform_data/i2c-hid.h index 1fb088239d12..c628bb5e1061 100644 --- a/include/linux/platform_data/i2c-hid.h +++ b/include/linux/platform_data/i2c-hid.h @@ -12,14 +12,13 @@ #ifndef __LINUX_I2C_HID_H #define __LINUX_I2C_HID_H +#include #include -struct regulator; - /** * struct i2chid_platform_data - used by hid over i2c implementation. * @hid_descriptor_address: i2c register where the HID descriptor is stored. - * @supply: regulator for powering on the device. + * @supplies: regulators for powering on the device. * @post_power_delay_ms: delay after powering on before device is usable. * * Note that it is the responsibility of the platform driver (or the acpi 5.0 @@ -35,7 +34,7 @@ struct regulator; */ struct i2c_hid_platform_data { u16 hid_descriptor_address; - struct regulator *supply; + struct regulator_bulk_data supplies[2]; int post_power_delay_ms; }; -- cgit v1.2.3 From 44d4d51de9a3534a2b63d69efda02a10e66541e4 Mon Sep 17 00:00:00 2001 From: Zhouyang Jia Date: Thu, 14 Jun 2018 21:37:17 +0800 Subject: HID: hid-ntrig: add error handling for sysfs_create_group When sysfs_create_group fails, the lack of error-handling code may cause unexpected results. This patch adds error-handling code after calling sysfs_create_group. Signed-off-by: Zhouyang Jia Signed-off-by: Jiri Kosina --- drivers/hid/hid-ntrig.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 43b1c7234316..9bc6f4867cb3 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -955,6 +955,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = sysfs_create_group(&hdev->dev.kobj, &ntrig_attribute_group); + if (ret) + hid_err(hdev, "cannot create sysfs group\n"); return 0; err_free: -- cgit v1.2.3 From dc9b8e85ed95cbe7e3ad0eabb5b48d617bbc365e Mon Sep 17 00:00:00 2001 From: Robert Munteanu Date: Tue, 19 Jun 2018 11:20:40 +0300 Subject: HID: redragon: fix num lock and caps lock LEDs The redragon asura keyboard registers two input devices. The initial commit 85455dd906d5 ("HID: redragon: Fix modifier keys for Redragon Asura Keyboard") considered this an error and prevented one of the devices from registering. However, once this is done the num lock and caps lock leds no longer toggle on and off, although the key functionality is not affected. This commit removes the code that prevents the input device registration and restores the num lock and caps lock LEDs. Fixes: 85455dd906d5 ("HID: redragon: Fix modifier keys for Redragon Asura Keyboard") Signed-off-by: Robert Munteanu Signed-off-by: Jiri Kosina --- drivers/hid/hid-redragon.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-redragon.c b/drivers/hid/hid-redragon.c index daf59578bf93..73c9d4c4fa34 100644 --- a/drivers/hid/hid-redragon.c +++ b/drivers/hid/hid-redragon.c @@ -44,29 +44,6 @@ static __u8 *redragon_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } -static int redragon_probe(struct hid_device *dev, - const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(dev); - if (ret) { - hid_err(dev, "parse failed\n"); - return ret; - } - - /* do not register unused input device */ - if (dev->maxapplication == 1) - return 0; - - ret = hid_hw_start(dev, HID_CONNECT_DEFAULT); - if (ret) { - hid_err(dev, "hw start failed\n"); - return ret; - } - - return 0; -} static const struct hid_device_id redragon_devices[] = { {HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_REDRAGON_ASURA)}, {} @@ -77,8 +54,7 @@ MODULE_DEVICE_TABLE(hid, redragon_devices); static struct hid_driver redragon_driver = { .name = "redragon", .id_table = redragon_devices, - .report_fixup = redragon_report_fixup, - .probe = redragon_probe + .report_fixup = redragon_report_fixup }; module_hid_driver(redragon_driver); -- cgit v1.2.3 From f49255e00c2e085b1467087d2dbb6b01d7042cc8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 19 Jun 2018 23:59:44 +0200 Subject: HID: usbhid: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the ->lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Cc: Jiri Kosina Cc: Benjamin Tissoires Cc: linux-input@vger.kernel.org Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index af0e0d061b15..11103efebbaa 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -480,6 +480,7 @@ static void hid_ctrl(struct urb *urb) { struct hid_device *hid = urb->context; struct usbhid_device *usbhid = hid->driver_data; + unsigned long flags; int unplug = 0, status = urb->status; switch (status) { @@ -501,7 +502,7 @@ static void hid_ctrl(struct urb *urb) hid_warn(urb->dev, "ctrl urb status %d received\n", status); } - spin_lock(&usbhid->lock); + spin_lock_irqsave(&usbhid->lock, flags); if (unplug) { usbhid->ctrltail = usbhid->ctrlhead; @@ -511,13 +512,13 @@ static void hid_ctrl(struct urb *urb) if (usbhid->ctrlhead != usbhid->ctrltail && hid_submit_ctrl(hid) == 0) { /* Successfully submitted next urb in queue */ - spin_unlock(&usbhid->lock); + spin_unlock_irqrestore(&usbhid->lock, flags); return; } } clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - spin_unlock(&usbhid->lock); + spin_unlock_irqrestore(&usbhid->lock, flags); usb_autopm_put_interface_async(usbhid->intf); wake_up(&usbhid->wait); } -- cgit v1.2.3 From d4bdf2d2ba737c2c02ec014bdc2db40a030595d0 Mon Sep 17 00:00:00 2001 From: Nicolas Adenis-Lamarre Date: Wed, 20 Jun 2018 12:55:15 +0200 Subject: HID: wiimote: add support for Guitar-Hero devices This adds the drums and guitar extensions for Wiimote devices. Devices are reported as "Nintendo Wii Remote Guitar/Drums". If I ever get my hands on "RockBand" guitars, I will try to report them via the same interface so user-space does not have to bother which device it deals with. This is a rebase of the original commits 8e22ecb603c8 and 73f8645db191. They were reverted several years ago, since they were dependent on the ABS_* rework of the input core. Sadly, this never worked out so these commits were never pushed into a release. This rebase now uses the ABS_HAT* event codes to report all pressure information. Signed-off-by: Nicolas.Adenis-Lamarre (Original commits by Nicolas, adapted to v4.18 by David) Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-core.c | 14 ++ drivers/hid/hid-wiimote-modules.c | 440 ++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-wiimote.h | 3 + 3 files changed, 457 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 579884ebd94d..7780da4fe897 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -455,6 +455,12 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem) return WIIMOTE_EXT_BALANCE_BOARD; if (rmem[4] == 0x01 && rmem[5] == 0x20) return WIIMOTE_EXT_PRO_CONTROLLER; + if (rmem[0] == 0x01 && rmem[1] == 0x00 && + rmem[4] == 0x01 && rmem[5] == 0x03) + return WIIMOTE_EXT_DRUMS; + if (rmem[0] == 0x00 && rmem[1] == 0x00 && + rmem[4] == 0x01 && rmem[5] == 0x03) + return WIIMOTE_EXT_GUITAR; return WIIMOTE_EXT_UNKNOWN; } @@ -488,6 +494,8 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype) /* map MP with correct pass-through mode */ switch (exttype) { case WIIMOTE_EXT_CLASSIC_CONTROLLER: + case WIIMOTE_EXT_DRUMS: + case WIIMOTE_EXT_GUITAR: wmem = 0x07; break; case WIIMOTE_EXT_NUNCHUK: @@ -1075,6 +1083,8 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = { [WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller", [WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board", [WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller", + [WIIMOTE_EXT_DRUMS] = "Nintendo Wii Drums", + [WIIMOTE_EXT_GUITAR] = "Nintendo Wii Guitar", }; /* @@ -1660,6 +1670,10 @@ static ssize_t wiimote_ext_show(struct device *dev, return sprintf(buf, "balanceboard\n"); case WIIMOTE_EXT_PRO_CONTROLLER: return sprintf(buf, "procontroller\n"); + case WIIMOTE_EXT_DRUMS: + return sprintf(buf, "drums\n"); + case WIIMOTE_EXT_GUITAR: + return sprintf(buf, "guitar\n"); case WIIMOTE_EXT_UNKNOWN: /* fallthrough */ default: diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index c830ed39348f..aa72eb9a8e2f 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -1949,6 +1949,444 @@ static const struct wiimod_ops wiimod_pro = { .in_ext = wiimod_pro_in_ext, }; +/* + * Drums + * Guitar-Hero, Rock-Band and other games came bundled with drums which can + * be plugged as extension to a Wiimote. Drum-reports are still not entirely + * figured out, but the most important information is known. + * We create a separate device for drums and report all information via this + * input device. + */ + +static inline void wiimod_drums_report_pressure(struct wiimote_data *wdata, + __u8 none, __u8 which, + __u8 pressure, __u8 onoff, + __u8 *store, __u16 code, + __u8 which_code) +{ + static const __u8 default_pressure = 3; + + if (!none && which == which_code) { + *store = pressure; + input_report_abs(wdata->extension.input, code, *store); + } else if (onoff != !!*store) { + *store = onoff ? default_pressure : 0; + input_report_abs(wdata->extension.input, code, *store); + } +} + +static void wiimod_drums_in_ext(struct wiimote_data *wdata, const __u8 *ext) +{ + __u8 pressure, which, none, hhp, sx, sy; + __u8 o, r, y, g, b, bass, bm, bp; + + /* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | 0 | 0 | SX <5:0> | + * 2 | 0 | 0 | SY <5:0> | + * -----+-----+-----+-----------------------------+-----+ + * 3 | HPP | NON | WHICH <5:1> | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 4 | SOFT <7:5> | 0 | 1 | 1 | 0 | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | ? | 1 | 1 | B- | 1 | B+ | 1 | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | O | R | Y | G | B | BSS | 1 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * All buttons are 0 if pressed + * + * With Motion+ enabled, the following bits will get invalid: + * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | 0 | 0 | SX <5:1> |XXXXX| + * 2 | 0 | 0 | SY <5:1> |XXXXX| + * -----+-----+-----+-----------------------------+-----+ + * 3 | HPP | NON | WHICH <5:1> | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 4 | SOFT <7:5> | 0 | 1 | 1 | 0 | ? | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | ? | 1 | 1 | B- | 1 | B+ | 1 |XXXXX| + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | O | R | Y | G | B | BSS |XXXXX|XXXXX| + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + */ + + pressure = 7 - (ext[3] >> 5); + which = (ext[2] >> 1) & 0x1f; + none = !!(ext[2] & 0x40); + hhp = !(ext[2] & 0x80); + sx = ext[0] & 0x3f; + sy = ext[1] & 0x3f; + o = !(ext[5] & 0x80); + r = !(ext[5] & 0x40); + y = !(ext[5] & 0x20); + g = !(ext[5] & 0x10); + b = !(ext[5] & 0x08); + bass = !(ext[5] & 0x04); + bm = !(ext[4] & 0x10); + bp = !(ext[4] & 0x04); + + if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) { + sx &= 0x3e; + sy &= 0x3e; + } + + wiimod_drums_report_pressure(wdata, none, which, pressure, + o, &wdata->state.pressure_drums[0], + ABS_HAT2Y, 0x0e); + wiimod_drums_report_pressure(wdata, none, which, pressure, + r, &wdata->state.pressure_drums[1], + ABS_HAT0X, 0x19); + wiimod_drums_report_pressure(wdata, none, which, pressure, + y, &wdata->state.pressure_drums[2], + ABS_HAT2X, 0x11); + wiimod_drums_report_pressure(wdata, none, which, pressure, + g, &wdata->state.pressure_drums[3], + ABS_HAT1X, 0x12); + wiimod_drums_report_pressure(wdata, none, which, pressure, + b, &wdata->state.pressure_drums[4], + ABS_HAT0Y, 0x0f); + + /* Bass shares pressure with hi-hat (set via hhp) */ + wiimod_drums_report_pressure(wdata, none, hhp ? 0xff : which, pressure, + bass, &wdata->state.pressure_drums[5], + ABS_HAT3X, 0x1b); + /* Hi-hat has no on/off values, just pressure. Force to off/0. */ + wiimod_drums_report_pressure(wdata, none, hhp ? which : 0xff, pressure, + 0, &wdata->state.pressure_drums[6], + ABS_HAT3Y, 0x0e); + + input_report_abs(wdata->extension.input, ABS_X, sx - 0x20); + input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20); + + input_report_key(wdata->extension.input, BTN_START, bp); + input_report_key(wdata->extension.input, BTN_SELECT, bm); + + input_sync(wdata->extension.input); +} + +static int wiimod_drums_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_drums_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_drums_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret; + + wdata->extension.input = input_allocate_device(); + if (!wdata->extension.input) + return -ENOMEM; + + input_set_drvdata(wdata->extension.input, wdata); + wdata->extension.input->open = wiimod_drums_open; + wdata->extension.input->close = wiimod_drums_close; + wdata->extension.input->dev.parent = &wdata->hdev->dev; + wdata->extension.input->id.bustype = wdata->hdev->bus; + wdata->extension.input->id.vendor = wdata->hdev->vendor; + wdata->extension.input->id.product = wdata->hdev->product; + wdata->extension.input->id.version = wdata->hdev->version; + wdata->extension.input->name = WIIMOTE_NAME " Drums"; + + set_bit(EV_KEY, wdata->extension.input->evbit); + set_bit(BTN_START, wdata->extension.input->keybit); + set_bit(BTN_SELECT, wdata->extension.input->keybit); + + set_bit(EV_ABS, wdata->extension.input->evbit); + set_bit(ABS_X, wdata->extension.input->absbit); + set_bit(ABS_Y, wdata->extension.input->absbit); + set_bit(ABS_HAT0X, wdata->extension.input->absbit); + set_bit(ABS_HAT0Y, wdata->extension.input->absbit); + set_bit(ABS_HAT1X, wdata->extension.input->absbit); + set_bit(ABS_HAT2X, wdata->extension.input->absbit); + set_bit(ABS_HAT2Y, wdata->extension.input->absbit); + set_bit(ABS_HAT3X, wdata->extension.input->absbit); + set_bit(ABS_HAT3Y, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_X, -32, 31, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_Y, -32, 31, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_HAT0X, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_HAT0Y, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_HAT1X, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_HAT2X, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_HAT2Y, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_HAT3X, 0, 7, 0, 0); + input_set_abs_params(wdata->extension.input, + ABS_HAT3Y, 0, 7, 0, 0); + + ret = input_register_device(wdata->extension.input); + if (ret) + goto err_free; + + return 0; + +err_free: + input_free_device(wdata->extension.input); + wdata->extension.input = NULL; + return ret; +} + +static void wiimod_drums_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->extension.input) + return; + + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; +} + +static const struct wiimod_ops wiimod_drums = { + .flags = 0, + .arg = 0, + .probe = wiimod_drums_probe, + .remove = wiimod_drums_remove, + .in_ext = wiimod_drums_in_ext, +}; + +/* + * Guitar + * Guitar-Hero, Rock-Band and other games came bundled with guitars which can + * be plugged as extension to a Wiimote. + * We create a separate device for guitars and report all information via this + * input device. + */ + +enum wiimod_guitar_keys { + WIIMOD_GUITAR_KEY_G, + WIIMOD_GUITAR_KEY_R, + WIIMOD_GUITAR_KEY_Y, + WIIMOD_GUITAR_KEY_B, + WIIMOD_GUITAR_KEY_O, + WIIMOD_GUITAR_KEY_UP, + WIIMOD_GUITAR_KEY_DOWN, + WIIMOD_GUITAR_KEY_PLUS, + WIIMOD_GUITAR_KEY_MINUS, + WIIMOD_GUITAR_KEY_NUM, +}; + +static const __u16 wiimod_guitar_map[] = { + BTN_1, /* WIIMOD_GUITAR_KEY_G */ + BTN_2, /* WIIMOD_GUITAR_KEY_R */ + BTN_3, /* WIIMOD_GUITAR_KEY_Y */ + BTN_4, /* WIIMOD_GUITAR_KEY_B */ + BTN_5, /* WIIMOD_GUITAR_KEY_O */ + BTN_DPAD_UP, /* WIIMOD_GUITAR_KEY_UP */ + BTN_DPAD_DOWN, /* WIIMOD_GUITAR_KEY_DOWN */ + BTN_START, /* WIIMOD_GUITAR_KEY_PLUS */ + BTN_SELECT, /* WIIMOD_GUITAR_KEY_MINUS */ +}; + +static void wiimod_guitar_in_ext(struct wiimote_data *wdata, const __u8 *ext) +{ + __u8 sx, sy, tb, wb, bd, bm, bp, bo, br, bb, bg, by, bu; + + /* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | 0 | 0 | SX <5:0> | + * 2 | 0 | 0 | SY <5:0> | + * -----+-----+-----+-----+-----------------------------+ + * 3 | 0 | 0 | 0 | TB <4:0> | + * -----+-----+-----+-----+-----------------------------+ + * 4 | 0 | 0 | 0 | WB <4:0> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | 1 | BD | 1 | B- | 1 | B+ | 1 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | BO | BR | BB | BG | BY | 1 | 1 | BU | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * All buttons are 0 if pressed + * + * With Motion+ enabled, it will look like this: + * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | 0 | 0 | SX <5:1> | BU | + * 2 | 0 | 0 | SY <5:1> | 1 | + * -----+-----+-----+-----+-----------------------+-----+ + * 3 | 0 | 0 | 0 | TB <4:0> | + * -----+-----+-----+-----+-----------------------------+ + * 4 | 0 | 0 | 0 | WB <4:0> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | 1 | BD | 1 | B- | 1 | B+ | 1 |XXXXX| + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | BO | BR | BB | BG | BY | 1 |XXXXX|XXXXX| + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + */ + + sx = ext[0] & 0x3f; + sy = ext[1] & 0x3f; + tb = ext[2] & 0x1f; + wb = ext[3] & 0x1f; + bd = !(ext[4] & 0x40); + bm = !(ext[4] & 0x10); + bp = !(ext[4] & 0x04); + bo = !(ext[5] & 0x80); + br = !(ext[5] & 0x40); + bb = !(ext[5] & 0x20); + bg = !(ext[5] & 0x10); + by = !(ext[5] & 0x08); + bu = !(ext[5] & 0x01); + + if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) { + bu = !(ext[0] & 0x01); + sx &= 0x3e; + sy &= 0x3e; + } + + input_report_abs(wdata->extension.input, ABS_X, sx - 0x20); + input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20); + input_report_abs(wdata->extension.input, ABS_HAT0X, tb); + input_report_abs(wdata->extension.input, ABS_HAT1X, wb - 0x10); + + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_G], + bg); + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_R], + br); + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_Y], + by); + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_B], + bb); + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_O], + bo); + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_UP], + bu); + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_DOWN], + bd); + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_PLUS], + bp); + input_report_key(wdata->extension.input, + wiimod_guitar_map[WIIMOD_GUITAR_KEY_MINUS], + bm); + + input_sync(wdata->extension.input); +} + +static int wiimod_guitar_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_guitar_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_guitar_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret, i; + + wdata->extension.input = input_allocate_device(); + if (!wdata->extension.input) + return -ENOMEM; + + input_set_drvdata(wdata->extension.input, wdata); + wdata->extension.input->open = wiimod_guitar_open; + wdata->extension.input->close = wiimod_guitar_close; + wdata->extension.input->dev.parent = &wdata->hdev->dev; + wdata->extension.input->id.bustype = wdata->hdev->bus; + wdata->extension.input->id.vendor = wdata->hdev->vendor; + wdata->extension.input->id.product = wdata->hdev->product; + wdata->extension.input->id.version = wdata->hdev->version; + wdata->extension.input->name = WIIMOTE_NAME " Guitar"; + + set_bit(EV_KEY, wdata->extension.input->evbit); + for (i = 0; i < WIIMOD_GUITAR_KEY_NUM; ++i) + set_bit(wiimod_guitar_map[i], + wdata->extension.input->keybit); + + set_bit(EV_ABS, wdata->extension.input->evbit); + set_bit(ABS_X, wdata->extension.input->absbit); + set_bit(ABS_Y, wdata->extension.input->absbit); + set_bit(ABS_HAT0X, wdata->extension.input->absbit); + set_bit(ABS_HAT1X, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_X, -32, 31, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_Y, -32, 31, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_HAT0X, 0, 0x1f, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_HAT1X, 0, 0x0f, 1, 1); + + ret = input_register_device(wdata->extension.input); + if (ret) + goto err_free; + + return 0; + +err_free: + input_free_device(wdata->extension.input); + wdata->extension.input = NULL; + return ret; +} + +static void wiimod_guitar_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->extension.input) + return; + + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; +} + +static const struct wiimod_ops wiimod_guitar = { + .flags = 0, + .arg = 0, + .probe = wiimod_guitar_probe, + .remove = wiimod_guitar_remove, + .in_ext = wiimod_guitar_in_ext, +}; + /* * Builtin Motion Plus * This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which @@ -2201,4 +2639,6 @@ const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = { [WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic, [WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard, [WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro, + [WIIMOTE_EXT_DRUMS] = &wiimod_drums, + [WIIMOTE_EXT_GUITAR] = &wiimod_guitar, }; diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 510ca77fe14e..3bf3d3cc1c38 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -89,6 +89,8 @@ enum wiimote_exttype { WIIMOTE_EXT_CLASSIC_CONTROLLER, WIIMOTE_EXT_BALANCE_BOARD, WIIMOTE_EXT_PRO_CONTROLLER, + WIIMOTE_EXT_DRUMS, + WIIMOTE_EXT_GUITAR, WIIMOTE_EXT_NUM, }; @@ -137,6 +139,7 @@ struct wiimote_state { /* calibration/cache data */ __u16 calib_bboard[4][3]; __s16 calib_pro_sticks[4]; + __u8 pressure_drums[7]; __u8 cache_rumble; }; -- cgit v1.2.3 From 29b9e14846f1ff201c4c1ba4fdb868dcdce6c760 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Mon, 25 Jun 2018 13:24:34 -0700 Subject: HID: wacom: Replace touch_max fixup code with static touch_max definitions Detecting the number of supported touches for a particular device used to be tricky, both because early forms of the driver didn't have a very good HID parser and because early hardware didn't always advertise the actual number. At the time, we added a block of code which would ensure that touch_max would always be equal to at least 1 on any touch device, and relied on setting touch_max to e.g. 2 only for the multitouch-capable exceptions. The common case has since flipped, and the driver and descriptors can reliably detect the number of touches supported by modern sensors. Because of this, it makes sense to remove the fixup code and instead place static declarations of "touch_max = 1" for these old devices. It isn't entirely clear if all 2-finger devices actually report a maximum number of touches so we leave these declarations still in place. For the eagle-eyed, the "> BAMBOO_PT" condition was originally equivalent to ">= TABLETPC", which is what the intent was. This commit doesn't have to consider the types introduced in the interim since they shouldn't be affected, hence why only the tablet PC definitions have been modified. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 10 ---------- drivers/hid/wacom_wac.c | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index d6797535fff9..6b67c6907caa 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -361,16 +361,6 @@ static void wacom_usage_mapping(struct hid_device *hdev, else return; - /* - * Bamboo models do not support HID_DG_CONTACTMAX. - * And, Bamboo Pen only descriptor contains touch. - */ - if (features->type > BAMBOO_PT) { - /* ISDv4 touch devices at least supports one touch point */ - if (finger && !features->touch_max) - features->touch_max = 1; - } - /* * ISDv4 devices which predate HID's adoption of the * HID_DG_BARELSWITCH2 usage use 0x000D0000 in its diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 0bb44d0088ed..e0842241f692 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -4351,19 +4351,19 @@ static const struct wacom_features wacom_features_0x5E = .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x90 = { "Wacom ISDv4 90", 26202, 16325, 255, 0, - TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */ static const struct wacom_features wacom_features_0x93 = { "Wacom ISDv4 93", 26202, 16325, 255, 0, - TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 }; static const struct wacom_features wacom_features_0x97 = { "Wacom ISDv4 97", 26202, 16325, 511, 0, - TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */ static const struct wacom_features wacom_features_0x9A = { "Wacom ISDv4 9A", 26202, 16325, 255, 0, - TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 }; static const struct wacom_features wacom_features_0x9F = { "Wacom ISDv4 9F", 26202, 16325, 255, 0, - TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 }; static const struct wacom_features wacom_features_0xE2 = { "Wacom ISDv4 E2", 26202, 16325, 255, 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; @@ -4378,13 +4378,13 @@ static const struct wacom_features wacom_features_0xE6 = TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; static const struct wacom_features wacom_features_0xEC = { "Wacom ISDv4 EC", 25710, 14500, 255, 0, - TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */ static const struct wacom_features wacom_features_0xED = { "Wacom ISDv4 ED", 26202, 16325, 255, 0, - TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 }; static const struct wacom_features wacom_features_0xEF = { "Wacom ISDv4 EF", 26202, 16325, 255, 0, - TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */ static const struct wacom_features wacom_features_0x100 = { "Wacom ISDv4 100", 26202, 16325, 255, 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -4402,10 +4402,10 @@ static const struct wacom_features wacom_features_0x10F = MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x116 = { "Wacom ISDv4 116", 26202, 16325, 255, 0, - TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 }; static const struct wacom_features wacom_features_0x12C = { "Wacom ISDv4 12C", 27848, 15752, 2047, 0, - TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */ static const struct wacom_features wacom_features_0x4001 = { "Wacom ISDv4 4001", 26202, 16325, 255, 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -- cgit v1.2.3 From 578325120ec122db98b3d57e833d16c1bdac6ec6 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Mon, 25 Jun 2018 13:24:35 -0700 Subject: HID: wacom: Move handling of HID quirks into a dedicated function We want to keep device-specific quirks as contained as possible so that the the code remains maintainable. Our 'wacom_setup_device_quirks' function is the usual place for this, but some quirks need to be applied to the HID descriptor as it is parsed. This commit introduces a new function which is called for each usage so that any HID-specific quirks can be applied. The function now houses quirks that were being done in 'wacom_feature_mapping' and 'wacom_usage_mapping'. Signed-off-by: Jason Gerecke Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 92 +++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 6b67c6907caa..3a4cf2666a7c 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -210,6 +210,57 @@ static int wacom_calc_hid_res(int logical_extents, int physical_extents, return hidinput_calc_abs_res(&field, ABS_X); } +static void wacom_hid_usage_quirk(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_features *features = &wacom->wacom_wac.features; + unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid); + + /* + * The Dell Canvas 27 needs to be switched to its vendor-defined + * report to provide the best resolution. + */ + if (hdev->vendor == USB_VENDOR_ID_WACOM && + hdev->product == 0x4200 && + field->application == HID_UP_MSVENDOR) { + wacom->wacom_wac.mode_report = field->report->id; + wacom->wacom_wac.mode_value = 2; + } + + /* + * ISDv4 devices which predate HID's adoption of the + * HID_DG_BARELSWITCH2 usage use 0x000D0000 in its + * position instead. We can accurately detect if a + * usage with that value should be HID_DG_BARRELSWITCH2 + * based on the surrounding usages, which have remained + * constant across generations. + */ + if (features->type == HID_GENERIC && + usage->hid == 0x000D0000 && + field->application == HID_DG_PEN && + field->physical == HID_DG_STYLUS) { + int i = usage->usage_index; + + if (i-4 >= 0 && i+1 < field->maxusage && + field->usage[i-4].hid == HID_DG_TIPSWITCH && + field->usage[i-3].hid == HID_DG_BARRELSWITCH && + field->usage[i-2].hid == HID_DG_ERASER && + field->usage[i-1].hid == HID_DG_INVERT && + field->usage[i+1].hid == HID_DG_INRANGE) { + usage->hid = HID_DG_BARRELSWITCH2; + } + } + + /* 2nd-generation Intuos Pro Large has incorrect Y maximum */ + if (hdev->vendor == USB_VENDOR_ID_WACOM && + hdev->product == 0x0358 && + WACOM_PEN_FIELD(field) && + equivalent_usage == HID_GD_Y) { + field->logical_maximum = 43200; + } +} + static void wacom_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { @@ -221,6 +272,8 @@ static void wacom_feature_mapping(struct hid_device *hdev, int ret; u32 n; + wacom_hid_usage_quirk(hdev, field, usage); + switch (equivalent_usage) { case HID_DG_CONTACTMAX: /* leave touch_max as is if predefined */ @@ -300,13 +353,6 @@ static void wacom_feature_mapping(struct hid_device *hdev, kfree(data); break; } - - if (hdev->vendor == USB_VENDOR_ID_WACOM && - hdev->product == 0x4200 /* Dell Canvas 27 */ && - field->application == HID_UP_MSVENDOR) { - wacom->wacom_wac.mode_report = field->report->id; - wacom->wacom_wac.mode_value = 2; - } } /* @@ -361,37 +407,7 @@ static void wacom_usage_mapping(struct hid_device *hdev, else return; - /* - * ISDv4 devices which predate HID's adoption of the - * HID_DG_BARELSWITCH2 usage use 0x000D0000 in its - * position instead. We can accurately detect if a - * usage with that value should be HID_DG_BARRELSWITCH2 - * based on the surrounding usages, which have remained - * constant across generations. - */ - if (features->type == HID_GENERIC && - usage->hid == 0x000D0000 && - field->application == HID_DG_PEN && - field->physical == HID_DG_STYLUS) { - int i = usage->usage_index; - - if (i-4 >= 0 && i+1 < field->maxusage && - field->usage[i-4].hid == HID_DG_TIPSWITCH && - field->usage[i-3].hid == HID_DG_BARRELSWITCH && - field->usage[i-2].hid == HID_DG_ERASER && - field->usage[i-1].hid == HID_DG_INVERT && - field->usage[i+1].hid == HID_DG_INRANGE) { - usage->hid = HID_DG_BARRELSWITCH2; - } - } - - /* 2nd-generation Intuos Pro Large has incorrect Y maximum */ - if (hdev->vendor == USB_VENDOR_ID_WACOM && - hdev->product == 0x0358 && - WACOM_PEN_FIELD(field) && - wacom_equivalent_usage(usage->hid) == HID_GD_Y) { - field->logical_maximum = 43200; - } + wacom_hid_usage_quirk(hdev, field, usage); switch (usage->hid) { case HID_GD_X: -- cgit v1.2.3 From 418b573b431310306db31e445594317ba62f5508 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Mon, 25 Jun 2018 13:24:36 -0700 Subject: HID: wacom: convert Wacom custom usages to standard HID usages Otherwise the switch would not catch the proper usages. Signed-off-by: Ping Cheng Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 3a4cf2666a7c..52e85d21e40f 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -394,6 +394,7 @@ static void wacom_usage_mapping(struct hid_device *hdev, struct wacom_features *features = &wacom->wacom_wac.features; bool finger = WACOM_FINGER_FIELD(field); bool pen = WACOM_PEN_FIELD(field); + unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); /* * Requiring Stylus Usage will ignore boot mouse @@ -409,7 +410,7 @@ static void wacom_usage_mapping(struct hid_device *hdev, wacom_hid_usage_quirk(hdev, field, usage); - switch (usage->hid) { + switch (equivalent_usage) { case HID_GD_X: features->x_max = field->logical_maximum; if (finger) { -- cgit v1.2.3 From cc070a847bc0b6fb66e4c05b5bd036d2ea80fb9a Mon Sep 17 00:00:00 2001 From: Hanno Zulla Date: Thu, 14 Jun 2018 16:29:08 +0200 Subject: HID: hid-sony.c: Use devm_ api to simplify sony_register_touchpad() HID: hid-sony.c: Use devm_ api to simplify sony_register_touchpad() Using devm_ calls, the resources of the Sony game devices's features are tied to the main device handle, making it easier to handle errors and teardown inside the device driver. Altogether, this reduces complexity of the driver source. Signed-off-by: Hanno Zulla Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 40 ++++++---------------------------------- 1 file changed, 6 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index e475c5073c99..f14286e62e15 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1353,7 +1353,7 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count, char *name; int ret; - sc->touchpad = input_allocate_device(); + sc->touchpad = devm_input_allocate_device(&sc->hdev->dev); if (!sc->touchpad) return -ENOMEM; @@ -1370,11 +1370,9 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count, * DS4 compatible non-Sony devices with different names. */ name_sz = strlen(sc->hdev->name) + sizeof(DS4_TOUCHPAD_SUFFIX); - name = kzalloc(name_sz, GFP_KERNEL); - if (!name) { - ret = -ENOMEM; - goto err; - } + name = devm_kzalloc(&sc->hdev->dev, name_sz, GFP_KERNEL); + if (!name) + return -ENOMEM; snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name); sc->touchpad->name = name; @@ -1403,34 +1401,13 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count, ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER); if (ret < 0) - goto err; + return ret; ret = input_register_device(sc->touchpad); if (ret < 0) - goto err; + return ret; return 0; - -err: - kfree(sc->touchpad->name); - sc->touchpad->name = NULL; - - input_free_device(sc->touchpad); - sc->touchpad = NULL; - - return ret; -} - -static void sony_unregister_touchpad(struct sony_sc *sc) -{ - if (!sc->touchpad) - return; - - kfree(sc->touchpad->name); - sc->touchpad->name = NULL; - - input_unregister_device(sc->touchpad); - sc->touchpad = NULL; } static int sony_register_sensors(struct sony_sc *sc) @@ -2883,8 +2860,6 @@ err_stop: sony_leds_remove(sc); if (sc->quirks & SONY_BATTERY_SUPPORT) sony_battery_remove(sc); - if (sc->touchpad) - sony_unregister_touchpad(sc); if (sc->sensor_dev) sony_unregister_sensors(sc); sony_cancel_work_sync(sc); @@ -2971,9 +2946,6 @@ static void sony_remove(struct hid_device *hdev) if (sc->quirks & SONY_BATTERY_SUPPORT) sony_battery_remove(sc); - if (sc->touchpad) - sony_unregister_touchpad(sc); - if (sc->sensor_dev) sony_unregister_sensors(sc); -- cgit v1.2.3 From ea4a5fdc8d07df9115881f698c14bcfd7046a846 Mon Sep 17 00:00:00 2001 From: Hanno Zulla Date: Thu, 14 Jun 2018 16:30:02 +0200 Subject: HID: hid-sony.c: Use devm_ api to simplify sony_register_sensors() [PATCH 2/5] HID: hid-sony.c: Use devm_ api to simplify sony_register_sensors() Using devm_ calls, the resources of the Sony game devices's features are tied to the main device handle, making it easier to handle errors and teardown inside the device driver. Altogether, this reduces complexity of the driver source. Signed-off-by: Hanno Zulla Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 39 +++++---------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index f14286e62e15..ebad1381e177 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1417,7 +1417,7 @@ static int sony_register_sensors(struct sony_sc *sc) int ret; int range; - sc->sensor_dev = input_allocate_device(); + sc->sensor_dev = devm_input_allocate_device(&sc->hdev->dev); if (!sc->sensor_dev) return -ENOMEM; @@ -1434,11 +1434,9 @@ static int sony_register_sensors(struct sony_sc *sc) * DS4 compatible non-Sony devices with different names. */ name_sz = strlen(sc->hdev->name) + sizeof(SENSOR_SUFFIX); - name = kzalloc(name_sz, GFP_KERNEL); - if (!name) { - ret = -ENOMEM; - goto err; - } + name = devm_kzalloc(&sc->hdev->dev, name_sz, GFP_KERNEL); + if (!name) + return -ENOMEM; snprintf(name, name_sz, "%s" SENSOR_SUFFIX, sc->hdev->name); sc->sensor_dev->name = name; @@ -1480,33 +1478,11 @@ static int sony_register_sensors(struct sony_sc *sc) ret = input_register_device(sc->sensor_dev); if (ret < 0) - goto err; + return ret; return 0; - -err: - kfree(sc->sensor_dev->name); - sc->sensor_dev->name = NULL; - - input_free_device(sc->sensor_dev); - sc->sensor_dev = NULL; - - return ret; } -static void sony_unregister_sensors(struct sony_sc *sc) -{ - if (!sc->sensor_dev) - return; - - kfree(sc->sensor_dev->name); - sc->sensor_dev->name = NULL; - - input_unregister_device(sc->sensor_dev); - sc->sensor_dev = NULL; -} - - /* * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller * to "operational". Without this, the ps3 controller will not report any @@ -2860,8 +2836,6 @@ err_stop: sony_leds_remove(sc); if (sc->quirks & SONY_BATTERY_SUPPORT) sony_battery_remove(sc); - if (sc->sensor_dev) - sony_unregister_sensors(sc); sony_cancel_work_sync(sc); kfree(sc->output_report_dmabuf); sony_remove_dev_list(sc); @@ -2946,9 +2920,6 @@ static void sony_remove(struct hid_device *hdev) if (sc->quirks & SONY_BATTERY_SUPPORT) sony_battery_remove(sc); - if (sc->sensor_dev) - sony_unregister_sensors(sc); - if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval); -- cgit v1.2.3 From f2d98e2c020e8f33813a746ac8a92a4582ac416a Mon Sep 17 00:00:00 2001 From: Hanno Zulla Date: Thu, 14 Jun 2018 16:30:46 +0200 Subject: HID: hid-sony.c: Use devm_ api to simplify sony_leds_init() [PATCH 3/5] HID: hid-sony.c: Use devm_ api to simplify sony_leds_init() Using devm_ calls, the resources of the Sony game devices's features are tied to the main device handle, making it easier to handle errors and teardown inside the device driver. Altogether, this reduces complexity of the driver source. Signed-off-by: Hanno Zulla Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index ebad1381e177..7c1c56d13122 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1940,25 +1940,6 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on, return 0; } -static void sony_leds_remove(struct sony_sc *sc) -{ - struct led_classdev *led; - int n; - - BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); - - for (n = 0; n < sc->led_count; n++) { - led = sc->leds[n]; - sc->leds[n] = NULL; - if (!led) - continue; - led_classdev_unregister(led); - kfree(led); - } - - sc->led_count = 0; -} - static int sony_leds_init(struct sony_sc *sc) { struct hid_device *hdev = sc->hdev; @@ -2031,11 +2012,10 @@ static int sony_leds_init(struct sony_sc *sc) if (use_ds4_names) name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2; - led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); + led = devm_kzalloc(&hdev->dev, sizeof(struct led_classdev) + name_sz, GFP_KERNEL); if (!led) { hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); - ret = -ENOMEM; - goto error_leds; + return -ENOMEM; } name = (void *)(&led[1]); @@ -2056,21 +2036,14 @@ static int sony_leds_init(struct sony_sc *sc) sc->leds[n] = led; - ret = led_classdev_register(&hdev->dev, led); + ret = devm_led_classdev_register(&hdev->dev, led); if (ret) { hid_err(hdev, "Failed to register LED %d\n", n); - sc->leds[n] = NULL; - kfree(led); - goto error_leds; + return ret; } } - return ret; - -error_leds: - sony_leds_remove(sc); - - return ret; + return 0; } static void sixaxis_send_output_report(struct sony_sc *sc) @@ -2832,8 +2805,6 @@ err_stop: device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version); if (sc->hw_version) device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version); - if (sc->quirks & SONY_LED_SUPPORT) - sony_leds_remove(sc); if (sc->quirks & SONY_BATTERY_SUPPORT) sony_battery_remove(sc); sony_cancel_work_sync(sc); @@ -2914,9 +2885,6 @@ static void sony_remove(struct hid_device *hdev) hid_hw_close(hdev); - if (sc->quirks & SONY_LED_SUPPORT) - sony_leds_remove(sc); - if (sc->quirks & SONY_BATTERY_SUPPORT) sony_battery_remove(sc); -- cgit v1.2.3 From 8082d3f02261adbdc4eb3301fb99960162c3d142 Mon Sep 17 00:00:00 2001 From: Hanno Zulla Date: Thu, 14 Jun 2018 16:31:36 +0200 Subject: HID: hid-sony.c: Use devm_ api to simplify sony_battery_probe() HID: hid-sony.c: Use devm_ api to simplify sony_battery_probe() Using devm_ calls, the resources of the Sony game devices's features are tied to the main device handle, making it easier to handle errors and teardown inside the device driver. Altogether, this reduces complexity of the driver source. Signed-off-by: Hanno Zulla Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 7c1c56d13122..9abdd77f16c9 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -2318,36 +2318,21 @@ static int sony_battery_probe(struct sony_sc *sc, int append_dev_id) sc->battery_desc.get_property = sony_battery_get_property; sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; sc->battery_desc.use_for_apm = 0; - sc->battery_desc.name = kasprintf(GFP_KERNEL, battery_str_fmt, - sc->mac_address, sc->device_id); + sc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + battery_str_fmt, sc->mac_address, sc->device_id); if (!sc->battery_desc.name) return -ENOMEM; - sc->battery = power_supply_register(&hdev->dev, &sc->battery_desc, + sc->battery = devm_power_supply_register(&hdev->dev, &sc->battery_desc, &psy_cfg); if (IS_ERR(sc->battery)) { ret = PTR_ERR(sc->battery); hid_err(hdev, "Unable to register battery device\n"); - goto err_free; + return ret; } power_supply_powers(sc->battery, &hdev->dev); return 0; - -err_free: - kfree(sc->battery_desc.name); - sc->battery_desc.name = NULL; - return ret; -} - -static void sony_battery_remove(struct sony_sc *sc) -{ - if (!sc->battery_desc.name) - return; - - power_supply_unregister(sc->battery); - kfree(sc->battery_desc.name); - sc->battery_desc.name = NULL; } /* @@ -2805,8 +2790,6 @@ err_stop: device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version); if (sc->hw_version) device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version); - if (sc->quirks & SONY_BATTERY_SUPPORT) - sony_battery_remove(sc); sony_cancel_work_sync(sc); kfree(sc->output_report_dmabuf); sony_remove_dev_list(sc); @@ -2885,9 +2868,6 @@ static void sony_remove(struct hid_device *hdev) hid_hw_close(hdev); - if (sc->quirks & SONY_BATTERY_SUPPORT) - sony_battery_remove(sc); - if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval); -- cgit v1.2.3 From ea58c33d4d85504b4255dd74999603a6793bc56a Mon Sep 17 00:00:00 2001 From: Hanno Zulla Date: Thu, 14 Jun 2018 16:32:16 +0200 Subject: HID: hid-sony.c: Use devm_ api to simplify sc->output_report_dmabuf HID: hid-sony.c: Use devm_ api to simplify sc->output_report_dmabuf Using devm_ calls, the resources of the Sony game devices's features are tied to the main device handle, making it easier to handle errors and teardown inside the device driver. Altogether, this reduces complexity of the driver source. Signed-off-by: Hanno Zulla Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 9abdd77f16c9..9671a4bad643 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -2202,16 +2202,20 @@ static int sony_allocate_output_report(struct sony_sc *sc) if ((sc->quirks & SIXAXIS_CONTROLLER) || (sc->quirks & NAVIGATION_CONTROLLER)) sc->output_report_dmabuf = - kmalloc(sizeof(union sixaxis_output_report_01), + devm_kmalloc(&sc->hdev->dev, + sizeof(union sixaxis_output_report_01), GFP_KERNEL); else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) - sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x11_SIZE, + sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev, + DS4_OUTPUT_REPORT_0x11_SIZE, GFP_KERNEL); else if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) - sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x05_SIZE, + sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev, + DS4_OUTPUT_REPORT_0x05_SIZE, GFP_KERNEL); else if (sc->quirks & MOTION_CONTROLLER) - sc->output_report_dmabuf = kmalloc(MOTION_REPORT_0x02_SIZE, + sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev, + MOTION_REPORT_0x02_SIZE, GFP_KERNEL); else return 0; @@ -2791,7 +2795,6 @@ err_stop: if (sc->hw_version) device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version); sony_cancel_work_sync(sc); - kfree(sc->output_report_dmabuf); sony_remove_dev_list(sc); sony_release_device_id(sc); hid_hw_stop(hdev); @@ -2879,8 +2882,6 @@ static void sony_remove(struct hid_device *hdev) sony_cancel_work_sync(sc); - kfree(sc->output_report_dmabuf); - sony_remove_dev_list(sc); sony_release_device_id(sc); -- cgit v1.2.3 From c4cf2d8df1dd9061cdc873e0ddb41deafa228b9d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:29 +0200 Subject: HID: elan: Remove unused max_area_x and max_area_y vatiables max_area_x and max_area_y are initialized but never used anywhere, drop them. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 803a725785cf..966fe36fab41 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -31,8 +31,6 @@ struct elan_touchpad_settings { u8 max_fingers; u16 max_x; u16 max_y; - u8 max_area_x; - u8 max_area_y; u8 max_w; int usb_bInterfaceNumber; }; @@ -390,8 +388,6 @@ static const struct elan_touchpad_settings hp_x2_10_touchpad_data = { .max_fingers = 5, .max_x = 2930, .max_y = 1250, - .max_area_x = 15, - .max_area_y = 15, .max_w = 255, .usb_bInterfaceNumber = 1, }; -- cgit v1.2.3 From f109b43ad9af6b5a003b6619cb2242fde19009ce Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:30 +0200 Subject: HID: elan: Stop claiming we have TOUCH_MAJOR and then never reporting it We never report MT_TOUCH_MAJOR, so lets not claim that we do. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 966fe36fab41..907b724b9435 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -92,8 +92,6 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) drvdata->settings->max_x, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, drvdata->settings->max_y, 0, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, - drvdata->settings->max_fingers, 0, 0); input_set_abs_params(input, ABS_TOOL_WIDTH, 0, drvdata->settings->max_w, 0, 0); -- cgit v1.2.3 From 2f612de2d67dca26191e6c3db544f4c30beb6838 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:31 +0200 Subject: HID: elan: Correctly report MT_PRESSURE instead of TOOL_WIDTH Elan has given me a (GPL-ed) Android driver for their non HID-mt touchpads to help improve the upstream support. Acoording to Elan what we are currently reporting as tool-width really is a per-touch pressure. This always has a maximum of 255, so there is no need to make the max configurable. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 907b724b9435..d0971f5e5523 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -23,6 +23,7 @@ #define ELAN_MT_FIRST_FINGER 0x82 #define ELAN_MT_SECOND_FINGER 0x83 #define ELAN_INPUT_REPORT_SIZE 8 +#define ELAN_MAX_PRESSURE 255 #define ELAN_MUTE_LED_REPORT 0xBC #define ELAN_LED_REPORT_SIZE 8 @@ -31,7 +32,6 @@ struct elan_touchpad_settings { u8 max_fingers; u16 max_x; u16 max_y; - u8 max_w; int usb_bInterfaceNumber; }; @@ -92,8 +92,8 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) drvdata->settings->max_x, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, drvdata->settings->max_y, 0, 0); - input_set_abs_params(input, ABS_TOOL_WIDTH, 0, - drvdata->settings->max_w, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, ELAN_MAX_PRESSURE, + 0, 0); __set_bit(BTN_LEFT, input->keybit); __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); @@ -122,7 +122,7 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data, unsigned int slot_num) { struct input_dev *input = drvdata->input; - int x, y, w; + int x, y, p; bool active = !!data; @@ -132,11 +132,11 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data, x = ((data[0] & 0xF0) << 4) | data[1]; y = drvdata->settings->max_y - (((data[0] & 0x07) << 8) | data[2]); - w = data[4]; + p = data[4]; input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - input_report_abs(input, ABS_TOOL_WIDTH, w); + input_report_abs(input, ABS_MT_PRESSURE, p); } } @@ -158,7 +158,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) * byte 5: x8 x7 x6 x5 x4 x3 x2 x1 * byte 6: y8 y7 y6 y5 y4 y3 y2 y1 * byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 - * byte 8: w8 w7 w6 w5 w4 w3 w2 w1 + * byte 8: p8 p7 p6 p5 p4 p3 p2 p1 * * packet structure for ELAN_MT_SECOND_FINGER: * @@ -167,15 +167,17 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) * byte 3: x8 x7 x6 x5 x4 x3 x2 x1 * byte 4: y8 y7 y6 y5 y4 y3 y2 y1 * byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 - * byte 6: w8 w7 w6 w5 w4 w3 w2 w1 + * byte 6: p8 p7 p6 p5 p4 p3 p2 p1 * byte 7: 0 0 0 0 0 0 0 0 * byte 8: 0 0 0 0 0 0 0 0 * * f5-f1: finger touch bits * L: clickpad button - * sy / sx: not sure yet, but this looks like rectangular - * area for finger - * w: looks like finger width + * sy / sx: finger width / height expressed in traces, the total number + * of traces can be queried by doing a HID_REQ_SET_REPORT + * { 0x0d, 0x05, 0x03, 0x05, 0x01 } followed by a GET, in the + * returned buf, buf[3]=no-x-traces, buf[4]=no-y-traces. + * p: pressure */ if (data[0] == ELAN_SINGLE_FINGER) { @@ -386,7 +388,6 @@ static const struct elan_touchpad_settings hp_x2_10_touchpad_data = { .max_fingers = 5, .max_x = 2930, .max_y = 1250, - .max_w = 255, .usb_bInterfaceNumber = 1, }; -- cgit v1.2.3 From 314f04e85cabf0027f098866897db57fdaf6cf19 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:32 +0200 Subject: HID: elan: Hardcode finger-count and usb-interface There is no need to have these configurable. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index d0971f5e5523..1a012d2b9fa1 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -23,16 +23,16 @@ #define ELAN_MT_FIRST_FINGER 0x82 #define ELAN_MT_SECOND_FINGER 0x83 #define ELAN_INPUT_REPORT_SIZE 8 +#define ELAN_MAX_FINGERS 5 #define ELAN_MAX_PRESSURE 255 +#define ELAN_TP_USB_INTF 1 #define ELAN_MUTE_LED_REPORT 0xBC #define ELAN_LED_REPORT_SIZE 8 struct elan_touchpad_settings { - u8 max_fingers; u16 max_x; u16 max_y; - int usb_bInterfaceNumber; }; struct elan_drvdata { @@ -46,9 +46,8 @@ struct elan_drvdata { static int is_not_elan_touchpad(struct hid_device *hdev) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct elan_drvdata *drvdata = hid_get_drvdata(hdev); - return (intf->altsetting->desc.bInterfaceNumber != drvdata->settings->usb_bInterfaceNumber); + return (intf->altsetting->desc.bInterfaceNumber != ELAN_TP_USB_INTF); } static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -98,8 +97,7 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) __set_bit(BTN_LEFT, input->keybit); __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); - ret = input_mt_init_slots(input, drvdata->settings->max_fingers, - INPUT_MT_POINTER); + ret = input_mt_init_slots(input, ELAN_MAX_FINGERS, INPUT_MT_POINTER); if (ret) { hid_err(hdev, "Failed to init elan MT slots: %d\n", ret); return ret; @@ -181,7 +179,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) */ if (data[0] == ELAN_SINGLE_FINGER) { - for (i = 0; i < drvdata->settings->max_fingers; i++) { + for (i = 0; i < ELAN_MAX_FINGERS; i++) { if (data[2] & BIT(i + 3)) elan_report_mt_slot(drvdata, data + 3, i); else @@ -208,7 +206,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) if (prev_report[0] != ELAN_MT_FIRST_FINGER) return; - for (i = 0; i < drvdata->settings->max_fingers; i++) { + for (i = 0; i < ELAN_MAX_FINGERS; i++) { if (prev_report[2] & BIT(i + 3)) { if (!first) { first = 1; @@ -385,10 +383,8 @@ static void elan_remove(struct hid_device *hdev) } static const struct elan_touchpad_settings hp_x2_10_touchpad_data = { - .max_fingers = 5, .max_x = 2930, .max_y = 1250, - .usb_bInterfaceNumber = 1, }; static const struct hid_device_id elan_devices[] = { -- cgit v1.2.3 From 79d11f2a2070613b63a620e4a8b87feb3e5543c3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:33 +0200 Subject: HID: elan: Query device max_x and max_y value from touchpad Query the device's max_x and max_y value from the touchpad rather then hardcoding it. This makes adding support for other USB ids a lot easier. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 95 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 1a012d2b9fa1..d696b57f1ccd 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -27,20 +27,22 @@ #define ELAN_MAX_PRESSURE 255 #define ELAN_TP_USB_INTF 1 +#define ELAN_FEATURE_REPORT 0x0d +#define ELAN_FEATURE_SIZE 5 +#define ELAN_PARAM_MAX_X 6 +#define ELAN_PARAM_MAX_Y 7 +#define ELAN_PARAM_RES 8 + #define ELAN_MUTE_LED_REPORT 0xBC #define ELAN_LED_REPORT_SIZE 8 -struct elan_touchpad_settings { - u16 max_x; - u16 max_y; -}; - struct elan_drvdata { struct input_dev *input; u8 prev_report[ELAN_INPUT_REPORT_SIZE]; struct led_classdev mute_led; u8 mute_led_state; - struct elan_touchpad_settings *settings; + u16 max_x; + u16 max_y; }; static int is_not_elan_touchpad(struct hid_device *hdev) @@ -65,6 +67,63 @@ static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } +static int elan_get_device_param(struct hid_device *hdev, + unsigned char *dmabuf, unsigned char param) +{ + int ret; + + dmabuf[0] = ELAN_FEATURE_REPORT; + dmabuf[1] = 0x05; + dmabuf[2] = 0x03; + dmabuf[3] = param; + dmabuf[4] = 0x01; + + ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf, + ELAN_FEATURE_SIZE, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); + if (ret != ELAN_FEATURE_SIZE) { + hid_err(hdev, "Set report error for parm %d: %d\n", param, ret); + return ret; + } + + ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf, + ELAN_FEATURE_SIZE, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + if (ret != ELAN_FEATURE_SIZE) { + hid_err(hdev, "Get report error for parm %d: %d\n", param, ret); + return ret; + } + + return 0; +} + +static int elan_get_device_params(struct hid_device *hdev) +{ + struct elan_drvdata *drvdata = hid_get_drvdata(hdev); + unsigned char *dmabuf; + int ret; + + dmabuf = kmalloc(ELAN_FEATURE_SIZE, GFP_KERNEL); + if (!dmabuf) + return -ENOMEM; + + ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_X); + if (ret) + goto err; + + drvdata->max_x = (dmabuf[4] << 8) | dmabuf[3]; + + ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_Y); + if (ret) + goto err; + + drvdata->max_y = (dmabuf[4] << 8) | dmabuf[3]; + +err: + kfree(dmabuf); + return ret; +} + static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) { int ret; @@ -74,6 +133,10 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) if (is_not_elan_touchpad(hdev)) return 0; + ret = elan_get_device_params(hdev); + if (ret) + return ret; + input = devm_input_allocate_device(&hdev->dev); if (!input) return -ENOMEM; @@ -87,10 +150,10 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) input->id.version = hdev->version; input->dev.parent = &hdev->dev; - input_set_abs_params(input, ABS_MT_POSITION_X, 0, - drvdata->settings->max_x, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, - drvdata->settings->max_y, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, drvdata->max_x, + 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, drvdata->max_y, + 0, 0); input_set_abs_params(input, ABS_MT_PRESSURE, 0, ELAN_MAX_PRESSURE, 0, 0); @@ -128,7 +191,7 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data, input_mt_report_slot_state(input, MT_TOOL_FINGER, active); if (active) { x = ((data[0] & 0xF0) << 4) | data[1]; - y = drvdata->settings->max_y - + y = drvdata->max_y - (((data[0] & 0x07) << 8) | data[2]); p = data[4]; @@ -339,7 +402,6 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id) if (!drvdata) return -ENOMEM; - drvdata->settings = (struct elan_touchpad_settings *)id->driver_data; hid_set_drvdata(hdev, drvdata); ret = hid_parse(hdev); @@ -382,17 +444,10 @@ static void elan_remove(struct hid_device *hdev) hid_hw_stop(hdev); } -static const struct elan_touchpad_settings hp_x2_10_touchpad_data = { - .max_x = 2930, - .max_y = 1250, -}; - static const struct hid_device_id elan_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER), - (kernel_ulong_t)&hp_x2_10_touchpad_data}, + { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER) }, { } }; - MODULE_DEVICE_TABLE(hid, elan_devices); static struct hid_driver elan_driver = { -- cgit v1.2.3 From 19588bee0ea569bd6cc6bea5b284c34f1f526a59 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:34 +0200 Subject: HID: elan: Query resolution from the touchpad Query the resolution from the touchpad and report it to userspace Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index d696b57f1ccd..6969fb5344a5 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -43,6 +43,8 @@ struct elan_drvdata { u8 mute_led_state; u16 max_x; u16 max_y; + u16 res_x; + u16 res_y; }; static int is_not_elan_touchpad(struct hid_device *hdev) @@ -97,6 +99,15 @@ static int elan_get_device_param(struct hid_device *hdev, return 0; } +static unsigned int elan_convert_res(char val) +{ + /* + * (value from firmware) * 10 + 790 = dpi + * dpi * 10 / 254 = dots/mm + */ + return (val * 10 + 790) * 10 / 254; +} + static int elan_get_device_params(struct hid_device *hdev) { struct elan_drvdata *drvdata = hid_get_drvdata(hdev); @@ -119,6 +130,13 @@ static int elan_get_device_params(struct hid_device *hdev) drvdata->max_y = (dmabuf[4] << 8) | dmabuf[3]; + ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_RES); + if (ret) + goto err; + + drvdata->res_x = elan_convert_res(dmabuf[3]); + drvdata->res_y = elan_convert_res(dmabuf[4]); + err: kfree(dmabuf); return ret; @@ -166,6 +184,9 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) return ret; } + input_abs_set_res(input, ABS_X, drvdata->res_x); + input_abs_set_res(input, ABS_Y, drvdata->res_y); + ret = input_register_device(input); if (ret) { hid_err(hdev, "Failed to register elan input device: %d\n", -- cgit v1.2.3 From 7ed3f281d8f36c19ca15fe7fcc52200668a73d72 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:35 +0200 Subject: HID: elan: Add a flag for selecting if the touchpad has a LED Note all Elan touchpads have a LED make this configurable using a flag in hi_id.driver_data. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 6969fb5344a5..ae1e24061c0a 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -36,6 +36,8 @@ #define ELAN_MUTE_LED_REPORT 0xBC #define ELAN_LED_REPORT_SIZE 8 +#define ELAN_HAS_LED BIT(0) + struct elan_drvdata { struct input_dev *input; u8 prev_report[ELAN_INPUT_REPORT_SIZE]; @@ -450,9 +452,11 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret) goto err; - ret = elan_init_mute_led(hdev); - if (ret) - goto err; + if (id->driver_data & ELAN_HAS_LED) { + ret = elan_init_mute_led(hdev); + if (ret) + goto err; + } return 0; err: @@ -466,7 +470,8 @@ static void elan_remove(struct hid_device *hdev) } static const struct hid_device_id elan_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER), + .driver_data = ELAN_HAS_LED }, { } }; MODULE_DEVICE_TABLE(hid, elan_devices); -- cgit v1.2.3 From 6e5dd6324a4ce3438b4e8ae35e1a0fdd096a0418 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:36 +0200 Subject: HID: elan: Add USB-id for HP x2 10-n000nd touchpad Now that we query all the parameters, adding support for new hardware is easy. This commit adds support for the touchpad found on the HP x2 10-n000nd touchpad 2-in-1. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 2 ++ drivers/hid/hid-ids.h | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index ae1e24061c0a..4b050d395ecc 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -470,6 +470,8 @@ static void elan_remove(struct hid_device *hdev) } static const struct hid_device_id elan_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2), + .driver_data = ELAN_HAS_LED }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER), .driver_data = ELAN_HAS_LED }, { } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c7981ddd8776..28f96de2a84e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -369,6 +369,7 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 #define USB_VENDOR_ID_ELAN 0x04f3 +#define USB_DEVICE_ID_HP_X2 0x074d #define USB_DEVICE_ID_HP_X2_10_COVER 0x0755 #define USB_VENDOR_ID_ELECOM 0x056e -- cgit v1.2.3 From e7ad3dc9f4a2a183f275c9a3becd0cdd5f69792e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Jul 2018 12:38:37 +0200 Subject: HID: elan: Add support for touchpad on the Toshiba Click Mini L9W The Toshiba Click Mini L9W keyboard dock has a single i2c-hid Elan device for both the keyboard and the touchpad. Add support for the touchpad to the hid-elan driver, rather then relying on mouse emulation. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++---- drivers/hid/hid-ids.h | 1 + 2 files changed, 61 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 4b050d395ecc..07e26c3567eb 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -19,10 +19,13 @@ #include "hid-ids.h" +#define ELAN_MT_I2C 0x5d #define ELAN_SINGLE_FINGER 0x81 #define ELAN_MT_FIRST_FINGER 0x82 #define ELAN_MT_SECOND_FINGER 0x83 #define ELAN_INPUT_REPORT_SIZE 8 +#define ELAN_I2C_REPORT_SIZE 32 +#define ELAN_FINGER_DATA_LEN 5 #define ELAN_MAX_FINGERS 5 #define ELAN_MAX_PRESSURE 255 #define ELAN_TP_USB_INTF 1 @@ -51,9 +54,14 @@ struct elan_drvdata { static int is_not_elan_touchpad(struct hid_device *hdev) { - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + if (hdev->bus == BUS_USB) { + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - return (intf->altsetting->desc.bInterfaceNumber != ELAN_TP_USB_INTF); + return (intf->altsetting->desc.bInterfaceNumber != + ELAN_TP_USB_INTF); + } + + return 0; } static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -65,7 +73,8 @@ static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi, if (field->report->id == ELAN_SINGLE_FINGER || field->report->id == ELAN_MT_FIRST_FINGER || - field->report->id == ELAN_MT_SECOND_FINGER) + field->report->id == ELAN_MT_SECOND_FINGER || + field->report->id == ELAN_MT_I2C) return -1; return 0; @@ -224,7 +233,7 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data, } } -static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) +static void elan_usb_report_input(struct elan_drvdata *drvdata, u8 *data) { int i; struct input_dev *input = drvdata->input; @@ -311,6 +320,46 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) input_sync(input); } +static void elan_i2c_report_input(struct elan_drvdata *drvdata, u8 *data) +{ + struct input_dev *input = drvdata->input; + u8 *finger_data; + int i; + + /* + * Elan MT touchpads in i2c mode send finger data in the same format + * as in USB mode, but then with all fingers in a single packet. + * + * packet structure for ELAN_MT_I2C: + * + * byte 1: 1 0 0 1 1 1 0 1 // 0x5d + * byte 2: f5 f4 f3 f2 f1 0 0 L + * byte 3: x12 x11 x10 x9 0? y11 y10 y9 + * byte 4: x8 x7 x6 x5 x4 x3 x2 x1 + * byte 5: y8 y7 y6 y5 y4 y3 y2 y1 + * byte 6: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 + * byte 7: p8 p7 p6 p5 p4 p3 p2 p1 + * byte 8-12: Same as byte 3-7 for second finger down + * byte 13-17: Same as byte 3-7 for third finger down + * byte 18-22: Same as byte 3-7 for fourth finger down + * byte 23-27: Same as byte 3-7 for fifth finger down + */ + + finger_data = data + 2; + for (i = 0; i < ELAN_MAX_FINGERS; i++) { + if (data[1] & BIT(i + 3)) { + elan_report_mt_slot(drvdata, finger_data, i); + finger_data += ELAN_FINGER_DATA_LEN; + } else { + elan_report_mt_slot(drvdata, NULL, i); + } + } + + input_report_key(input, BTN_LEFT, data[1] & 0x01); + input_mt_sync_frame(input); + input_sync(input); +} + static int elan_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { @@ -323,11 +372,16 @@ static int elan_raw_event(struct hid_device *hdev, data[0] == ELAN_MT_FIRST_FINGER || data[0] == ELAN_MT_SECOND_FINGER) { if (size == ELAN_INPUT_REPORT_SIZE) { - elan_report_input(drvdata, data); + elan_usb_report_input(drvdata, data); return 1; } } + if (data[0] == ELAN_MT_I2C && size == ELAN_I2C_REPORT_SIZE) { + elan_i2c_report_input(drvdata, data); + return 1; + } + return 0; } @@ -474,6 +528,7 @@ static const struct hid_device_id elan_devices[] = { .driver_data = ELAN_HAS_LED }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER), .driver_data = ELAN_HAS_LED }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_TOSHIBA_CLICK_L9W) }, { } }; MODULE_DEVICE_TABLE(hid, elan_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 28f96de2a84e..aa57e70e54db 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -369,6 +369,7 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 #define USB_VENDOR_ID_ELAN 0x04f3 +#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401 #define USB_DEVICE_ID_HP_X2 0x074d #define USB_DEVICE_ID_HP_X2_10_COVER 0x0755 -- cgit v1.2.3 From cf6d15d7b1f386905616912b2d450c8d7092a0f0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jul 2018 16:13:44 +0200 Subject: HID: multitouch: make sure the static list of class is not changed const is a magic keyword here :) Acked-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 45968f7970f8..91624a2240ca 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -211,7 +211,7 @@ static int cypress_compute_slot(struct mt_device *td) return -1; } -static struct mt_class mt_classes[] = { +static const struct mt_class mt_classes[] = { { .name = MT_CLS_DEFAULT, .quirks = MT_QUIRK_ALWAYS_VALID | MT_QUIRK_CONTACT_CNT_ACCURATE }, @@ -1432,7 +1432,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret, i; struct mt_device *td; - struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ + const struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ for (i = 0; mt_classes[i].name ; i++) { if (id->driver_data == mt_classes[i].name) { -- cgit v1.2.3 From f146d1c4d7eafde96a33838e52ba7568d05bf1f7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jul 2018 16:13:45 +0200 Subject: HID: multitouch: Store per collection multitouch data Currently, hid-multitouch can only handle one multitouch collection at a time. This is an issue for the Dell Canvas, as the Totem (a dial tool) is also using a multitouch-like collection. Factor out the multitouch collection data in their own struct. Acked-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 380 ++++++++++++++++++++++++++----------------- 1 file changed, 230 insertions(+), 150 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 91624a2240ca..cd207e99d26a 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -28,14 +28,11 @@ */ /* - * This driver is regularly tested thanks to the tool hid-test[1]. - * This tool relies on hid-replay[2] and a database of hid devices[3]. + * This driver is regularly tested thanks to the test suite in hid-tools[1]. * Please run these regression tests before patching this module so that * your patch won't break existing known devices. * - * [1] https://github.com/bentiss/hid-test - * [2] https://github.com/bentiss/hid-replay - * [3] https://github.com/bentiss/hid-devices + * [1] https://gitlab.freedesktop.org/libevdev/hid-tools */ #include @@ -99,6 +96,37 @@ struct mt_slot { bool has_azimuth; /* the contact reports azimuth */ }; +struct mt_application { + struct list_head list; + unsigned int application; + struct mt_slot curdata; /* placeholder of incoming data */ + + int cc_index; /* contact count field index in the report */ + int cc_value_index; /* contact count value index in the field */ + int scantime_index; /* scantime field index in the report */ + int scantime_val_index; /* scantime value index in the field */ + unsigned int last_slot_field; /* the last field of a slot */ + bool curvalid; /* is the current contact valid? */ + + int left_button_state; /* left button state */ + unsigned int mt_flags; /* flags to pass to input-mt */ + + __u8 num_received; /* how many contacts we received */ + __u8 num_expected; /* expected last contact index */ + __u8 buttons_count; /* number of physical buttons per touchpad */ + __u8 touches_by_report; /* how many touches are present in one report: + * 1 means we should use a serial protocol + * > 1 means hybrid (multitouch) protocol + */ + + __s32 dev_time; /* the scan time provided by the device */ + unsigned long jiffies; /* the frame's jiffies */ + int timestamp; /* the timestamp to be sent */ + int prev_scantime; /* scantime reported previously */ + + bool have_contact_count; +}; + struct mt_class { __s32 name; /* MT_CLS */ __s32 quirks; @@ -117,40 +145,24 @@ struct mt_fields { }; struct mt_device { - struct mt_slot curdata; /* placeholder of incoming data */ struct mt_class mtclass; /* our mt device class */ struct timer_list release_timer; /* to release sticky fingers */ struct hid_device *hdev; /* hid_device we're attached to */ struct mt_fields *fields; /* temporary placeholder for storing the multitouch fields */ unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */ - int cc_index; /* contact count field index in the report */ - int cc_value_index; /* contact count value index in the field */ - int scantime_index; /* scantime field index in the report */ - int scantime_val_index; /* scantime value index in the field */ - int prev_scantime; /* scantime reported in the previous packet */ - int left_button_state; /* left button state */ - unsigned last_slot_field; /* the last field of a slot */ unsigned mt_report_id; /* the report ID of the multitouch device */ __u8 inputmode_value; /* InputMode HID feature value */ - __u8 num_received; /* how many contacts we received */ - __u8 num_expected; /* expected last contact index */ __u8 maxcontacts; - __u8 touches_by_report; /* how many touches are present in one report: - * 1 means we should use a serial protocol - * > 1 means hybrid (multitouch) protocol */ - __u8 buttons_count; /* number of physical buttons per touchpad */ bool is_buttonpad; /* is this device a button pad? */ bool serial_maybe; /* need to check for serial protocol */ - bool curvalid; /* is the current contact valid? */ - unsigned mt_flags; /* flags to pass to input-mt */ - __s32 dev_time; /* the scan time provided by the device */ - unsigned long jiffies; /* the frame's jiffies */ - int timestamp; /* the timestamp to be sent */ + + struct list_head applications; }; -static void mt_post_parse_default_settings(struct mt_device *td); -static void mt_post_parse(struct mt_device *td); +static void mt_post_parse_default_settings(struct mt_device *td, + struct mt_application *app); +static void mt_post_parse(struct mt_device *td, struct mt_application *app); /* classes of device behavior */ #define MT_CLS_DEFAULT 0x0001 @@ -203,10 +215,10 @@ static void mt_post_parse(struct mt_device *td); * to a valid contact that was just read. */ -static int cypress_compute_slot(struct mt_device *td) +static int cypress_compute_slot(struct mt_application *app) { - if (td->curdata.contactid != 0 || td->num_received == 0) - return td->curdata.contactid; + if (app->curdata.contactid != 0 || app->num_received == 0) + return app->curdata.contactid; else return -1; } @@ -353,15 +365,22 @@ static ssize_t mt_set_quirks(struct device *dev, { struct hid_device *hdev = to_hid_device(dev); struct mt_device *td = hid_get_drvdata(hdev); + struct mt_application *application; unsigned long val; + bool confidence_found = false; if (kstrtoul(buf, 0, &val)) return -EINVAL; td->mtclass.quirks = val; - if (td->cc_index < 0) + list_for_each_entry(application, &td->applications, list) { + if (application->have_contact_count) + confidence_found = true; + } + + if (!confidence_found) td->mtclass.quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; return count; @@ -457,6 +476,55 @@ static void set_abs(struct input_dev *input, unsigned int code, input_abs_set_res(input, code, hidinput_calc_abs_res(field, code)); } +static struct mt_application *mt_allocate_application(struct mt_device *td, + unsigned int application) +{ + struct mt_application *mt_application; + + mt_application = devm_kzalloc(&td->hdev->dev, sizeof(*mt_application), + GFP_KERNEL); + if (!mt_application) + return NULL; + + mt_application->application = application; + + if (application == HID_DG_TOUCHSCREEN) + mt_application->mt_flags |= INPUT_MT_DIRECT; + + /* + * Model touchscreens providing buttons as touchpads. + */ + if (application == HID_DG_TOUCHPAD) { + mt_application->mt_flags |= INPUT_MT_POINTER; + td->inputmode_value = MT_INPUTMODE_TOUCHPAD; + } + + mt_application->cc_index = -1; + mt_application->scantime_index = -1; + + list_add_tail(&mt_application->list, &td->applications); + + return mt_application; +} + +static struct mt_application *mt_find_application(struct mt_device *td, + unsigned int application) +{ + struct mt_application *tmp, *mt_application = NULL; + + list_for_each_entry(tmp, &td->applications, list) { + if (application == tmp->application) { + mt_application = tmp; + break; + } + } + + if (!mt_application) + mt_application = mt_allocate_application(td, application); + + return mt_application; +} + static void mt_store_field(struct hid_usage *usage, struct mt_device *td, struct hid_input *hi) { @@ -470,28 +538,24 @@ static void mt_store_field(struct hid_usage *usage, struct mt_device *td, static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) + unsigned long **bit, int *max, struct mt_application *app) { struct mt_device *td = hid_get_drvdata(hdev); struct mt_class *cls = &td->mtclass; int code; struct hid_usage *prev_usage = NULL; - if (field->application == HID_DG_TOUCHSCREEN) - td->mt_flags |= INPUT_MT_DIRECT; - /* * Model touchscreens providing buttons as touchpads. */ - if (field->application == HID_DG_TOUCHPAD || - (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { - td->mt_flags |= INPUT_MT_POINTER; + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { + app->mt_flags |= INPUT_MT_POINTER; td->inputmode_value = MT_INPUTMODE_TOUCHPAD; } /* count the buttons on touchpads */ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) - td->buttons_count++; + app->buttons_count++; if (usage->usage_index) prev_usage = &field->usage[usage->usage_index - 1]; @@ -501,33 +565,23 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: - if (prev_usage && (prev_usage->hid == usage->hid)) { - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TOOL_X); - set_abs(hi->input, ABS_MT_TOOL_X, field, - cls->sn_move); - } else { - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_POSITION_X); - set_abs(hi->input, ABS_MT_POSITION_X, field, - cls->sn_move); - } + if (prev_usage && (prev_usage->hid == usage->hid)) + code = ABS_MT_TOOL_X; + else + code = ABS_MT_POSITION_X; + hid_map_usage(hi, usage, bit, max, EV_ABS, code); + set_abs(hi->input, code, field, cls->sn_move); mt_store_field(usage, td, hi); return 1; case HID_GD_Y: - if (prev_usage && (prev_usage->hid == usage->hid)) { - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TOOL_Y); - set_abs(hi->input, ABS_MT_TOOL_Y, field, - cls->sn_move); - } else { - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_POSITION_Y); - set_abs(hi->input, ABS_MT_POSITION_Y, field, - cls->sn_move); - } + if (prev_usage && (prev_usage->hid == usage->hid)) + code = ABS_MT_TOOL_Y; + else + code = ABS_MT_POSITION_Y; + hid_map_usage(hi, usage, bit, max, EV_ABS, code); + set_abs(hi->input, code, field, cls->sn_move); mt_store_field(usage, td, hi); return 1; } @@ -558,7 +612,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; case HID_DG_CONTACTID: mt_store_field(usage, td, hi); - td->touches_by_report++; + app->touches_by_report++; td->mt_report_id = field->report->id; return 1; case HID_DG_WIDTH: @@ -602,20 +656,16 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, if (field->index >= field->report->maxfield || usage->usage_index >= field->report_count) return 1; - td->scantime_index = field->index; - td->scantime_val_index = usage->usage_index; - /* - * We don't set td->last_slot_field as scan time is - * global to the report. - */ + app->scantime_index = field->index; + app->scantime_val_index = usage->usage_index; return 1; case HID_DG_CONTACTCOUNT: /* Ignore if indexes are out of bounds. */ if (field->index >= field->report->maxfield || usage->usage_index >= field->report_count) return 1; - td->cc_index = field->index; - td->cc_value_index = usage->usage_index; + app->cc_index = field->index; + app->cc_value_index = usage->usage_index; return 1; case HID_DG_AZIMUTH: hid_map_usage(hi, usage, bit, max, @@ -666,39 +716,41 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static int mt_compute_slot(struct mt_device *td, struct input_dev *input) +static int mt_compute_slot(struct mt_device *td, struct mt_application *app, + struct input_dev *input) { __s32 quirks = td->mtclass.quirks; if (quirks & MT_QUIRK_SLOT_IS_CONTACTID) - return td->curdata.contactid; + return app->curdata.contactid; if (quirks & MT_QUIRK_CYPRESS) - return cypress_compute_slot(td); + return cypress_compute_slot(app); if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER) - return td->num_received; + return app->num_received; if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE) - return td->curdata.contactid - 1; + return app->curdata.contactid - 1; - return input_mt_get_slot_by_key(input, td->curdata.contactid); + return input_mt_get_slot_by_key(input, app->curdata.contactid); } /* * this function is called when a whole contact has been processed, * so that it can assign it to a slot and store the data there */ -static void mt_complete_slot(struct mt_device *td, struct input_dev *input) +static void mt_complete_slot(struct mt_device *td, struct mt_application *app, + struct input_dev *input) { if ((td->mtclass.quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) && - td->num_received >= td->num_expected) + app->num_received >= app->num_expected) return; - if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) { + if (app->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) { int active; - int slotnum = mt_compute_slot(td, input); - struct mt_slot *s = &td->curdata; + int slotnum = mt_compute_slot(td, app, input); + struct mt_slot *s = &app->curdata; struct input_mt *mt = input->mt; if (slotnum < 0 || slotnum >= td->maxcontacts) @@ -753,23 +805,25 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) } } - td->num_received++; + app->num_received++; } /* * this function is called when a whole packet has been received and processed, * so that it can decide what to send to the input layer. */ -static void mt_sync_frame(struct mt_device *td, struct input_dev *input) +static void mt_sync_frame(struct mt_device *td, struct mt_application *app, + struct input_dev *input) { if (td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS) - input_event(input, EV_KEY, BTN_LEFT, td->left_button_state); + input_event(input, EV_KEY, BTN_LEFT, app->left_button_state); input_mt_sync_frame(input); - input_event(input, EV_MSC, MSC_TIMESTAMP, td->timestamp); + input_event(input, EV_MSC, MSC_TIMESTAMP, app->timestamp); input_sync(input); - td->num_received = 0; - td->left_button_state = 0; + app->num_received = 0; + app->left_button_state = 0; + if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags)) set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags); else @@ -777,14 +831,13 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input) clear_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); } -static int mt_compute_timestamp(struct mt_device *td, struct hid_field *field, - __s32 value) +static int mt_compute_timestamp(struct mt_application *app, + struct hid_field *field, __s32 value) { - long delta = value - td->dev_time; - unsigned long jdelta = jiffies_to_usecs(jiffies - td->jiffies); + long delta = value - app->prev_scantime; + unsigned long jdelta = jiffies_to_usecs(jiffies - app->jiffies); - td->jiffies = jiffies; - td->dev_time = value; + app->jiffies = jiffies; if (delta < 0) delta += field->logical_maximum; @@ -796,7 +849,7 @@ static int mt_compute_timestamp(struct mt_device *td, struct hid_field *field, /* No data received for a while, resync the timestamp. */ return 0; else - return td->timestamp + delta; + return app->timestamp + delta; } static int mt_touch_event(struct hid_device *hid, struct hid_field *field, @@ -811,7 +864,7 @@ static int mt_touch_event(struct hid_device *hid, struct hid_field *field, static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, - bool first_packet) + struct mt_application *app, bool first_packet) { struct mt_device *td = hid_get_drvdata(hid); __s32 quirks = td->mtclass.quirks; @@ -821,47 +874,48 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, switch (usage->hid) { case HID_DG_INRANGE: if (quirks & MT_QUIRK_VALID_IS_INRANGE) - td->curvalid = value; + app->curvalid = value; if (quirks & MT_QUIRK_HOVERING) - td->curdata.inrange_state = value; + app->curdata.inrange_state = value; break; case HID_DG_TIPSWITCH: if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) - td->curvalid = value; - td->curdata.touch_state = value; + app->curvalid = value; + app->curdata.touch_state = value; break; case HID_DG_CONFIDENCE: if (quirks & MT_QUIRK_CONFIDENCE) - td->curdata.confidence_state = value; + app->curdata.confidence_state = value; if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE) - td->curvalid = value; + app->curvalid = value; break; case HID_DG_CONTACTID: - td->curdata.contactid = value; + app->curdata.contactid = value; break; case HID_DG_TIPPRESSURE: - td->curdata.p = value; + app->curdata.p = value; break; case HID_GD_X: if (usage->code == ABS_MT_TOOL_X) - td->curdata.cx = value; + app->curdata.cx = value; else - td->curdata.x = value; + app->curdata.x = value; break; case HID_GD_Y: if (usage->code == ABS_MT_TOOL_Y) - td->curdata.cy = value; + app->curdata.cy = value; else - td->curdata.y = value; + app->curdata.y = value; break; case HID_DG_WIDTH: - td->curdata.w = value; + app->curdata.w = value; break; case HID_DG_HEIGHT: - td->curdata.h = value; + app->curdata.h = value; break; case HID_DG_SCANTIME: - td->timestamp = mt_compute_timestamp(td, field, value); + app->timestamp = mt_compute_timestamp(app, field, + value); break; case HID_DG_CONTACTCOUNT: break; @@ -878,8 +932,8 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, */ if (value > field->logical_maximum / 2) value -= field->logical_maximum; - td->curdata.a = -value; - td->curdata.has_azimuth = true; + app->curdata.a = -value; + app->curdata.has_azimuth = true; break; case HID_DG_TOUCH: /* do nothing */ @@ -904,7 +958,7 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, */ if ((quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && usage->type == EV_KEY && usage->code == BTN_LEFT) { - td->left_button_state |= value; + app->left_button_state |= value; return; } @@ -916,8 +970,9 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, if (usage->usage_index + 1 == field->report_count) { /* we only take into account the last report. */ - if (usage->hid == td->last_slot_field) - mt_complete_slot(td, field->hidinput->input); + if (usage->hid == app->last_slot_field) + mt_complete_slot(td, app, + field->hidinput->input); } } @@ -926,26 +981,34 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, static void mt_touch_report(struct hid_device *hid, struct hid_report *report) { struct mt_device *td = hid_get_drvdata(hid); + struct mt_application *app; struct hid_field *field; bool first_packet; unsigned count; - int r, n, scantime = 0; + int r, n; + int scantime = 0; + int contact_count = -1; /* sticky fingers release in progress, abort */ if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; + app = mt_find_application(td, report->application); + + if (!app) + return; + /* * Includes multi-packet support where subsequent * packets are sent with zero contactcount. */ - if (td->scantime_index >= 0) { - field = report->field[td->scantime_index]; - scantime = field->value[td->scantime_val_index]; + if (app->scantime_index >= 0) { + field = report->field[app->scantime_index]; + scantime = field->value[app->scantime_val_index]; } - if (td->cc_index >= 0) { - struct hid_field *field = report->field[td->cc_index]; - int value = field->value[td->cc_value_index]; + if (app->cc_index >= 0) { + field = report->field[app->cc_index]; + contact_count = field->value[app->cc_value_index]; /* * For Win8 PTPs the first packet (td->num_received == 0) may @@ -955,15 +1018,16 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) * timestamp has changed. */ if ((td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && - td->num_received == 0 && td->prev_scantime != scantime) - td->num_expected = value; + app->num_received == 0 && + app->prev_scantime != scantime) + app->num_expected = contact_count; /* A non 0 contact count always indicates a first packet */ - else if (value) - td->num_expected = value; + else if (contact_count) + app->num_expected = contact_count; } - td->prev_scantime = scantime; + app->prev_scantime = scantime; - first_packet = td->num_received == 0; + first_packet = app->num_received == 0; for (r = 0; r < report->maxfield; r++) { field = report->field[r]; count = field->report_count; @@ -973,11 +1037,11 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) for (n = 0; n < count; n++) mt_process_mt_event(hid, field, &field->usage[n], - field->value[n], first_packet); + field->value[n], app, first_packet); } - if (td->num_received >= td->num_expected) - mt_sync_frame(td, report->field[0]->hidinput->input); + if (app->num_received >= app->num_expected) + mt_sync_frame(td, app, report->field[0]->hidinput->input); /* * Windows 8 specs says 2 things: @@ -1009,7 +1073,8 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) } static int mt_touch_input_configured(struct hid_device *hdev, - struct hid_input *hi) + struct hid_input *hi, + struct mt_application *app) { struct mt_device *td = hid_get_drvdata(hdev); struct mt_class *cls = &td->mtclass; @@ -1019,28 +1084,29 @@ static int mt_touch_input_configured(struct hid_device *hdev, if (!td->maxcontacts) td->maxcontacts = MT_DEFAULT_MAXCONTACT; - mt_post_parse(td); + mt_post_parse(td, app); if (td->serial_maybe) - mt_post_parse_default_settings(td); + mt_post_parse_default_settings(td, app); if (cls->is_indirect) - td->mt_flags |= INPUT_MT_POINTER; + app->mt_flags |= INPUT_MT_POINTER; if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) - td->mt_flags |= INPUT_MT_DROP_UNUSED; + app->mt_flags |= INPUT_MT_DROP_UNUSED; /* check for clickpads */ - if ((td->mt_flags & INPUT_MT_POINTER) && (td->buttons_count == 1)) + if ((app->mt_flags & INPUT_MT_POINTER) && + (app->buttons_count == 1)) td->is_buttonpad = true; if (td->is_buttonpad) __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); - ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + ret = input_mt_init_slots(input, td->maxcontacts, app->mt_flags); if (ret) return ret; - td->mt_flags = 0; + app->mt_flags = 0; return 0; } @@ -1051,6 +1117,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, unsigned long **bit, int *max) { struct mt_device *td = hid_get_drvdata(hdev); + struct mt_application *application; /* * If mtclass.export_all_inputs is not set, only map fields from @@ -1093,6 +1160,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } + application = mt_find_application(td, field->application); + /* * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" * for the stylus. @@ -1109,7 +1178,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, if (field->application == HID_DG_TOUCHSCREEN || field->application == HID_DG_TOUCHPAD) - return mt_touch_input_mapping(hdev, hi, field, usage, bit, max); + return mt_touch_input_mapping(hdev, hi, field, usage, bit, max, + application); /* let hid-core decide for the others */ return 0; @@ -1259,12 +1329,13 @@ static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency, } } -static void mt_post_parse_default_settings(struct mt_device *td) +static void mt_post_parse_default_settings(struct mt_device *td, + struct mt_application *app) { __s32 quirks = td->mtclass.quirks; /* unknown serial device needs special quirks */ - if (td->touches_by_report == 1) { + if (app->touches_by_report == 1) { quirks |= MT_QUIRK_ALWAYS_VALID; quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; quirks &= ~MT_QUIRK_VALID_IS_INRANGE; @@ -1275,17 +1346,19 @@ static void mt_post_parse_default_settings(struct mt_device *td) td->mtclass.quirks = quirks; } -static void mt_post_parse(struct mt_device *td) +static void mt_post_parse(struct mt_device *td, struct mt_application *app) { struct mt_fields *f = td->fields; struct mt_class *cls = &td->mtclass; - if (td->touches_by_report > 0) { - int field_count_per_touch = f->length / td->touches_by_report; - td->last_slot_field = f->usages[field_count_per_touch - 1]; + if (app->touches_by_report > 0) { + int field_count_per_touch; + + field_count_per_touch = f->length / app->touches_by_report; + app->last_slot_field = f->usages[field_count_per_touch - 1]; } - if (td->cc_index < 0) + if (app->cc_index < 0) cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } @@ -1295,13 +1368,17 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) char *name; const char *suffix = NULL; unsigned int application = 0; + struct mt_application *mt_application = NULL; struct hid_report *report; int ret; list_for_each_entry(report, &hi->reports, hidinput_list) { application = report->application; + mt_application = mt_find_application(td, application); + if (report->id == td->mt_report_id) { - ret = mt_touch_input_configured(hdev, hi); + ret = mt_touch_input_configured(hdev, hi, + mt_application); if (ret) return ret; } @@ -1390,6 +1467,7 @@ static void mt_fix_const_fields(struct hid_device *hdev, unsigned int usage) static void mt_release_contacts(struct hid_device *hid) { struct hid_input *hidinput; + struct mt_application *application; struct mt_device *td = hid_get_drvdata(hid); list_for_each_entry(hidinput, &hid->inputs, list) { @@ -1409,7 +1487,9 @@ static void mt_release_contacts(struct hid_device *hid) } } - td->num_received = 0; + list_for_each_entry(application, &td->applications, list) { + application->num_received = 0; + } } static void mt_expired_timeout(struct timer_list *t) @@ -1449,11 +1529,11 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) td->hdev = hdev; td->mtclass = *mtclass; td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN; - td->cc_index = -1; - td->scantime_index = -1; td->mt_report_id = -1; hid_set_drvdata(hdev, td); + INIT_LIST_HEAD(&td->applications); + td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields), GFP_KERNEL); if (!td->fields) { -- cgit v1.2.3 From 3ceb3826448d1ec4650a6c2b62baa9e0994ac7d3 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jul 2018 16:13:46 +0200 Subject: HID: multitouch: store a per application quirks value If a device has more than one multitouch collection, there is a chance we need per tool quirks. This is the case for the Totem on the Dell Canvas. Note that thesysfs attribute quirks can now get out of sync, but there should not be much users of it as it's debugging only. Acked-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 64 ++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index cd207e99d26a..beaac36f61a7 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -99,6 +99,9 @@ struct mt_slot { struct mt_application { struct list_head list; unsigned int application; + + __s32 quirks; + struct mt_slot curdata; /* placeholder of incoming data */ int cc_index; /* contact count field index in the report */ @@ -368,7 +371,6 @@ static ssize_t mt_set_quirks(struct device *dev, struct mt_application *application; unsigned long val; - bool confidence_found = false; if (kstrtoul(buf, 0, &val)) return -EINVAL; @@ -376,13 +378,11 @@ static ssize_t mt_set_quirks(struct device *dev, td->mtclass.quirks = val; list_for_each_entry(application, &td->applications, list) { - if (application->have_contact_count) - confidence_found = true; + application->quirks = val; + if (!application->have_contact_count) + application->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } - if (!confidence_found) - td->mtclass.quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; - return count; } @@ -501,6 +501,7 @@ static struct mt_application *mt_allocate_application(struct mt_device *td, mt_application->cc_index = -1; mt_application->scantime_index = -1; + mt_application->quirks = td->mtclass.quirks; list_add_tail(&mt_application->list, &td->applications); @@ -590,7 +591,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_DIGITIZER: switch (usage->hid) { case HID_DG_INRANGE: - if (cls->quirks & MT_QUIRK_HOVERING) { + if (app->quirks & MT_QUIRK_HOVERING) { hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_DISTANCE); input_set_abs_params(hi->input, @@ -602,7 +603,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, if ((cls->name == MT_CLS_WIN_8 || cls->name == MT_CLS_WIN_8_DUAL) && field->application == HID_DG_TOUCHPAD) - cls->quirks |= MT_QUIRK_CONFIDENCE; + app->quirks |= MT_QUIRK_CONFIDENCE; mt_store_field(usage, td, hi); return 1; case HID_DG_TIPSWITCH: @@ -618,7 +619,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_WIDTH: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MAJOR); - if (!(cls->quirks & MT_QUIRK_NO_AREA)) + if (!(app->quirks & MT_QUIRK_NO_AREA)) set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, cls->sn_width); mt_store_field(usage, td, hi); @@ -626,7 +627,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_HEIGHT: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MINOR); - if (!(cls->quirks & MT_QUIRK_NO_AREA)) { + if (!(app->quirks & MT_QUIRK_NO_AREA)) { set_abs(hi->input, ABS_MT_TOUCH_MINOR, field, cls->sn_height); @@ -700,7 +701,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, * MS PTP spec says that external buttons left and right have * usages 2 and 3. */ - if ((cls->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && + if ((app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && field->application == HID_DG_TOUCHPAD && (usage->hid & HID_USAGE) > 1) code--; @@ -719,7 +720,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, static int mt_compute_slot(struct mt_device *td, struct mt_application *app, struct input_dev *input) { - __s32 quirks = td->mtclass.quirks; + __s32 quirks = app->quirks; if (quirks & MT_QUIRK_SLOT_IS_CONTACTID) return app->curdata.contactid; @@ -743,11 +744,11 @@ static int mt_compute_slot(struct mt_device *td, struct mt_application *app, static void mt_complete_slot(struct mt_device *td, struct mt_application *app, struct input_dev *input) { - if ((td->mtclass.quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) && + if ((app->quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) && app->num_received >= app->num_expected) return; - if (app->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) { + if (app->curvalid || (app->quirks & MT_QUIRK_ALWAYS_VALID)) { int active; int slotnum = mt_compute_slot(td, app, input); struct mt_slot *s = &app->curdata; @@ -756,14 +757,14 @@ static void mt_complete_slot(struct mt_device *td, struct mt_application *app, if (slotnum < 0 || slotnum >= td->maxcontacts) return; - if ((td->mtclass.quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) { + if ((app->quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) { struct input_mt_slot *slot = &mt->slots[slotnum]; if (input_mt_is_active(slot) && input_mt_is_used(mt, slot)) return; } - if (!(td->mtclass.quirks & MT_QUIRK_CONFIDENCE)) + if (!(app->quirks & MT_QUIRK_CONFIDENCE)) s->confidence_state = true; active = (s->touch_state || s->inrange_state) && s->confidence_state; @@ -784,7 +785,7 @@ static void mt_complete_slot(struct mt_device *td, struct mt_application *app, * divided by two to match visual scale of touch * for devices with this quirk */ - if (td->mtclass.quirks & MT_QUIRK_TOUCH_SIZE_SCALING) { + if (app->quirks & MT_QUIRK_TOUCH_SIZE_SCALING) { major = major >> 1; minor = minor >> 1; } @@ -815,7 +816,7 @@ static void mt_complete_slot(struct mt_device *td, struct mt_application *app, static void mt_sync_frame(struct mt_device *td, struct mt_application *app, struct input_dev *input) { - if (td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS) + if (app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) input_event(input, EV_KEY, BTN_LEFT, app->left_button_state); input_mt_sync_frame(input); @@ -867,7 +868,7 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, struct mt_application *app, bool first_packet) { struct mt_device *td = hid_get_drvdata(hid); - __s32 quirks = td->mtclass.quirks; + __s32 quirks = app->quirks; struct input_dev *input = field->hidinput->input; if (hid->claimed & HID_CLAIMED_INPUT) { @@ -1017,7 +1018,7 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) * of a possible multi-packet frame be checking that the * timestamp has changed. */ - if ((td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && + if ((app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && app->num_received == 0 && app->prev_scantime != scantime) app->num_expected = contact_count; @@ -1061,7 +1062,7 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) * only affect laggish machines and the ones that have a firmware * defect. */ - if (td->mtclass.quirks & MT_QUIRK_STICKY_FINGERS) { + if (app->quirks & MT_QUIRK_STICKY_FINGERS) { if (test_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags)) mod_timer(&td->release_timer, jiffies + msecs_to_jiffies(100)); @@ -1091,7 +1092,7 @@ static int mt_touch_input_configured(struct hid_device *hdev, if (cls->is_indirect) app->mt_flags |= INPUT_MT_POINTER; - if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) + if (app->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) app->mt_flags |= INPUT_MT_DROP_UNUSED; /* check for clickpads */ @@ -1119,6 +1120,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct mt_device *td = hid_get_drvdata(hdev); struct mt_application *application; + application = mt_find_application(td, field->application); + /* * If mtclass.export_all_inputs is not set, only map fields from * TouchScreen or TouchPad collections. We need to ignore fields @@ -1134,7 +1137,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->application != HID_CP_CONSUMER_CONTROL && field->application != HID_GD_WIRELESS_RADIO_CTLS && !(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS && - td->mtclass.quirks & MT_QUIRK_ASUS_CUSTOM_UP)) + application->quirks & MT_QUIRK_ASUS_CUSTOM_UP)) return -1; /* @@ -1143,7 +1146,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, * map usages to input keys. */ if (field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS && - td->mtclass.quirks & MT_QUIRK_ASUS_CUSTOM_UP && + application->quirks & MT_QUIRK_ASUS_CUSTOM_UP && (usage->hid & HID_USAGE_PAGE) == HID_UP_CUSTOM) { set_bit(EV_REP, hi->input->evbit); if (field->flags & HID_MAIN_ITEM_VARIABLE) @@ -1160,8 +1163,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } - application = mt_find_application(td, field->application); - /* * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" * for the stylus. @@ -1267,9 +1268,9 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev, return true; case HID_DG_CONTACTMAX: - if (td->mtclass.maxcontacts) { + if (cls->maxcontacts) { max = min_t(int, field->logical_maximum, - td->mtclass.maxcontacts); + cls->maxcontacts); if (field->value[index] != max) { field->value[index] = max; return true; @@ -1332,7 +1333,7 @@ static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency, static void mt_post_parse_default_settings(struct mt_device *td, struct mt_application *app) { - __s32 quirks = td->mtclass.quirks; + __s32 quirks = app->quirks; /* unknown serial device needs special quirks */ if (app->touches_by_report == 1) { @@ -1343,13 +1344,12 @@ static void mt_post_parse_default_settings(struct mt_device *td, quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } - td->mtclass.quirks = quirks; + app->quirks = quirks; } static void mt_post_parse(struct mt_device *td, struct mt_application *app) { struct mt_fields *f = td->fields; - struct mt_class *cls = &td->mtclass; if (app->touches_by_report > 0) { int field_count_per_touch; @@ -1359,7 +1359,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app) } if (app->cc_index < 0) - cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; + app->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) -- cgit v1.2.3 From 8dfe14b3b47ff832cb638731f9fc696a3a84f804 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jul 2018 16:13:47 +0200 Subject: HID: multitouch: ditch mt_report_id Now that the driver can handle more than one multitouch collection in a single HID device, ditch the last bit that contains us to use only one mt collection. Acked-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 126 ++++++++++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index beaac36f61a7..a2c10fc62ef2 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -147,6 +147,13 @@ struct mt_fields { unsigned int length; }; +struct mt_report_data { + struct list_head list; + struct hid_report *report; + struct mt_application *application; + bool is_mt_collection; +}; + struct mt_device { struct mt_class mtclass; /* our mt device class */ struct timer_list release_timer; /* to release sticky fingers */ @@ -154,13 +161,13 @@ struct mt_device { struct mt_fields *fields; /* temporary placeholder for storing the multitouch fields */ unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */ - unsigned mt_report_id; /* the report ID of the multitouch device */ __u8 inputmode_value; /* InputMode HID feature value */ __u8 maxcontacts; bool is_buttonpad; /* is this device a button pad? */ bool serial_maybe; /* need to check for serial protocol */ struct list_head applications; + struct list_head reports; }; static void mt_post_parse_default_settings(struct mt_device *td, @@ -526,6 +533,60 @@ static struct mt_application *mt_find_application(struct mt_device *td, return mt_application; } +static struct mt_report_data *mt_allocate_report_data(struct mt_device *td, + struct hid_report *report) +{ + struct mt_report_data *rdata; + struct hid_field *field; + int r, n; + + rdata = devm_kzalloc(&td->hdev->dev, sizeof(*rdata), GFP_KERNEL); + if (!rdata) + return NULL; + + rdata->report = report; + rdata->application = mt_find_application(td, report->application); + + if (!rdata->application) { + devm_kfree(&td->hdev->dev, rdata); + return NULL; + } + + for (r = 0; r < report->maxfield; r++) { + field = report->field[r]; + + if (!(HID_MAIN_ITEM_VARIABLE & field->flags)) + continue; + + for (n = 0; n < field->report_count; n++) { + if (field->usage[n].hid == HID_DG_CONTACTID) + rdata->is_mt_collection = true; + } + } + + list_add_tail(&rdata->list, &td->reports); + + return rdata; +} + +static struct mt_report_data *mt_find_report_data(struct mt_device *td, + struct hid_report *report) +{ + struct mt_report_data *tmp, *rdata = NULL; + + list_for_each_entry(tmp, &td->reports, list) { + if (report == tmp->report) { + rdata = tmp; + break; + } + } + + if (!rdata) + rdata = mt_allocate_report_data(td, report); + + return rdata; +} + static void mt_store_field(struct hid_usage *usage, struct mt_device *td, struct hid_input *hi) { @@ -614,7 +675,6 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_CONTACTID: mt_store_field(usage, td, hi); app->touches_by_report++; - td->mt_report_id = field->report->id; return 1; case HID_DG_WIDTH: hid_map_usage(hi, usage, bit, max, @@ -979,10 +1039,12 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, } } -static void mt_touch_report(struct hid_device *hid, struct hid_report *report) +static void mt_touch_report(struct hid_device *hid, + struct mt_report_data *rdata) { struct mt_device *td = hid_get_drvdata(hid); - struct mt_application *app; + struct hid_report *report = rdata->report; + struct mt_application *app = rdata->application; struct hid_field *field; bool first_packet; unsigned count; @@ -994,11 +1056,6 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; - app = mt_find_application(td, report->application); - - if (!app) - return; - /* * Includes multi-packet support where subsequent * packets are sent with zero contactcount. @@ -1119,8 +1176,15 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, { struct mt_device *td = hid_get_drvdata(hdev); struct mt_application *application; + struct mt_report_data *rdata; + + rdata = mt_find_report_data(td, field->report); + if (!rdata) { + hid_err(hdev, "failed to allocate data for report\n"); + return 0; + } - application = mt_find_application(td, field->application); + application = rdata->application; /* * If mtclass.export_all_inputs is not set, only map fields from @@ -1163,22 +1227,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } - /* - * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" - * for the stylus. - * The check for mt_report_id ensures we don't process - * HID_DG_CONTACTCOUNT from the pen report as it is outside the physical - * collection, but within the report ID. - */ - if (field->physical == HID_DG_STYLUS) - return 0; - else if ((field->physical == 0) && - (field->report->id != td->mt_report_id) && - (td->mt_report_id != -1)) - return 0; - - if (field->application == HID_DG_TOUCHSCREEN || - field->application == HID_DG_TOUCHPAD) + if (rdata->is_mt_collection && + (field->application == HID_DG_TOUCHSCREEN || + field->application == HID_DG_TOUCHPAD)) return mt_touch_input_mapping(hdev, hi, field, usage, bit, max, application); @@ -1211,8 +1262,10 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct mt_device *td = hid_get_drvdata(hid); + struct mt_report_data *rdata; - if (field->report->id == td->mt_report_id) + rdata = mt_find_report_data(td, field->report); + if (rdata && rdata->is_mt_collection) return mt_touch_event(hid, field, usage, value); return 0; @@ -1222,12 +1275,14 @@ static void mt_report(struct hid_device *hid, struct hid_report *report) { struct mt_device *td = hid_get_drvdata(hid); struct hid_field *field = report->field[0]; + struct mt_report_data *rdata; if (!(hid->claimed & HID_CLAIMED_INPUT)) return; - if (report->id == td->mt_report_id) - return mt_touch_report(hid, report); + rdata = mt_find_report_data(td, report); + if (rdata && rdata->is_mt_collection) + return mt_touch_report(hid, rdata); if (field && field->hidinput && field->hidinput->input) input_sync(field->hidinput->input); @@ -1368,15 +1423,22 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) char *name; const char *suffix = NULL; unsigned int application = 0; + struct mt_report_data *rdata; struct mt_application *mt_application = NULL; struct hid_report *report; int ret; list_for_each_entry(report, &hi->reports, hidinput_list) { application = report->application; - mt_application = mt_find_application(td, application); + rdata = mt_find_report_data(td, report); + if (!rdata) { + hid_err(hdev, "failed to allocate data for report\n"); + return -ENOMEM; + } + + mt_application = rdata->application; - if (report->id == td->mt_report_id) { + if (rdata->is_mt_collection) { ret = mt_touch_input_configured(hdev, hi, mt_application); if (ret) @@ -1529,10 +1591,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) td->hdev = hdev; td->mtclass = *mtclass; td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN; - td->mt_report_id = -1; hid_set_drvdata(hdev, td); INIT_LIST_HEAD(&td->applications); + INIT_LIST_HEAD(&td->reports); td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields), GFP_KERNEL); -- cgit v1.2.3 From 01eaac7e57134cae4e75b36195a10d525939c7bf Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jul 2018 16:13:48 +0200 Subject: HID: multitouch: remove one copy of values The current way of handling multitouch data is not very straightforward: - in mt_event() we do nothing - in mt_report() we: - do some gym to fetch the scantime and the contact count - then iterate over the input fields where we copy the data to a temporary place - when we see the last field in a slot, we then use this data to emit the input data A more streamlined way is to first get all of the address in the report of all fields, and then just pick the fields we are interested in in mt_report() Acked-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 545 +++++++++++++++++++++---------------------- 1 file changed, 264 insertions(+), 281 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a2c10fc62ef2..346e9caef6f3 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -87,30 +87,34 @@ enum latency_mode { #define MT_IO_FLAGS_ACTIVE_SLOTS 1 #define MT_IO_FLAGS_PENDING_SLOTS 2 -struct mt_slot { - __s32 x, y, cx, cy, p, w, h, a; - __s32 contactid; /* the device ContactID assigned to this slot */ - bool touch_state; /* is the touch valid? */ - bool inrange_state; /* is the finger in proximity of the sensor? */ - bool confidence_state; /* is the touch made by a finger? */ - bool has_azimuth; /* the contact reports azimuth */ +static const bool mtrue = true; /* default for true */ +static const bool mfalse; /* default for false */ +static const __s32 mzero; /* default for 0 */ + +#define DEFAULT_TRUE ((void *)&mtrue) +#define DEFAULT_FALSE ((void *)&mfalse) +#define DEFAULT_ZERO ((void *)&mzero) + +struct mt_usages { + struct list_head list; + __s32 *x, *y, *cx, *cy, *p, *w, *h, *a; + __s32 *contactid; /* the device ContactID assigned to this slot */ + bool *tip_state; /* is the touch valid? */ + bool *inrange_state; /* is the finger in proximity of the sensor? */ + bool *confidence_state; /* is the touch made by a finger? */ }; struct mt_application { struct list_head list; unsigned int application; + struct list_head mt_usages; /* mt usages list */ __s32 quirks; - struct mt_slot curdata; /* placeholder of incoming data */ - - int cc_index; /* contact count field index in the report */ - int cc_value_index; /* contact count value index in the field */ - int scantime_index; /* scantime field index in the report */ - int scantime_val_index; /* scantime value index in the field */ - unsigned int last_slot_field; /* the last field of a slot */ - bool curvalid; /* is the current contact valid? */ + __s32 *scantime; /* scantime reported */ + __s32 scantime_logical_max; /* max value for raw scantime */ + __s32 *raw_cc; /* contact count in the report */ int left_button_state; /* left button state */ unsigned int mt_flags; /* flags to pass to input-mt */ @@ -142,11 +146,6 @@ struct mt_class { bool export_all_inputs; /* do not ignore mouse, keyboards, etc... */ }; -struct mt_fields { - unsigned usages[HID_MAX_FIELDS]; - unsigned int length; -}; - struct mt_report_data { struct list_head list; struct hid_report *report; @@ -158,8 +157,6 @@ struct mt_device { struct mt_class mtclass; /* our mt device class */ struct timer_list release_timer; /* to release sticky fingers */ struct hid_device *hdev; /* hid_device we're attached to */ - struct mt_fields *fields; /* temporary placeholder for storing the - multitouch fields */ unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */ __u8 inputmode_value; /* InputMode HID feature value */ __u8 maxcontacts; @@ -225,10 +222,11 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); * to a valid contact that was just read. */ -static int cypress_compute_slot(struct mt_application *app) +static int cypress_compute_slot(struct mt_application *application, + struct mt_usages *slot) { - if (app->curdata.contactid != 0 || app->num_received == 0) - return app->curdata.contactid; + if (*slot->contactid != 0 || application->num_received == 0) + return *slot->contactid; else return -1; } @@ -483,6 +481,34 @@ static void set_abs(struct input_dev *input, unsigned int code, input_abs_set_res(input, code, hidinput_calc_abs_res(field, code)); } +static struct mt_usages *mt_allocate_usage(struct hid_device *hdev, + struct mt_application *application) +{ + struct mt_usages *usage; + + usage = devm_kzalloc(&hdev->dev, sizeof(*usage), GFP_KERNEL); + if (!usage) + return NULL; + + /* set some defaults so we do not need to check for null pointers */ + usage->x = DEFAULT_ZERO; + usage->y = DEFAULT_ZERO; + usage->cx = DEFAULT_ZERO; + usage->cy = DEFAULT_ZERO; + usage->p = DEFAULT_ZERO; + usage->w = DEFAULT_ZERO; + usage->h = DEFAULT_ZERO; + usage->a = DEFAULT_ZERO; + usage->contactid = DEFAULT_ZERO; + usage->tip_state = DEFAULT_FALSE; + usage->inrange_state = DEFAULT_FALSE; + usage->confidence_state = DEFAULT_TRUE; + + list_add_tail(&usage->list, &application->mt_usages); + + return usage; +} + static struct mt_application *mt_allocate_application(struct mt_device *td, unsigned int application) { @@ -494,6 +520,7 @@ static struct mt_application *mt_allocate_application(struct mt_device *td, return NULL; mt_application->application = application; + INIT_LIST_HEAD(&mt_application->mt_usages); if (application == HID_DG_TOUCHSCREEN) mt_application->mt_flags |= INPUT_MT_DIRECT; @@ -506,8 +533,8 @@ static struct mt_application *mt_allocate_application(struct mt_device *td, td->inputmode_value = MT_INPUTMODE_TOUCHPAD; } - mt_application->cc_index = -1; - mt_application->scantime_index = -1; + mt_application->scantime = DEFAULT_ZERO; + mt_application->raw_cc = DEFAULT_ZERO; mt_application->quirks = td->mtclass.quirks; list_add_tail(&mt_application->list, &td->applications); @@ -587,17 +614,45 @@ static struct mt_report_data *mt_find_report_data(struct mt_device *td, return rdata; } -static void mt_store_field(struct hid_usage *usage, struct mt_device *td, - struct hid_input *hi) +static void mt_store_field(struct hid_device *hdev, + struct mt_application *application, + __s32 *value, + size_t offset) { - struct mt_fields *f = td->fields; + struct mt_usages *usage; + __s32 **target; + + if (list_empty(&application->mt_usages)) + usage = mt_allocate_usage(hdev, application); + else + usage = list_last_entry(&application->mt_usages, + struct mt_usages, + list); - if (f->length >= HID_MAX_FIELDS) + if (!usage) return; - f->usages[f->length++] = usage->hid; + target = (__s32 **)((char *)usage + offset); + + /* the value has already been filled, create a new slot */ + if (*target != DEFAULT_TRUE && + *target != DEFAULT_FALSE && + *target != DEFAULT_ZERO) { + usage = mt_allocate_usage(hdev, application); + if (!usage) + return; + + target = (__s32 **)((char *)usage + offset); + } + + *target = value; } +#define MT_STORE_FIELD(__name) \ + mt_store_field(hdev, app, \ + &field->value[usage->usage_index], \ + offsetof(struct mt_usages, __name)) + static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max, struct mt_application *app) @@ -627,24 +682,28 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: - if (prev_usage && (prev_usage->hid == usage->hid)) + if (prev_usage && (prev_usage->hid == usage->hid)) { code = ABS_MT_TOOL_X; - else + MT_STORE_FIELD(cx); + } else { code = ABS_MT_POSITION_X; + MT_STORE_FIELD(x); + } - hid_map_usage(hi, usage, bit, max, EV_ABS, code); set_abs(hi->input, code, field, cls->sn_move); - mt_store_field(usage, td, hi); + return 1; case HID_GD_Y: - if (prev_usage && (prev_usage->hid == usage->hid)) + if (prev_usage && (prev_usage->hid == usage->hid)) { code = ABS_MT_TOOL_Y; - else + MT_STORE_FIELD(cy); + } else { code = ABS_MT_POSITION_Y; + MT_STORE_FIELD(y); + } - hid_map_usage(hi, usage, bit, max, EV_ABS, code); set_abs(hi->input, code, field, cls->sn_move); - mt_store_field(usage, td, hi); + return 1; } return 0; @@ -653,40 +712,33 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, switch (usage->hid) { case HID_DG_INRANGE: if (app->quirks & MT_QUIRK_HOVERING) { - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_DISTANCE); input_set_abs_params(hi->input, ABS_MT_DISTANCE, 0, 1, 0, 0); } - mt_store_field(usage, td, hi); + MT_STORE_FIELD(inrange_state); return 1; case HID_DG_CONFIDENCE: if ((cls->name == MT_CLS_WIN_8 || cls->name == MT_CLS_WIN_8_DUAL) && field->application == HID_DG_TOUCHPAD) app->quirks |= MT_QUIRK_CONFIDENCE; - mt_store_field(usage, td, hi); + MT_STORE_FIELD(confidence_state); return 1; case HID_DG_TIPSWITCH: - hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); input_set_capability(hi->input, EV_KEY, BTN_TOUCH); - mt_store_field(usage, td, hi); + MT_STORE_FIELD(tip_state); return 1; case HID_DG_CONTACTID: - mt_store_field(usage, td, hi); + MT_STORE_FIELD(contactid); app->touches_by_report++; return 1; case HID_DG_WIDTH: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TOUCH_MAJOR); if (!(app->quirks & MT_QUIRK_NO_AREA)) set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, cls->sn_width); - mt_store_field(usage, td, hi); + MT_STORE_FIELD(w); return 1; case HID_DG_HEIGHT: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TOUCH_MINOR); if (!(app->quirks & MT_QUIRK_NO_AREA)) { set_abs(hi->input, ABS_MT_TOUCH_MINOR, field, cls->sn_height); @@ -700,37 +752,23 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, input_set_abs_params(hi->input, ABS_MT_ORIENTATION, 0, 1, 0, 0); } - mt_store_field(usage, td, hi); + MT_STORE_FIELD(h); return 1; case HID_DG_TIPPRESSURE: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_PRESSURE); set_abs(hi->input, ABS_MT_PRESSURE, field, cls->sn_pressure); - mt_store_field(usage, td, hi); + MT_STORE_FIELD(p); return 1; case HID_DG_SCANTIME: - hid_map_usage(hi, usage, bit, max, - EV_MSC, MSC_TIMESTAMP); input_set_capability(hi->input, EV_MSC, MSC_TIMESTAMP); - /* Ignore if indexes are out of bounds. */ - if (field->index >= field->report->maxfield || - usage->usage_index >= field->report_count) - return 1; - app->scantime_index = field->index; - app->scantime_val_index = usage->usage_index; + app->scantime = &field->value[usage->usage_index]; + app->scantime_logical_max = field->logical_maximum; return 1; case HID_DG_CONTACTCOUNT: - /* Ignore if indexes are out of bounds. */ - if (field->index >= field->report->maxfield || - usage->usage_index >= field->report_count) - return 1; - app->cc_index = field->index; - app->cc_value_index = usage->usage_index; + app->have_contact_count = true; + app->raw_cc = &field->value[usage->usage_index]; return 1; case HID_DG_AZIMUTH: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_ORIENTATION); /* * Azimuth has the range of [0, MAX) representing a full * revolution. Set ABS_MT_ORIENTATION to a quarter of @@ -741,11 +779,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->logical_maximum / 4, cls->sn_move ? field->logical_maximum / cls->sn_move : 0, 0); - mt_store_field(usage, td, hi); + MT_STORE_FIELD(a); return 1; case HID_DG_CONTACTMAX: - /* we don't set td->last_slot_field as contactcount and - * contact max are global to the report */ + /* contact max are global to the report */ return -1; case HID_DG_TOUCH: /* Legacy devices use TIPSWITCH and not TOUCH. @@ -778,95 +815,24 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, } static int mt_compute_slot(struct mt_device *td, struct mt_application *app, + struct mt_usages *slot, struct input_dev *input) { __s32 quirks = app->quirks; if (quirks & MT_QUIRK_SLOT_IS_CONTACTID) - return app->curdata.contactid; + return *slot->contactid; if (quirks & MT_QUIRK_CYPRESS) - return cypress_compute_slot(app); + return cypress_compute_slot(app, slot); if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER) return app->num_received; if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE) - return app->curdata.contactid - 1; + return *slot->contactid - 1; - return input_mt_get_slot_by_key(input, app->curdata.contactid); -} - -/* - * this function is called when a whole contact has been processed, - * so that it can assign it to a slot and store the data there - */ -static void mt_complete_slot(struct mt_device *td, struct mt_application *app, - struct input_dev *input) -{ - if ((app->quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) && - app->num_received >= app->num_expected) - return; - - if (app->curvalid || (app->quirks & MT_QUIRK_ALWAYS_VALID)) { - int active; - int slotnum = mt_compute_slot(td, app, input); - struct mt_slot *s = &app->curdata; - struct input_mt *mt = input->mt; - - if (slotnum < 0 || slotnum >= td->maxcontacts) - return; - - if ((app->quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) { - struct input_mt_slot *slot = &mt->slots[slotnum]; - if (input_mt_is_active(slot) && - input_mt_is_used(mt, slot)) - return; - } - - if (!(app->quirks & MT_QUIRK_CONFIDENCE)) - s->confidence_state = true; - active = (s->touch_state || s->inrange_state) && - s->confidence_state; - - input_mt_slot(input, slotnum); - input_mt_report_slot_state(input, MT_TOOL_FINGER, active); - if (active) { - /* this finger is in proximity of the sensor */ - int wide = (s->w > s->h); - int major = max(s->w, s->h); - int minor = min(s->w, s->h); - int orientation = wide; - - if (s->has_azimuth) - orientation = s->a; - - /* - * divided by two to match visual scale of touch - * for devices with this quirk - */ - if (app->quirks & MT_QUIRK_TOUCH_SIZE_SCALING) { - major = major >> 1; - minor = minor >> 1; - } - - input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); - input_event(input, EV_ABS, ABS_MT_TOOL_X, s->cx); - input_event(input, EV_ABS, ABS_MT_TOOL_Y, s->cy); - input_event(input, EV_ABS, ABS_MT_DISTANCE, - !s->touch_state); - input_event(input, EV_ABS, ABS_MT_ORIENTATION, - orientation); - input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); - input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); - input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); - - set_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); - } - } - - app->num_received++; + return input_mt_get_slot_by_key(input, *slot->contactid); } /* @@ -892,8 +858,7 @@ static void mt_sync_frame(struct mt_device *td, struct mt_application *app, clear_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); } -static int mt_compute_timestamp(struct mt_application *app, - struct hid_field *field, __s32 value) +static int mt_compute_timestamp(struct mt_application *app, __s32 value) { long delta = value - app->prev_scantime; unsigned long jdelta = jiffies_to_usecs(jiffies - app->jiffies); @@ -901,7 +866,7 @@ static int mt_compute_timestamp(struct mt_application *app, app->jiffies = jiffies; if (delta < 0) - delta += field->logical_maximum; + delta += app->scantime_logical_max; /* HID_DG_SCANTIME is expressed in 100us, we want it in us. */ delta *= 100; @@ -923,64 +888,69 @@ static int mt_touch_event(struct hid_device *hid, struct hid_field *field, return 1; } -static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value, - struct mt_application *app, bool first_packet) +static int mt_process_slot(struct mt_device *td, struct input_dev *input, + struct mt_application *app, + struct mt_usages *slot) { - struct mt_device *td = hid_get_drvdata(hid); + struct input_mt *mt = input->mt; __s32 quirks = app->quirks; - struct input_dev *input = field->hidinput->input; + bool valid = true; + bool confidence_state = true; + bool inrange_state = false; + int active; + int slotnum; - if (hid->claimed & HID_CLAIMED_INPUT) { - switch (usage->hid) { - case HID_DG_INRANGE: - if (quirks & MT_QUIRK_VALID_IS_INRANGE) - app->curvalid = value; - if (quirks & MT_QUIRK_HOVERING) - app->curdata.inrange_state = value; - break; - case HID_DG_TIPSWITCH: - if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) - app->curvalid = value; - app->curdata.touch_state = value; - break; - case HID_DG_CONFIDENCE: - if (quirks & MT_QUIRK_CONFIDENCE) - app->curdata.confidence_state = value; - if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE) - app->curvalid = value; - break; - case HID_DG_CONTACTID: - app->curdata.contactid = value; - break; - case HID_DG_TIPPRESSURE: - app->curdata.p = value; - break; - case HID_GD_X: - if (usage->code == ABS_MT_TOOL_X) - app->curdata.cx = value; - else - app->curdata.x = value; - break; - case HID_GD_Y: - if (usage->code == ABS_MT_TOOL_Y) - app->curdata.cy = value; - else - app->curdata.y = value; - break; - case HID_DG_WIDTH: - app->curdata.w = value; - break; - case HID_DG_HEIGHT: - app->curdata.h = value; - break; - case HID_DG_SCANTIME: - app->timestamp = mt_compute_timestamp(app, field, - value); - break; - case HID_DG_CONTACTCOUNT: - break; - case HID_DG_AZIMUTH: + if (!slot) + return -EINVAL; + + if ((quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) && + app->num_received >= app->num_expected) + return -EAGAIN; + + if (!(quirks & MT_QUIRK_ALWAYS_VALID)) { + if (quirks & MT_QUIRK_VALID_IS_INRANGE) + valid = *slot->inrange_state; + if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) + valid = *slot->tip_state; + if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE) + valid = *slot->confidence_state; + + if (!valid) + return 0; + } + + slotnum = mt_compute_slot(td, app, slot, input); + if (slotnum < 0 || slotnum >= td->maxcontacts) + return 0; + + if ((quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) { + struct input_mt_slot *i_slot = &mt->slots[slotnum]; + + if (input_mt_is_active(i_slot) && + input_mt_is_used(mt, i_slot)) + return -EAGAIN; + } + + if (quirks & MT_QUIRK_CONFIDENCE) + confidence_state = *slot->confidence_state; + + if (quirks & MT_QUIRK_HOVERING) + inrange_state = *slot->inrange_state; + + active = (*slot->tip_state || inrange_state) && confidence_state; + + input_mt_slot(input, slotnum); + input_mt_report_slot_state(input, MT_TOOL_FINGER, active); + if (active) { + /* this finger is in proximity of the sensor */ + int wide = (*slot->w > *slot->h); + int major = max(*slot->w, *slot->h); + int minor = min(*slot->w, *slot->h); + int orientation = wide; + int max_azimuth; + int azimuth; + + if (slot->a != DEFAULT_ZERO) { /* * Azimuth is counter-clockwise and ranges from [0, MAX) * (a full revolution). Convert it to clockwise ranging @@ -991,52 +961,76 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, * out of range to [-MAX/2, MAX/2] to report an upside * down ellipsis. */ - if (value > field->logical_maximum / 2) - value -= field->logical_maximum; - app->curdata.a = -value; - app->curdata.has_azimuth = true; - break; - case HID_DG_TOUCH: - /* do nothing */ - break; + azimuth = *slot->a; + max_azimuth = input_abs_get_max(input, + ABS_MT_ORIENTATION); + if (azimuth > max_azimuth * 2) + azimuth -= max_azimuth * 4; + orientation = -azimuth; + } - default: - /* - * For Win8 PTP touchpads we should only look at - * non finger/touch events in the first_packet of - * a (possible) multi-packet frame. - */ - if ((quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && - !first_packet) - return; + /* + * divided by two to match visual scale of touch + * for devices with this quirk + */ + if (quirks & MT_QUIRK_TOUCH_SIZE_SCALING) { + major = major >> 1; + minor = minor >> 1; + } - /* - * For Win8 PTP touchpads we map both the clickpad click - * and any "external" left buttons to BTN_LEFT if a - * device claims to have both we need to report 1 for - * BTN_LEFT if either is pressed, so we or all values - * together and report the result in mt_sync_frame(). - */ - if ((quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && - usage->type == EV_KEY && usage->code == BTN_LEFT) { - app->left_button_state |= value; - return; - } + input_event(input, EV_ABS, ABS_MT_POSITION_X, *slot->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, *slot->y); + input_event(input, EV_ABS, ABS_MT_TOOL_X, *slot->cx); + input_event(input, EV_ABS, ABS_MT_TOOL_Y, *slot->cy); + input_event(input, EV_ABS, ABS_MT_DISTANCE, !*slot->tip_state); + input_event(input, EV_ABS, ABS_MT_ORIENTATION, orientation); + input_event(input, EV_ABS, ABS_MT_PRESSURE, *slot->p); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + + set_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); + } - if (usage->type) - input_event(input, usage->type, usage->code, - value); + return 0; +} + +static void mt_process_mt_event(struct hid_device *hid, + struct mt_application *app, + struct hid_field *field, + struct hid_usage *usage, + __s32 value, + bool first_packet) +{ + __s32 quirks = app->quirks; + struct input_dev *input = field->hidinput->input; + + if (!usage->type || !(hid->claimed & HID_CLAIMED_INPUT)) + return; + + if (quirks & MT_QUIRK_WIN8_PTP_BUTTONS) { + + /* + * For Win8 PTP touchpads we should only look at + * non finger/touch events in the first_packet of a + * (possible) multi-packet frame. + */ + if (!first_packet) return; - } - if (usage->usage_index + 1 == field->report_count) { - /* we only take into account the last report. */ - if (usage->hid == app->last_slot_field) - mt_complete_slot(td, app, - field->hidinput->input); + /* + * For Win8 PTP touchpads we map both the clickpad click + * and any "external" left buttons to BTN_LEFT if a + * device claims to have both we need to report 1 for + * BTN_LEFT if either is pressed, so we or all values + * together and report the result in mt_sync_frame(). + */ + if (usage->type == EV_KEY && usage->code == BTN_LEFT) { + app->left_button_state |= value; + return; } - } + + input_event(input, usage->type, usage->code, value); } static void mt_touch_report(struct hid_device *hid, @@ -1046,6 +1040,8 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report = rdata->report; struct mt_application *app = rdata->application; struct hid_field *field; + struct input_dev *input; + struct mt_usages *slot; bool first_packet; unsigned count; int r, n; @@ -1056,18 +1052,16 @@ static void mt_touch_report(struct hid_device *hid, if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; + scantime = *app->scantime; + app->timestamp = mt_compute_timestamp(app, scantime); + if (app->raw_cc != DEFAULT_ZERO) + contact_count = *app->raw_cc; + /* * Includes multi-packet support where subsequent * packets are sent with zero contactcount. */ - if (app->scantime_index >= 0) { - field = report->field[app->scantime_index]; - scantime = field->value[app->scantime_val_index]; - } - if (app->cc_index >= 0) { - field = report->field[app->cc_index]; - contact_count = field->value[app->cc_value_index]; - + if (contact_count >= 0) { /* * For Win8 PTPs the first packet (td->num_received == 0) may * have a contactcount of 0 if there only is a button event. @@ -1086,6 +1080,14 @@ static void mt_touch_report(struct hid_device *hid, app->prev_scantime = scantime; first_packet = app->num_received == 0; + + input = report->field[0]->hidinput->input; + + list_for_each_entry(slot, &app->mt_usages, list) { + if (!mt_process_slot(td, input, app, slot)) + app->num_received++; + } + for (r = 0; r < report->maxfield; r++) { field = report->field[r]; count = field->report_count; @@ -1094,12 +1096,13 @@ static void mt_touch_report(struct hid_device *hid, continue; for (n = 0; n < count; n++) - mt_process_mt_event(hid, field, &field->usage[n], - field->value[n], app, first_packet); + mt_process_mt_event(hid, app, field, + &field->usage[n], field->value[n], + first_packet); } if (app->num_received >= app->num_expected) - mt_sync_frame(td, app, report->field[0]->hidinput->input); + mt_sync_frame(td, app, input); /* * Windows 8 specs says 2 things: @@ -1391,7 +1394,7 @@ static void mt_post_parse_default_settings(struct mt_device *td, __s32 quirks = app->quirks; /* unknown serial device needs special quirks */ - if (app->touches_by_report == 1) { + if (list_is_singular(&app->mt_usages)) { quirks |= MT_QUIRK_ALWAYS_VALID; quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; quirks &= ~MT_QUIRK_VALID_IS_INRANGE; @@ -1404,16 +1407,7 @@ static void mt_post_parse_default_settings(struct mt_device *td, static void mt_post_parse(struct mt_device *td, struct mt_application *app) { - struct mt_fields *f = td->fields; - - if (app->touches_by_report > 0) { - int field_count_per_touch; - - field_count_per_touch = f->length / app->touches_by_report; - app->last_slot_field = f->usages[field_count_per_touch - 1]; - } - - if (app->cc_index < 0) + if (!app->have_contact_count) app->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } @@ -1596,13 +1590,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) INIT_LIST_HEAD(&td->applications); INIT_LIST_HEAD(&td->reports); - td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields), - GFP_KERNEL); - if (!td->fields) { - dev_err(&hdev->dev, "cannot allocate multitouch fields data\n"); - return -ENOMEM; - } - if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) td->serial_maybe = true; @@ -1638,10 +1625,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true); - /* release .fields memory as it is not used anymore */ - devm_kfree(&hdev->dev, td->fields); - td->fields = NULL; - return 0; } -- cgit v1.2.3 From ba6b055e0f3b4ff4942e4ab273260affcfad9bff Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jul 2018 16:13:49 +0200 Subject: HID: input: enable Totem on the Dell Canvas 27 The Dell Canvas 27 has a tool that can be put on the surface and acts as a dial. The firmware processes the detection of the tool and forward regular HID reports with X, Y, Azimuth, rotation, width/height. The firmware also exports Contact ID, Countact Count which may hint that several totems can be used at the same time (the FW only supports one). We can tell that MT_TOOL_DIAL will be reported by setting the min/max of ABS_MT_TOOL_TYPE to MT_TOOL_DIAL. This tool is aimed at being used by the system and not the applications, so the user space processing should not go through the regular touch inputs. We set INPUT_PROP_DIRECT which applies ID_INPUT_TOUCHSCREEN to this new type of devices, but we will counter this for the time being with the special udev hwdb entry mentioned above. Link: https://bugzilla.redhat.com/show_bug.cgi?id=1511846 Acked-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 3 +++ drivers/hid/hid-multitouch.c | 48 +++++++++++++++++++++++++++++++------------- include/linux/hid.h | 6 ++++++ 3 files changed, 43 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index ab93dd5927c3..4e94ea3e280a 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1550,6 +1550,9 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid, case HID_GD_WIRELESS_RADIO_CTLS: suffix = "Wireless Radio Control"; break; + case HID_GD_SYSTEM_MULTIAXIS: + suffix = "System Multi Axis"; + break; default: break; } diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 346e9caef6f3..a4a274ebfbef 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -665,7 +665,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, /* * Model touchscreens providing buttons as touchpads. */ - if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { + if (field->application == HID_DG_TOUCHSCREEN && + (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { app->mt_flags |= INPUT_MT_POINTER; td->inputmode_value = MT_INPUTMODE_TOUCHPAD; } @@ -692,6 +693,19 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, set_abs(hi->input, code, field, cls->sn_move); + /* + * A system multi-axis that exports X and Y has a high + * chance of being used directly on a surface + */ + if (field->application == HID_GD_SYSTEM_MULTIAXIS) { + __set_bit(INPUT_PROP_DIRECT, + hi->input->propbit); + input_set_abs_params(hi->input, + ABS_MT_TOOL_TYPE, + MT_TOOL_DIAL, + MT_TOOL_DIAL, 0, 0); + } + return 1; case HID_GD_Y: if (prev_usage && (prev_usage->hid == usage->hid)) { @@ -725,7 +739,9 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, MT_STORE_FIELD(confidence_state); return 1; case HID_DG_TIPSWITCH: - input_set_capability(hi->input, EV_KEY, BTN_TOUCH); + if (field->application != HID_GD_SYSTEM_MULTIAXIS) + input_set_capability(hi->input, + EV_KEY, BTN_TOUCH); MT_STORE_FIELD(tip_state); return 1; case HID_DG_CONTACTID: @@ -802,6 +818,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->application == HID_DG_TOUCHPAD && (usage->hid & HID_USAGE) > 1) code--; + + if (field->application == HID_GD_SYSTEM_MULTIAXIS) + code = BTN_0 + ((usage->hid - 1) & HID_USAGE); + hid_map_usage(hi, usage, bit, max, EV_KEY, code); input_set_capability(hi->input, EV_KEY, code); return 1; @@ -899,6 +919,7 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, bool inrange_state = false; int active; int slotnum; + int tool = MT_TOOL_FINGER; if (!slot) return -EINVAL; @@ -939,8 +960,11 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, active = (*slot->tip_state || inrange_state) && confidence_state; + if (app->application == HID_GD_SYSTEM_MULTIAXIS) + tool = MT_TOOL_DIAL; + input_mt_slot(input, slotnum); - input_mt_report_slot_state(input, MT_TOOL_FINGER, active); + input_mt_report_slot_state(input, tool, active); if (active) { /* this finger is in proximity of the sensor */ int wide = (*slot->w > *slot->h); @@ -1203,6 +1227,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->application != HID_GD_SYSTEM_CONTROL && field->application != HID_CP_CONSUMER_CONTROL && field->application != HID_GD_WIRELESS_RADIO_CTLS && + field->application != HID_GD_SYSTEM_MULTIAXIS && !(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS && application->quirks & MT_QUIRK_ASUS_CUSTOM_UP)) return -1; @@ -1230,9 +1255,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } - if (rdata->is_mt_collection && - (field->application == HID_DG_TOUCHSCREEN || - field->application == HID_DG_TOUCHPAD)) + if (rdata->is_mt_collection) return mt_touch_input_mapping(hdev, hi, field, usage, bit, max, application); @@ -1244,15 +1267,11 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { - /* - * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" - * for the stylus. - */ - if (field->physical == HID_DG_STYLUS) - return 0; + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_report_data *rdata; - if (field->application == HID_DG_TOUCHSCREEN || - field->application == HID_DG_TOUCHPAD) { + rdata = mt_find_report_data(td, field->report); + if (rdata && rdata->is_mt_collection) { /* We own these mappings, tell hid-input to ignore them */ return -1; } @@ -1460,6 +1479,7 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) case HID_GD_SYSTEM_CONTROL: case HID_CP_CONSUMER_CONTROL: case HID_GD_WIRELESS_RADIO_CTLS: + case HID_GD_SYSTEM_MULTIAXIS: /* already handled by hid core */ break; case HID_DG_TOUCHSCREEN: diff --git a/include/linux/hid.h b/include/linux/hid.h index 773bcb1d4044..2e4498d52a2f 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -190,6 +190,12 @@ struct hid_item { * http://www.usb.org/developers/hidpage/HUTRR40RadioHIDUsagesFinal.pdf */ #define HID_GD_WIRELESS_RADIO_CTLS 0x0001000c +/* + * System Multi-Axis, see: + * http://www.usb.org/developers/hidpage/HUTRR62_-_Generic_Desktop_CA_for_System_Multi-Axis_Controllers.txt + */ +#define HID_GD_SYSTEM_MULTIAXIS 0x0001000e + #define HID_GD_X 0x00010030 #define HID_GD_Y 0x00010031 #define HID_GD_Z 0x00010032 -- cgit v1.2.3 From 08a8a7cf14595f95d5cbb28ef5c15c56a6255fb4 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jul 2018 16:13:50 +0200 Subject: HID: core: do not upper bound the collection stack Looks like 4 was sufficient until now. However, the Surface Dial needs a stack of 5 and simply fails at probing. Dynamically add HID_COLLECTION_STACK_SIZE to the size of the stack if we hit the upper bound. Checkpatch complains about bare unsigned, so converting those to 'unsigned int' in struct hid_parser Acked-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 17 ++++++++++++++--- include/linux/hid.h | 9 +++++---- 2 files changed, 19 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 3942ee61bd1c..5de6f18c9bf7 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -128,9 +128,19 @@ static int open_collection(struct hid_parser *parser, unsigned type) usage = parser->local.usage[0]; - if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { - hid_err(parser->device, "collection stack overflow\n"); - return -EINVAL; + if (parser->collection_stack_ptr == parser->collection_stack_size) { + unsigned int *collection_stack; + unsigned int new_size = parser->collection_stack_size + + HID_COLLECTION_STACK_SIZE; + + collection_stack = krealloc(parser->collection_stack, + new_size * sizeof(unsigned int), + GFP_KERNEL); + if (!collection_stack) + return -ENOMEM; + + parser->collection_stack = collection_stack; + parser->collection_stack_size = new_size; } if (parser->device->maxcollection == parser->device->collection_size) { @@ -840,6 +850,7 @@ static int hid_scan_report(struct hid_device *hid) break; } + kfree(parser->collection_stack); vfree(parser); return 0; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 2e4498d52a2f..aee281522c6d 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -644,12 +644,13 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data) struct hid_parser { struct hid_global global; struct hid_global global_stack[HID_GLOBAL_STACK_SIZE]; - unsigned global_stack_ptr; + unsigned int global_stack_ptr; struct hid_local local; - unsigned collection_stack[HID_COLLECTION_STACK_SIZE]; - unsigned collection_stack_ptr; + unsigned int *collection_stack; + unsigned int collection_stack_ptr; + unsigned int collection_stack_size; struct hid_device *device; - unsigned scan_flags; + unsigned int scan_flags; }; struct hid_class_descriptor { -- cgit v1.2.3 From 30576c5f490cb220ae8c1dcd918605ac1c4c103e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jul 2018 16:13:51 +0200 Subject: HID: microsoft: support the Surface Dial The tool works nicely with hid-generic, but it ends up creating 9 different input nodes with most of them only having ABS_MISC set. Filter the axis out, which reduces the amount of devices to 2. One is the proper System Multi-axis collection, the other exported device seems to provide SLEEP and POWER Key, not sure how one can trigger those events though. Filtering the ABS_X and ABS_Y axes also prevents udev to detect this as a touchscreen. Acked-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-microsoft.c | 49 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 96e7d3231d2f..72d983626afd 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -22,12 +22,13 @@ #include "hid-ids.h" -#define MS_HIDINPUT 0x01 -#define MS_ERGONOMY 0x02 -#define MS_PRESENTER 0x04 -#define MS_RDESC 0x08 -#define MS_NOGET 0x10 -#define MS_DUPLICATE_USAGES 0x20 +#define MS_HIDINPUT BIT(0) +#define MS_ERGONOMY BIT(1) +#define MS_PRESENTER BIT(2) +#define MS_RDESC BIT(3) +#define MS_NOGET BIT(4) +#define MS_DUPLICATE_USAGES BIT(5) +#define MS_SURFACE_DIAL BIT(6) static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) @@ -130,6 +131,30 @@ static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage, return 1; } +static int ms_surface_dial_quirk(struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + switch (usage->hid & HID_USAGE_PAGE) { + case 0xff070000: + /* fall-through */ + case HID_UP_DIGITIZER: + /* ignore those axis */ + return -1; + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: + /* fall-through */ + case HID_GD_Y: + /* fall-through */ + case HID_GD_RFKILL_BTN: + /* ignore those axis */ + return -1; + } + } + + return 0; +} + static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -146,6 +171,13 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, ms_presenter_8k_quirk(hi, usage, bit, max)) return 1; + if (quirks & MS_SURFACE_DIAL) { + int ret = ms_surface_dial_quirk(hi, field, usage, bit, max); + + if (ret) + return ret; + } + return 0; } @@ -229,6 +261,9 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) if (quirks & MS_NOGET) hdev->quirks |= HID_QUIRK_NOGET; + if (quirks & MS_SURFACE_DIAL) + hdev->quirks |= HID_QUIRK_INPUT_PER_APP; + ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); @@ -281,6 +316,8 @@ static const struct hid_device_id ms_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), .driver_data = MS_PRESENTER }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B), + .driver_data = MS_SURFACE_DIAL }, { } }; MODULE_DEVICE_TABLE(hid, ms_devices); -- cgit v1.2.3 From 9152c7d77d87e2b6b9ccc21bb52f8eea15744f50 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 13 Jul 2018 16:13:52 +0200 Subject: HID: multitouch: report MT_TOOL_PALM for non-confident touches According to Microsoft specification [1] for Precision Touchpads (and Touchscreens) the devices use "confidence" reports to signal accidental touches, or contacts that are "too large to be a finger". Instead of simply marking contact inactive in this case (which causes issues if contact was originally proper and we lost confidence in it later, as this results in accidental clicks, drags, etc), let's report such contacts as MT_TOOL_PALM and let userspace decide what to do. [1] https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchpad-windows-precision-touchpad-collection Signed-off-by: Dmitry Torokhov [splitted and rebased] Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a4a274ebfbef..587369443f50 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -736,6 +736,13 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, cls->name == MT_CLS_WIN_8_DUAL) && field->application == HID_DG_TOUCHPAD) app->quirks |= MT_QUIRK_CONFIDENCE; + + if (app->quirks & MT_QUIRK_CONFIDENCE) + input_set_abs_params(hi->input, + ABS_MT_TOOL_TYPE, + MT_TOOL_FINGER, + MT_TOOL_PALM, 0, 0); + MT_STORE_FIELD(confidence_state); return 1; case HID_DG_TIPSWITCH: @@ -958,10 +965,12 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, if (quirks & MT_QUIRK_HOVERING) inrange_state = *slot->inrange_state; - active = (*slot->tip_state || inrange_state) && confidence_state; + active = *slot->tip_state || inrange_state; if (app->application == HID_GD_SYSTEM_MULTIAXIS) tool = MT_TOOL_DIAL; + else if (unlikely(!confidence_state)) + tool = MT_TOOL_PALM; input_mt_slot(input, slotnum); input_mt_report_slot_state(input, tool, active); @@ -993,11 +1002,11 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, orientation = -azimuth; } - /* - * divided by two to match visual scale of touch - * for devices with this quirk - */ if (quirks & MT_QUIRK_TOUCH_SIZE_SCALING) { + /* + * divided by two to match visual scale of touch + * for devices with this quirk + */ major = major >> 1; minor = minor >> 1; } -- cgit v1.2.3 From f9024374592560ab7fac327fbacbc66aab9d6c63 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 13 Jul 2018 16:13:53 +0200 Subject: HID: multitouch: touchscreens also use confidence reports According to [1] the confidence is used not only by touchpad devices, but also by touchscreens. [1] https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchscreen-required-hid-top-level-collections Acked-by: Peter Hutterer Signed-off-by: Dmitry Torokhov [rebased] Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 587369443f50..3464adab0085 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -734,7 +734,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_CONFIDENCE: if ((cls->name == MT_CLS_WIN_8 || cls->name == MT_CLS_WIN_8_DUAL) && - field->application == HID_DG_TOUCHPAD) + (field->application == HID_DG_TOUCHPAD || + field->application == HID_DG_TOUCHSCREEN)) app->quirks |= MT_QUIRK_CONFIDENCE; if (app->quirks & MT_QUIRK_CONFIDENCE) -- cgit v1.2.3 From 28a042a3b7ab3e28039d6309f0764a8e4c1b261d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 13 Jul 2018 16:13:54 +0200 Subject: HID: multitouch: handle palm for touchscreens Usually, there is no palm rejection for touchscreens. You don't rest your palm on the touchscreen while interacting with it. However, some wacom devices do so because you can rest your palm while interacting with the stylus. Unfortunately, the spec for touchscreens[1] is less precise than the one for touchpads[2]. This leads to a situation where it's 'legitimate' for a touchscreen to provide both tipswitch off and confidence off in the same report. Work around that by keeping the slot active for one frame where we report MT_TOOL_PALM, and then synthesizing the release event in a separate frame. frame Acked-by: Peter Hutterer Signed-off-by: Dmitry Torokhov [rebased and new commit message] Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 52 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 3464adab0085..40fbb7c52723 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -118,6 +118,9 @@ struct mt_application { int left_button_state; /* left button state */ unsigned int mt_flags; /* flags to pass to input-mt */ + unsigned long *pending_palm_slots; /* slots where we reported palm + * and need to release */ + __u8 num_received; /* how many contacts we received */ __u8 num_expected; /* expected last contact index */ __u8 buttons_count; /* number of physical buttons per touchpad */ @@ -863,6 +866,28 @@ static int mt_compute_slot(struct mt_device *td, struct mt_application *app, return input_mt_get_slot_by_key(input, *slot->contactid); } +static void mt_release_pending_palms(struct mt_device *td, + struct mt_application *app, + struct input_dev *input) +{ + int slotnum; + bool need_sync = false; + + for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) { + clear_bit(slotnum, app->pending_palm_slots); + + input_mt_slot(input, slotnum); + input_mt_report_slot_state(input, MT_TOOL_PALM, false); + + need_sync = true; + } + + if (need_sync) { + input_mt_sync_frame(input); + input_sync(input); + } +} + /* * this function is called when a whole packet has been received and processed, * so that it can decide what to send to the input layer. @@ -876,6 +901,9 @@ static void mt_sync_frame(struct mt_device *td, struct mt_application *app, input_mt_sync_frame(input); input_event(input, EV_MSC, MSC_TIMESTAMP, app->timestamp); input_sync(input); + + mt_release_pending_palms(td, app, input); + app->num_received = 0; app->left_button_state = 0; @@ -970,8 +998,23 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, if (app->application == HID_GD_SYSTEM_MULTIAXIS) tool = MT_TOOL_DIAL; - else if (unlikely(!confidence_state)) + else if (unlikely(!confidence_state)) { tool = MT_TOOL_PALM; + if (!active && + input_mt_is_active(&mt->slots[slotnum])) { + /* + * The non-confidence was reported for + * previously valid contact that is also no + * longer valid. We can't simply report + * lift-off as userspace will not be aware + * of non-confidence, so we need to split + * it into 2 events: active MT_TOOL_PALM + * and a separate liftoff. + */ + active = true; + set_bit(slotnum, app->pending_palm_slots); + } + } input_mt_slot(input, slotnum); input_mt_report_slot_state(input, tool, active); @@ -1197,6 +1240,13 @@ static int mt_touch_input_configured(struct hid_device *hdev, if (td->is_buttonpad) __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + app->pending_palm_slots = devm_kcalloc(&hi->input->dev, + BITS_TO_LONGS(td->maxcontacts), + sizeof(long), + GFP_KERNEL); + if (!app->pending_palm_slots) + return -ENOMEM; + ret = input_mt_init_slots(input, td->maxcontacts, app->mt_flags); if (ret) return ret; -- cgit v1.2.3 From 7f342e9c61d7932b7b3dd8a0b9c2768fe7ca3436 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 2 Jul 2018 10:40:28 +0100 Subject: HID: intel-ish-hid: remove redundant variable num_frags Variable num_frags is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'num_frags' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/hbm.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c index ae4a69f7f2f4..8b5dd580ceec 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.c +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c @@ -298,7 +298,6 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr = &hdr; const size_t len = sizeof(struct hbm_flow_control); int rv; - unsigned int num_frags; unsigned long flags; spin_lock_irqsave(&cl->fc_spinlock, flags); @@ -314,7 +313,6 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, return 0; } - num_frags = cl->recv_msg_num_frags; cl->recv_msg_num_frags = 0; rv = ishtp_write_message(dev, ishtp_hdr, data); -- cgit v1.2.3 From 1a8861f117608e269647fb09658ce77bd5899d42 Mon Sep 17 00:00:00 2001 From: "Daniel M. Lambea" Date: Tue, 17 Jul 2018 22:35:36 +0100 Subject: HID: cougar: make compare_device_paths reusable The function compare_device_paths from wacom_sys.c is generic and useful for other drivers. Move the function to hid-core and rename it as hid_compare_device_paths. Signed-off-by: Daniel M. Lambea Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 23 +++++++++++++++++++++++ drivers/hid/wacom_sys.c | 18 +++--------------- include/linux/hid.h | 2 ++ 3 files changed, 28 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 3942ee61bd1c..402ad974b31c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1939,6 +1939,29 @@ static int hid_bus_match(struct device *dev, struct device_driver *drv) return hid_match_device(hdev, hdrv) != NULL; } +/** + * hid_compare_device_paths - check if both devices share the same path + * @hdev_a: hid device + * @hdev_b: hid device + * @separator: char to use as separator + * + * Check if two devices share the same path up to the last occurrence of + * the separator char. Both paths must exist (i.e., zero-length paths + * don't match). + */ +bool hid_compare_device_paths(struct hid_device *hdev_a, + struct hid_device *hdev_b, char separator) +{ + int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys; + int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys; + + if (n1 != n2 || n1 <= 0 || n2 <= 0) + return false; + + return !strncmp(hdev_a->phys, hdev_b->phys, n1); +} +EXPORT_SYMBOL_GPL(hid_compare_device_paths); + static int hid_device_probe(struct device *dev) { struct hid_driver *hdrv = to_hid_driver(dev->driver); diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index d6797535fff9..ffe59a19b3a3 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -703,18 +703,6 @@ struct wacom_hdev_data { static LIST_HEAD(wacom_udev_list); static DEFINE_MUTEX(wacom_udev_list_lock); -static bool compare_device_paths(struct hid_device *hdev_a, - struct hid_device *hdev_b, char separator) -{ - int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys; - int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys; - - if (n1 != n2 || n1 <= 0 || n2 <= 0) - return false; - - return !strncmp(hdev_a->phys, hdev_b->phys, n1); -} - static bool wacom_are_sibling(struct hid_device *hdev, struct hid_device *sibling) { @@ -737,10 +725,10 @@ static bool wacom_are_sibling(struct hid_device *hdev, * the same physical parent device path. */ if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) { - if (!compare_device_paths(hdev, sibling, '/')) + if (!hid_compare_device_paths(hdev, sibling, '/')) return false; } else { - if (!compare_device_paths(hdev, sibling, '.')) + if (!hid_compare_device_paths(hdev, sibling, '.')) return false; } @@ -787,7 +775,7 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) /* Try to find an already-probed interface from the same device */ list_for_each_entry(data, &wacom_udev_list, list) { - if (compare_device_paths(hdev, data->dev, '/')) { + if (hid_compare_device_paths(hdev, data->dev, '/')) { kref_get(&data->kref); return data; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 773bcb1d4044..938d9ba6d7cd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -894,6 +894,8 @@ const struct hid_device_id *hid_match_id(const struct hid_device *hdev, const struct hid_device_id *id); const struct hid_device_id *hid_match_device(struct hid_device *hdev, struct hid_driver *hdrv); +bool hid_compare_device_paths(struct hid_device *hdev_a, + struct hid_device *hdev_b, char separator); s32 hid_snto32(__u32 value, unsigned n); __u32 hid_field_extract(const struct hid_device *hid, __u8 *report, unsigned offset, unsigned n); -- cgit v1.2.3 From b8e759b8f6dab1c473c30ac12709095d0b81078e Mon Sep 17 00:00:00 2001 From: "Daniel M. Lambea" Date: Tue, 17 Jul 2018 22:35:37 +0100 Subject: HID: cougar: Add support for the Cougar 500k Gaming Keyboard Cougar 500k Gaming Keyboard have some special function keys that make the keyboard stop responding once pressed. Implement the custom vendor interface that deals with the extended keypresses to fix. The bug can be reproduced by plugging in the keyboard, then pressing the rightmost part of the spacebar. Signed-off-by: Daniel M. Lambea Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 10 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-cougar.c | 312 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-ids.h | 3 + 4 files changed, 326 insertions(+) create mode 100644 drivers/hid/hid-cougar.c (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a49a10437c40..61e1953ff921 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -207,6 +207,16 @@ config HID_CORSAIR - Vengeance K90 - Scimitar PRO RGB +config HID_COUGAR + tristate "Cougar devices" + depends on HID + help + Support for Cougar devices that are not fully compliant with the + HID standard. + + Supported devices: + - Cougar 500k Gaming Keyboard + config HID_PRODIKEYS tristate "Prodikeys PC-MIDI Keyboard support" depends on HID && SND diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 511e1cbff768..bd7ac53b75c5 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o +obj-$(CONFIG_HID_COUGAR) += hid-cougar.o obj-$(CONFIG_HID_CP2112) += hid-cp2112.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o diff --git a/drivers/hid/hid-cougar.c b/drivers/hid/hid-cougar.c new file mode 100644 index 000000000000..ad2e87de7dc5 --- /dev/null +++ b/drivers/hid/hid-cougar.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for Cougar 500k Gaming Keyboard + * + * Copyright (c) 2018 Daniel M. Lambea + */ + +#include +#include + +#include "hid-ids.h" + +MODULE_AUTHOR("Daniel M. Lambea "); +MODULE_DESCRIPTION("Cougar 500k Gaming Keyboard"); +MODULE_LICENSE("GPL"); +MODULE_INFO(key_mappings, "G1-G6 are mapped to F13-F18"); + +static int cougar_g6_is_space = 1; +module_param_named(g6_is_space, cougar_g6_is_space, int, 0600); +MODULE_PARM_DESC(g6_is_space, + "If set, G6 programmable key sends SPACE instead of F18 (0=off, 1=on) (default=1)"); + + +#define COUGAR_VENDOR_USAGE 0xff00ff00 + +#define COUGAR_FIELD_CODE 1 +#define COUGAR_FIELD_ACTION 2 + +#define COUGAR_KEY_G1 0x83 +#define COUGAR_KEY_G2 0x84 +#define COUGAR_KEY_G3 0x85 +#define COUGAR_KEY_G4 0x86 +#define COUGAR_KEY_G5 0x87 +#define COUGAR_KEY_G6 0x78 +#define COUGAR_KEY_FN 0x0d +#define COUGAR_KEY_MR 0x6f +#define COUGAR_KEY_M1 0x80 +#define COUGAR_KEY_M2 0x81 +#define COUGAR_KEY_M3 0x82 +#define COUGAR_KEY_LEDS 0x67 +#define COUGAR_KEY_LOCK 0x6e + + +/* Default key mappings. The special key COUGAR_KEY_G6 is defined first + * because it is more frequent to use the spacebar rather than any other + * special keys. Depending on the value of the parameter 'g6_is_space', + * the mapping will be updated in the probe function. + */ +static unsigned char cougar_mapping[][2] = { + { COUGAR_KEY_G6, KEY_SPACE }, + { COUGAR_KEY_G1, KEY_F13 }, + { COUGAR_KEY_G2, KEY_F14 }, + { COUGAR_KEY_G3, KEY_F15 }, + { COUGAR_KEY_G4, KEY_F16 }, + { COUGAR_KEY_G5, KEY_F17 }, + { COUGAR_KEY_LOCK, KEY_SCREENLOCK }, +/* The following keys are handled by the hardware itself, so no special + * treatment is required: + { COUGAR_KEY_FN, KEY_RESERVED }, + { COUGAR_KEY_MR, KEY_RESERVED }, + { COUGAR_KEY_M1, KEY_RESERVED }, + { COUGAR_KEY_M2, KEY_RESERVED }, + { COUGAR_KEY_M3, KEY_RESERVED }, + { COUGAR_KEY_LEDS, KEY_RESERVED }, +*/ + { 0, 0 }, +}; + +struct cougar_shared { + struct list_head list; + struct kref kref; + bool enabled; + struct hid_device *dev; + struct input_dev *input; +}; + +struct cougar { + bool special_intf; + struct cougar_shared *shared; +}; + +static LIST_HEAD(cougar_udev_list); +static DEFINE_MUTEX(cougar_udev_list_lock); + +static void cougar_fix_g6_mapping(struct hid_device *hdev) +{ + int i; + + for (i = 0; cougar_mapping[i][0]; i++) { + if (cougar_mapping[i][0] == COUGAR_KEY_G6) { + cougar_mapping[i][1] = + cougar_g6_is_space ? KEY_SPACE : KEY_F18; + hid_info(hdev, "G6 mapped to %s\n", + cougar_g6_is_space ? "space" : "F18"); + return; + } + } + hid_warn(hdev, "no mapping defined for G6/spacebar"); +} + +/* + * Constant-friendly rdesc fixup for mouse interface + */ +static __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + if (rdesc[2] == 0x09 && rdesc[3] == 0x02 && + (rdesc[115] | rdesc[116] << 8) >= HID_MAX_USAGES) { + hid_info(hdev, + "usage count exceeds max: fixing up report descriptor\n"); + rdesc[115] = ((HID_MAX_USAGES-1) & 0xff); + rdesc[116] = ((HID_MAX_USAGES-1) >> 8); + } + return rdesc; +} + +static struct cougar_shared *cougar_get_shared_data(struct hid_device *hdev) +{ + struct cougar_shared *shared; + + /* Try to find an already-probed interface from the same device */ + list_for_each_entry(shared, &cougar_udev_list, list) { + if (hid_compare_device_paths(hdev, shared->dev, '/')) { + kref_get(&shared->kref); + return shared; + } + } + return NULL; +} + +static void cougar_release_shared_data(struct kref *kref) +{ + struct cougar_shared *shared = container_of(kref, + struct cougar_shared, kref); + + mutex_lock(&cougar_udev_list_lock); + list_del(&shared->list); + mutex_unlock(&cougar_udev_list_lock); + + kfree(shared); +} + +static void cougar_remove_shared_data(void *resource) +{ + struct cougar *cougar = resource; + + if (cougar->shared) { + kref_put(&cougar->shared->kref, cougar_release_shared_data); + cougar->shared = NULL; + } +} + +/* + * Bind the device group's shared data to this cougar struct. + * If no shared data exists for this group, create and initialize it. + */ +static int cougar_bind_shared_data(struct hid_device *hdev, struct cougar *cougar) +{ + struct cougar_shared *shared; + int error = 0; + + mutex_lock(&cougar_udev_list_lock); + + shared = cougar_get_shared_data(hdev); + if (!shared) { + shared = kzalloc(sizeof(*shared), GFP_KERNEL); + if (!shared) { + error = -ENOMEM; + goto out; + } + + kref_init(&shared->kref); + shared->dev = hdev; + list_add_tail(&shared->list, &cougar_udev_list); + } + + cougar->shared = shared; + + error = devm_add_action(&hdev->dev, cougar_remove_shared_data, cougar); + if (error) { + mutex_unlock(&cougar_udev_list_lock); + cougar_remove_shared_data(cougar); + return error; + } + +out: + mutex_unlock(&cougar_udev_list_lock); + return error; +} + +static int cougar_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct cougar *cougar; + struct hid_input *next, *hidinput = NULL; + unsigned int connect_mask; + int error; + + cougar = devm_kzalloc(&hdev->dev, sizeof(*cougar), GFP_KERNEL); + if (!cougar) + return -ENOMEM; + hid_set_drvdata(hdev, cougar); + + error = hid_parse(hdev); + if (error) { + hid_err(hdev, "parse failed\n"); + goto fail; + } + + if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { + cougar->special_intf = true; + connect_mask = HID_CONNECT_HIDRAW; + } else + connect_mask = HID_CONNECT_DEFAULT; + + error = hid_hw_start(hdev, connect_mask); + if (error) { + hid_err(hdev, "hw start failed\n"); + goto fail; + } + + error = cougar_bind_shared_data(hdev, cougar); + if (error) + goto fail_stop_and_cleanup; + + /* The custom vendor interface will use the hid_input registered + * for the keyboard interface, in order to send translated key codes + * to it. + */ + if (hdev->collection->usage == HID_GD_KEYBOARD) { + cougar_fix_g6_mapping(hdev); + list_for_each_entry_safe(hidinput, next, &hdev->inputs, list) { + if (hidinput->registered && hidinput->input != NULL) { + cougar->shared->input = hidinput->input; + cougar->shared->enabled = true; + break; + } + } + } else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { + error = hid_hw_open(hdev); + if (error) + goto fail_stop_and_cleanup; + } + return 0; + +fail_stop_and_cleanup: + hid_hw_stop(hdev); +fail: + hid_set_drvdata(hdev, NULL); + return error; +} + +/* + * Convert events from vendor intf to input key events + */ +static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct cougar *cougar; + unsigned char code, action; + int i; + + cougar = hid_get_drvdata(hdev); + if (!cougar->special_intf || !cougar->shared || + !cougar->shared->input || !cougar->shared->enabled) + return 0; + + code = data[COUGAR_FIELD_CODE]; + action = data[COUGAR_FIELD_ACTION]; + for (i = 0; cougar_mapping[i][0]; i++) { + if (code == cougar_mapping[i][0]) { + input_event(cougar->shared->input, EV_KEY, + cougar_mapping[i][1], action); + input_sync(cougar->shared->input); + return 0; + } + } + hid_warn(hdev, "unmapped special key code %x: ignoring\n", code); + return 0; +} + +static void cougar_remove(struct hid_device *hdev) +{ + struct cougar *cougar = hid_get_drvdata(hdev); + + if (cougar) { + /* Stop the vendor intf to process more events */ + if (cougar->shared) + cougar->shared->enabled = false; + if (cougar->special_intf) + hid_hw_close(hdev); + } + hid_hw_stop(hdev); +} + +static struct hid_device_id cougar_id_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, + USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) }, + {} +}; +MODULE_DEVICE_TABLE(hid, cougar_id_table); + +static struct hid_driver cougar_driver = { + .name = "cougar", + .id_table = cougar_id_table, + .report_fixup = cougar_report_fixup, + .probe = cougar_probe, + .remove = cougar_remove, + .raw_event = cougar_raw_event, +}; + +module_hid_driver(cougar_driver); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c7981ddd8776..a1ee8f643f24 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1001,6 +1001,9 @@ #define USB_VENDOR_ID_SINO_LITE 0x1345 #define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008 +#define USB_VENDOR_ID_SOLID_YEAR 0x060b +#define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD 0x500a + #define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046 -- cgit v1.2.3 From a1e9a9c0dfe47a3f13734be489b7953a2f8bdd83 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 18 Jul 2018 12:32:00 -0700 Subject: HID: intel-ish-hid: Prevent loading of driver on Mehlow On Mehlow Xeon-E workstation, ISH PCI device is enabled but without ISH firmware. Here the ISH device PCI device id was reused for some non Linux storage drivers. So this was not done for enabling ISH. But this has a undesirable side effect for Linux. Here the ISH driver will be loaded via PCI enumeration and will try to do reset sequence. But reset sequence will wait till timeout as there is no real ISH firmware is present to take action. This delay will add to boot time of Linux (This platform will still continue to boot after this timeout). To avoid this boot delay we need to prevent loading of ISH drivers on this platform. So we need to have hack to avoid treating this device as ISH on this platform. To identify this workstation, we need some runtime method. Luckily there are special PCI id on this workstation to distinguish from the client version of this platform. On client version, the ISH is supported using same PCI device id. So this change look for the presence of PCI device IDs A309 and A30A and exit. Signed-off-by: Srinivas Pandruvada Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/pci-ish.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index a2c53ea3b5ed..4a55eab39b88 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -95,6 +95,13 @@ static int ish_init(struct ishtp_device *dev) return 0; } +static const struct pci_device_id ish_invalid_pci_ids[] = { + /* Mehlow platform special pci ids */ + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)}, + {} +}; + /** * ish_probe() - PCI driver probe callback * @pdev: pci device @@ -110,6 +117,10 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct ish_hw *hw; int ret; + /* Check for invalid platforms for ISH support */ + if (pci_dev_present(ish_invalid_pci_ids)) + return -ENODEV; + /* enable pci dev */ ret = pci_enable_device(pdev); if (ret) { -- cgit v1.2.3 From 50fa92594a2bc76265f4bb357f9a25dd3bdb2c0a Mon Sep 17 00:00:00 2001 From: Anton Vasilyev Date: Wed, 1 Aug 2018 14:26:51 +0300 Subject: HID: intel_ish-hid: tx_buf memory leak on probe/remove ish_dev_init() allocates 512*176 bytes memory for tx_buf and stores it at &dev->wr_free_list_head.link list on ish_probe(). But there is no deallocation of this memory in ish_remove() and in ish_probe() error path. So current intel-ish-ipc provides 88 KB memory leak for each probe/release. The patch replaces kzalloc allocation by devm_kzalloc and removes ishtp_device *dev deallocation by kfree. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Anton Vasilyev Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/ipc.c | 9 ++++++--- drivers/hid/intel-ish-hid/ipc/pci-ish.c | 2 -- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 9a60ec13cb10..bfbca7ec54ce 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -907,8 +907,9 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev) struct ishtp_device *dev; int i; - dev = kzalloc(sizeof(struct ishtp_device) + sizeof(struct ish_hw), - GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, + sizeof(struct ishtp_device) + sizeof(struct ish_hw), + GFP_KERNEL); if (!dev) return NULL; @@ -925,7 +926,9 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev) for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) { struct wr_msg_ctl_info *tx_buf; - tx_buf = kzalloc(sizeof(struct wr_msg_ctl_info), GFP_KERNEL); + tx_buf = devm_kzalloc(&pdev->dev, + sizeof(struct wr_msg_ctl_info), + GFP_KERNEL); if (!tx_buf) { /* * IPC buffers may be limited or not available diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 4a55eab39b88..050f9872f5c0 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -183,7 +183,6 @@ free_irq: free_irq(pdev->irq, dev); free_device: pci_iounmap(pdev, hw->mem_addr); - kfree(dev); release_regions: pci_release_regions(pdev); disable_device: @@ -213,7 +212,6 @@ static void ish_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_clear_master(pdev); pci_disable_device(pdev); - kfree(ishtp_dev); } static struct device __maybe_unused *ish_resume_device; -- cgit v1.2.3