lkml.org 
[lkml]   [2017]   [Apr]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 094/152] HID: asus: Add i2c touchpad support
    Date
    4.9-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Brendan McGrath <redmcg@redmandi.dyndns.org>

    [ Upstream commit 9ce12d8be12c94334634dd57050444910415e45f ]

    Update the hid-asus module to add multitouch support for the Asus i2c touchpad.

    This patch aims to resolve the issue raised here:
    https://bugzilla.kernel.org/show_bug.cgi?id=120181

    The issue is in relation to an Asus touchpad device which currently does not
    have multitouch support.

    The device currently falls through to the hid-generic driver which
    treats the device as a mouse.

    This patch aims to add the multitouch support.

    [jkosina@suse.cz: move most of the 'patch comment' into actual changelog]
    [jkosina@suse.cz: drop hunk that changes ->name of the driver]
    Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
    Signed-off-by: Brendan McGrath <redmcg@redmandi.dyndns.org>
    Signed-off-by: Victor Vlasenko <victor.vlasenko@sysgears.com>
    Signed-off-by: Frederik Wenigwieser <frederik.wenigwieser@gmail.com>
    Signed-off-by: Jiri Kosina <jkosina@suse.cz>
    Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/hid/Kconfig | 2
    drivers/hid/hid-asus.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++-
    drivers/hid/hid-core.c | 1
    drivers/hid/hid-ids.h | 1
    4 files changed, 296 insertions(+), 7 deletions(-)

    --- a/drivers/hid/Kconfig
    +++ b/drivers/hid/Kconfig
    @@ -138,7 +138,7 @@ config HID_ASUS
    tristate "Asus"
    depends on I2C_HID
    ---help---
    - Support for Asus notebook built-in keyboard via i2c.
    + Support for Asus notebook built-in keyboard and touchpad via i2c.

    Supported devices:
    - EeeBook X205TA
    --- a/drivers/hid/hid-asus.c
    +++ b/drivers/hid/hid-asus.c
    @@ -11,6 +11,12 @@
    * This module based on hid-ortek by
    * Copyright (c) 2010 Johnathon Harris <jmharris@gmail.com>
    * Copyright (c) 2011 Jiri Kosina
    + *
    + * This module has been updated to add support for Asus i2c touchpad.
    + *
    + * Copyright (c) 2016 Brendan McGrath <redmcg@redmandi.dyndns.org>
    + * Copyright (c) 2016 Victor Vlasenko <victor.vlasenko@sysgears.com>
    + * Copyright (c) 2016 Frederik Wenigwieser <frederik.wenigwieser@gmail.com>
    */

    /*
    @@ -20,16 +26,287 @@
    * any later version.
    */

    -#include <linux/device.h>
    #include <linux/hid.h>
    #include <linux/module.h>
    +#include <linux/input/mt.h>

    #include "hid-ids.h"

    +MODULE_AUTHOR("Yusuke Fujimaki <usk.fujimaki@gmail.com>");
    +MODULE_AUTHOR("Brendan McGrath <redmcg@redmandi.dyndns.org>");
    +MODULE_AUTHOR("Victor Vlasenko <victor.vlasenko@sysgears.com>");
    +MODULE_AUTHOR("Frederik Wenigwieser <frederik.wenigwieser@gmail.com>");
    +MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
    +
    +#define FEATURE_REPORT_ID 0x0d
    +#define INPUT_REPORT_ID 0x5d
    +
    +#define INPUT_REPORT_SIZE 28
    +
    +#define MAX_CONTACTS 5
    +
    +#define MAX_X 2794
    +#define MAX_Y 1758
    +#define MAX_TOUCH_MAJOR 8
    +#define MAX_PRESSURE 128
    +
    +#define CONTACT_DATA_SIZE 5
    +
    +#define BTN_LEFT_MASK 0x01
    +#define CONTACT_TOOL_TYPE_MASK 0x80
    +#define CONTACT_X_MSB_MASK 0xf0
    +#define CONTACT_Y_MSB_MASK 0x0f
    +#define CONTACT_TOUCH_MAJOR_MASK 0x07
    +#define CONTACT_PRESSURE_MASK 0x7f
    +
    +#define QUIRK_FIX_NOTEBOOK_REPORT BIT(0)
    +#define QUIRK_NO_INIT_REPORTS BIT(1)
    +#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
    +#define QUIRK_IS_MULTITOUCH BIT(3)
    +
    +#define NOTEBOOK_QUIRKS QUIRK_FIX_NOTEBOOK_REPORT
    +#define TOUCHPAD_QUIRKS (QUIRK_NO_INIT_REPORTS | \
    + QUIRK_SKIP_INPUT_MAPPING | \
    + QUIRK_IS_MULTITOUCH)
    +
    +#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
    +
    +struct asus_drvdata {
    + unsigned long quirks;
    + struct input_dev *input;
    +};
    +
    +static void asus_report_contact_down(struct input_dev *input,
    + int toolType, u8 *data)
    +{
    + int touch_major, pressure;
    + int x = (data[0] & CONTACT_X_MSB_MASK) << 4 | data[1];
    + int y = MAX_Y - ((data[0] & CONTACT_Y_MSB_MASK) << 8 | data[2]);
    +
    + if (toolType == MT_TOOL_PALM) {
    + touch_major = MAX_TOUCH_MAJOR;
    + pressure = MAX_PRESSURE;
    + } else {
    + touch_major = (data[3] >> 4) & CONTACT_TOUCH_MAJOR_MASK;
    + pressure = data[4] & CONTACT_PRESSURE_MASK;
    + }
    +
    + input_report_abs(input, ABS_MT_POSITION_X, x);
    + input_report_abs(input, ABS_MT_POSITION_Y, y);
    + input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
    + input_report_abs(input, ABS_MT_PRESSURE, pressure);
    +}
    +
    +/* Required for Synaptics Palm Detection */
    +static void asus_report_tool_width(struct input_dev *input)
    +{
    + struct input_mt *mt = input->mt;
    + struct input_mt_slot *oldest;
    + int oldid, count, i;
    +
    + oldest = NULL;
    + oldid = mt->trkid;
    + count = 0;
    +
    + for (i = 0; i < mt->num_slots; ++i) {
    + struct input_mt_slot *ps = &mt->slots[i];
    + int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
    +
    + if (id < 0)
    + continue;
    + if ((id - oldid) & TRKID_SGN) {
    + oldest = ps;
    + oldid = id;
    + }
    + count++;
    + }
    +
    + if (oldest) {
    + input_report_abs(input, ABS_TOOL_WIDTH,
    + input_mt_get_value(oldest, ABS_MT_TOUCH_MAJOR));
    + }
    +}
    +
    +static void asus_report_input(struct input_dev *input, u8 *data)
    +{
    + int i;
    + u8 *contactData = data + 2;
    +
    + for (i = 0; i < MAX_CONTACTS; i++) {
    + bool down = !!(data[1] & BIT(i+3));
    + int toolType = contactData[3] & CONTACT_TOOL_TYPE_MASK ?
    + MT_TOOL_PALM : MT_TOOL_FINGER;
    +
    + input_mt_slot(input, i);
    + input_mt_report_slot_state(input, toolType, down);
    +
    + if (down) {
    + asus_report_contact_down(input, toolType, contactData);
    + contactData += CONTACT_DATA_SIZE;
    + }
    + }
    +
    + input_report_key(input, BTN_LEFT, data[1] & BTN_LEFT_MASK);
    + asus_report_tool_width(input);
    +
    + input_mt_sync_frame(input);
    + input_sync(input);
    +}
    +
    +static int asus_raw_event(struct hid_device *hdev,
    + struct hid_report *report, u8 *data, int size)
    +{
    + struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
    +
    + if (drvdata->quirks & QUIRK_IS_MULTITOUCH &&
    + data[0] == INPUT_REPORT_ID &&
    + size == INPUT_REPORT_SIZE) {
    + asus_report_input(drvdata->input, data);
    + return 1;
    + }
    +
    + return 0;
    +}
    +
    +static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
    +{
    + struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
    +
    + if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
    + int ret;
    + struct input_dev *input = hi->input;
    +
    + input_set_abs_params(input, ABS_MT_POSITION_X, 0, MAX_X, 0, 0);
    + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, MAX_Y, 0, 0);
    + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MAX_TOUCH_MAJOR, 0, 0);
    + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MAX_TOUCH_MAJOR, 0, 0);
    + input_set_abs_params(input, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0);
    +
    + __set_bit(BTN_LEFT, input->keybit);
    + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
    +
    + ret = input_mt_init_slots(input, MAX_CONTACTS, INPUT_MT_POINTER);
    +
    + if (ret) {
    + hid_err(hdev, "Asus input mt init slots failed: %d\n", ret);
    + return ret;
    + }
    +
    + drvdata->input = input;
    + }
    +
    + return 0;
    +}
    +
    +static int asus_input_mapping(struct hid_device *hdev,
    + struct hid_input *hi, struct hid_field *field,
    + struct hid_usage *usage, unsigned long **bit,
    + int *max)
    +{
    + struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
    +
    + if (drvdata->quirks & QUIRK_SKIP_INPUT_MAPPING) {
    + /* Don't map anything from the HID report.
    + * We do it all manually in asus_input_configured
    + */
    + return -1;
    + }
    +
    + return 0;
    +}
    +
    +static int asus_start_multitouch(struct hid_device *hdev)
    +{
    + int ret;
    + const unsigned char buf[] = { FEATURE_REPORT_ID, 0x00, 0x03, 0x01, 0x00 };
    + unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);
    +
    + if (!dmabuf) {
    + ret = -ENOMEM;
    + hid_err(hdev, "Asus failed to alloc dma buf: %d\n", ret);
    + return ret;
    + }
    +
    + ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf),
    + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
    +
    + kfree(dmabuf);
    +
    + if (ret != sizeof(buf)) {
    + hid_err(hdev, "Asus failed to start multitouch: %d\n", ret);
    + return ret;
    + }
    +
    + return 0;
    +}
    +
    +static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
    +{
    + struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
    +
    + if (drvdata->quirks & QUIRK_IS_MULTITOUCH)
    + return asus_start_multitouch(hdev);
    +
    + return 0;
    +}
    +
    +static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
    +{
    + int ret;
    + struct asus_drvdata *drvdata;
    +
    + drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
    + if (drvdata == NULL) {
    + hid_err(hdev, "Can't alloc Asus descriptor\n");
    + return -ENOMEM;
    + }
    +
    + hid_set_drvdata(hdev, drvdata);
    +
    + drvdata->quirks = id->driver_data;
    +
    + if (drvdata->quirks & QUIRK_NO_INIT_REPORTS)
    + hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
    +
    + ret = hid_parse(hdev);
    + if (ret) {
    + hid_err(hdev, "Asus hid parse failed: %d\n", ret);
    + return ret;
    + }
    +
    + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
    + if (ret) {
    + hid_err(hdev, "Asus hw start failed: %d\n", ret);
    + return ret;
    + }
    +
    + if (!drvdata->input) {
    + hid_err(hdev, "Asus input not registered\n");
    + ret = -ENOMEM;
    + goto err_stop_hw;
    + }
    +
    + drvdata->input->name = "Asus TouchPad";
    +
    + if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
    + ret = asus_start_multitouch(hdev);
    + if (ret)
    + goto err_stop_hw;
    + }
    +
    + return 0;
    +err_stop_hw:
    + hid_hw_stop(hdev);
    + return ret;
    +}
    +
    static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
    unsigned int *rsize)
    {
    - if (*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x65) {
    + struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
    +
    + if (drvdata->quirks & QUIRK_FIX_NOTEBOOK_REPORT &&
    + *rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x65) {
    hid_info(hdev, "Fixing up Asus notebook report descriptor\n");
    rdesc[55] = 0xdd;
    }
    @@ -37,15 +314,25 @@ static __u8 *asus_report_fixup(struct hi
    }

    static const struct hid_device_id asus_devices[] = {
    - { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) },
    + { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
    + USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD), NOTEBOOK_QUIRKS},
    + { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
    + USB_DEVICE_ID_ASUSTEK_TOUCHPAD), TOUCHPAD_QUIRKS },
    { }
    };
    MODULE_DEVICE_TABLE(hid, asus_devices);

    static struct hid_driver asus_driver = {
    - .name = "asus",
    - .id_table = asus_devices,
    - .report_fixup = asus_report_fixup
    + .name = "asus",
    + .id_table = asus_devices,
    + .report_fixup = asus_report_fixup,
    + .probe = asus_probe,
    + .input_mapping = asus_input_mapping,
    + .input_configured = asus_input_configured,
    +#ifdef CONFIG_PM
    + .reset_resume = asus_reset_resume,
    +#endif
    + .raw_event = asus_raw_event
    };
    module_hid_driver(asus_driver);

    --- a/drivers/hid/hid-core.c
    +++ b/drivers/hid/hid-core.c
    @@ -1856,6 +1856,7 @@ static const struct hid_device_id hid_ha
    { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
    { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
    { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) },
    + { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_TOUCHPAD) },
    { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
    { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
    { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) },
    --- a/drivers/hid/hid-ids.h
    +++ b/drivers/hid/hid-ids.h
    @@ -174,6 +174,7 @@
    #define USB_DEVICE_ID_ASUSTEK_LCM 0x1726
    #define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b
    #define USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD 0x8585
    +#define USB_DEVICE_ID_ASUSTEK_TOUCHPAD 0x0101

    #define USB_VENDOR_ID_ATEN 0x0557
    #define USB_DEVICE_ID_ATEN_UC100KM 0x2004

    \
     
     \ /
      Last update: 2017-04-10 19:34    [W:3.400 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site