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/hid') 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-59-g8ed1b 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/hid') 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-59-g8ed1b 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/hid') 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-59-g8ed1b 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/hid') 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-59-g8ed1b 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/hid') 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-59-g8ed1b 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/hid') 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-59-g8ed1b 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/hid') 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-59-g8ed1b 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/hid') 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-59-g8ed1b 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/hid') 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-59-g8ed1b