From 47c78e891323513e9909729b44033e2c6649e2b7 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 27 Nov 2010 09:16:48 +0100 Subject: [PATCH 01/14] input: mt: Break out slots handling In preparation for common code to handle a larger set of MT slots devices, move the slots handling over to a separate file. Signed-off-by: Henrik Rydberg --- drivers/hid/hid-3m-pct.c | 1 + drivers/input/Makefile | 2 +- drivers/input/input-mt.c | 58 +++++++++++++++++++++++++ drivers/input/input.c | 48 +------------------- drivers/input/misc/uinput.c | 1 + drivers/input/tablet/wacom_wac.c | 1 + drivers/input/touchscreen/wacom_w8001.c | 2 +- include/linux/input.h | 16 ------- include/linux/input/mt.h | 44 +++++++++++++++++++ 9 files changed, 108 insertions(+), 65 deletions(-) create mode 100644 drivers/input/input-mt.c create mode 100644 include/linux/input/mt.h diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c index 02d8cd3b1b1b..18575a4e0d63 100644 --- a/drivers/hid/hid-3m-pct.c +++ b/drivers/hid/hid-3m-pct.c @@ -19,6 +19,7 @@ #include #include #include +#include MODULE_AUTHOR("Stephane Chatty "); MODULE_DESCRIPTION("3M PCT multitouch panels"); diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 7ad212d31f99..569938b3cc04 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -5,7 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT) += input-core.o -input-core-objs := input.o input-compat.o ff-core.o +input-core-objs := input.o input-compat.o ff-core.o input-mt.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c new file mode 100644 index 000000000000..463a4d7d54f2 --- /dev/null +++ b/drivers/input/input-mt.c @@ -0,0 +1,58 @@ +/* + * Input Multitouch Library + * + * Copyright (c) 2008-2010 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include + +/** + * input_mt_create_slots() - create MT input slots + * @dev: input device supporting MT events and finger tracking + * @num_slots: number of slots used by the device + * + * This function allocates all necessary memory for MT slot handling in the + * input device, and adds ABS_MT_SLOT to the device capabilities. All slots + * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. + */ +int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) +{ + int i; + + if (!num_slots) + return 0; + + dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); + if (!dev->mt) + return -ENOMEM; + + dev->mtsize = num_slots; + input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + + /* Mark slots as 'unused' */ + for (i = 0; i < num_slots; i++) + input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1); + + return 0; +} +EXPORT_SYMBOL(input_mt_create_slots); + +/** + * input_mt_destroy_slots() - frees the MT slots of the input device + * @dev: input device with allocated MT slots + * + * This function is only needed in error path as the input core will + * automatically free the MT slots when the device is destroyed. + */ +void input_mt_destroy_slots(struct input_dev *dev) +{ + kfree(dev->mt); + dev->mt = NULL; + dev->mtsize = 0; +} +EXPORT_SYMBOL(input_mt_destroy_slots); diff --git a/drivers/input/input.c b/drivers/input/input.c index d092ef9291da..37708d1d86ec 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include @@ -1690,52 +1690,6 @@ void input_free_device(struct input_dev *dev) } EXPORT_SYMBOL(input_free_device); -/** - * input_mt_create_slots() - create MT input slots - * @dev: input device supporting MT events and finger tracking - * @num_slots: number of slots used by the device - * - * This function allocates all necessary memory for MT slot handling in the - * input device, and adds ABS_MT_SLOT to the device capabilities. All slots - * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. - */ -int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) -{ - int i; - - if (!num_slots) - return 0; - - dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); - if (!dev->mt) - return -ENOMEM; - - dev->mtsize = num_slots; - input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); - - /* Mark slots as 'unused' */ - for (i = 0; i < num_slots; i++) - dev->mt[i].abs[ABS_MT_TRACKING_ID - ABS_MT_FIRST] = -1; - - return 0; -} -EXPORT_SYMBOL(input_mt_create_slots); - -/** - * input_mt_destroy_slots() - frees the MT slots of the input device - * @dev: input device with allocated MT slots - * - * This function is only needed in error path as the input core will - * automatically free the MT slots when the device is destroyed. - */ -void input_mt_destroy_slots(struct input_dev *dev) -{ - kfree(dev->mt); - dev->mt = NULL; - dev->mtsize = 0; -} -EXPORT_SYMBOL(input_mt_destroy_slots); - /** * input_set_capability - mark device as capable of a certain event * @dev: device that is capable of emitting or accepting event diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index b9410784e6a1..8f374143190e 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "../input-compat.h" static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index b3252ef1e279..bde612c6d36d 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -14,6 +14,7 @@ #include "wacom_wac.h" #include "wacom.h" +#include static int wacom_penpartner_irq(struct wacom_wac *wacom) { diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 9ae4c7b16ba7..5d4f50e52a28 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/linux/input.h b/include/linux/input.h index 51af441f3a21..99e2a52c0509 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1082,14 +1082,6 @@ struct ff_effect { #include #include -/** - * struct input_mt_slot - represents the state of an input MT slot - * @abs: holds current values of ABS_MT axes for this slot - */ -struct input_mt_slot { - int abs[ABS_MT_LAST - ABS_MT_FIRST + 1]; -}; - /** * struct input_dev - represents an input device * @name: name of the device @@ -1461,11 +1453,6 @@ static inline void input_mt_sync(struct input_dev *dev) input_event(dev, EV_SYN, SYN_MT_REPORT, 0); } -static inline void input_mt_slot(struct input_dev *dev, int slot) -{ - input_event(dev, EV_ABS, ABS_MT_SLOT, slot); -} - void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code); /** @@ -1578,8 +1565,5 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file); int input_ff_create_memless(struct input_dev *dev, void *data, int (*play_effect)(struct input_dev *, void *, struct ff_effect *)); -int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots); -void input_mt_destroy_slots(struct input_dev *dev); - #endif #endif diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h new file mode 100644 index 000000000000..4f5e9d0e2eae --- /dev/null +++ b/include/linux/input/mt.h @@ -0,0 +1,44 @@ +#ifndef _INPUT_MT_H +#define _INPUT_MT_H + +/* + * Input Multitouch Library + * + * Copyright (c) 2010 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include + +/** + * struct input_mt_slot - represents the state of an input MT slot + * @abs: holds current values of ABS_MT axes for this slot + */ +struct input_mt_slot { + int abs[ABS_MT_LAST - ABS_MT_FIRST + 1]; +}; + +static inline void input_mt_set_value(struct input_mt_slot *slot, + unsigned code, int value) +{ + slot->abs[code - ABS_MT_FIRST] = value; +} + +static inline int input_mt_get_value(const struct input_mt_slot *slot, + unsigned code) +{ + return slot->abs[code - ABS_MT_FIRST]; +} + +int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots); +void input_mt_destroy_slots(struct input_dev *dev); + +static inline void input_mt_slot(struct input_dev *dev, int slot) +{ + input_event(dev, EV_ABS, ABS_MT_SLOT, slot); +} + +#endif From 8cde81001626c4c60b26ef2eb5fc522885ed9fd0 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 27 Nov 2010 10:50:54 +0100 Subject: [PATCH 02/14] input: mt: Collect slots initialization code The MT slots devices all follow the same initialization pattern of creating slots and hinting about buffer size. Let drivers call an initialization function instead, and make sure it can be called repeatedly without side effects. Signed-off-by: Henrik Rydberg --- drivers/hid/hid-3m-pct.c | 5 +---- drivers/input/input-mt.c | 19 +++++++++++++------ drivers/input/misc/uinput.c | 3 +-- drivers/input/tablet/wacom_wac.c | 2 +- drivers/input/touchscreen/wacom_w8001.c | 2 +- include/linux/input/mt.h | 2 +- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c index 18575a4e0d63..ea475964d05a 100644 --- a/drivers/hid/hid-3m-pct.c +++ b/drivers/hid/hid-3m-pct.c @@ -29,7 +29,6 @@ MODULE_LICENSE("GPL"); #define MAX_SLOTS 60 #define MAX_TRKID USHRT_MAX -#define MAX_EVENTS 360 /* estimated signal-to-noise ratios */ #define SN_MOVE 2048 @@ -123,9 +122,7 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, EV_ABS, ABS_MT_TRACKING_ID); input_set_abs_params(hi->input, ABS_MT_TRACKING_ID, 0, MAX_TRKID, 0, 0); - if (!hi->input->mt) - input_mt_create_slots(hi->input, MAX_SLOTS); - input_set_events_per_packet(hi->input, MAX_EVENTS); + input_mt_init_slots(hi->input, MAX_SLOTS); return 1; } /* let hid-input decide for the others */ diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 463a4d7d54f2..f400e47092c4 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -12,20 +12,25 @@ #include /** - * input_mt_create_slots() - create MT input slots + * input_mt_init_slots() - initialize MT input slots * @dev: input device supporting MT events and finger tracking * @num_slots: number of slots used by the device * - * This function allocates all necessary memory for MT slot handling in the - * input device, and adds ABS_MT_SLOT to the device capabilities. All slots - * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. + * This function allocates all necessary memory for MT slot handling + * in the input device, adds ABS_MT_SLOT to the device capabilities + * and sets up appropriate event buffers. All slots are initially + * marked as unused by setting ABS_MT_TRACKING_ID to -1. May be called + * repeatedly. Returns -EINVAL if attempting to reinitialize with a + * different number of slots. */ -int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) +int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) { int i; if (!num_slots) return 0; + if (dev->mt) + return dev->mtsize != num_slots ? -EINVAL : 0; dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); if (!dev->mt) @@ -33,6 +38,7 @@ int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) dev->mtsize = num_slots; input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + input_set_events_per_packet(dev, 6 * num_slots); /* Mark slots as 'unused' */ for (i = 0; i < num_slots; i++) @@ -40,7 +46,7 @@ int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) return 0; } -EXPORT_SYMBOL(input_mt_create_slots); +EXPORT_SYMBOL(input_mt_init_slots); /** * input_mt_destroy_slots() - frees the MT slots of the input device @@ -54,5 +60,6 @@ void input_mt_destroy_slots(struct input_dev *dev) kfree(dev->mt); dev->mt = NULL; dev->mtsize = 0; + dev->slot = 0; } EXPORT_SYMBOL(input_mt_destroy_slots); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 8f374143190e..bea89722c4e9 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -407,8 +407,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu goto exit; if (test_bit(ABS_MT_SLOT, dev->absbit)) { int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_create_slots(dev, nslot); - input_set_events_per_packet(dev, 6 * nslot); + input_mt_init_slots(dev, nslot); } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { input_set_events_per_packet(dev, 60); } diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index bde612c6d36d..f26e2238f6ca 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1273,7 +1273,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOOL_FINGER, input_dev->keybit); __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - input_mt_create_slots(input_dev, 2); + input_mt_init_slots(input_dev, 2); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, features->x_max, features->x_fuzz, 0); diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 5d4f50e52a28..4a2e8cf4c8ef 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -318,7 +318,7 @@ static int w8001_setup(struct w8001 *w8001) case 5: w8001->pktlen = W8001_PKTLEN_TOUCH2FG; - input_mt_create_slots(dev, 2); + input_mt_init_slots(dev, 2); input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, MAX_TRACKING_ID, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index 4f5e9d0e2eae..d7f6518e3222 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -33,7 +33,7 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot, return slot->abs[code - ABS_MT_FIRST]; } -int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots); +int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots); void input_mt_destroy_slots(struct input_dev *dev); static inline void input_mt_slot(struct input_dev *dev, int slot) From c5f4dec1ceb6ab773bbbefbe64a7c990c7d6b17f Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 15 Dec 2010 13:50:34 +0100 Subject: [PATCH 03/14] input: mt: Move tracking and pointer emulation to input-mt The drivers using the type B protocol all report tracking information the same way. The contact id is semantically equivalent to ABS_MT_SLOT, and the handling of ABS_MT_TRACKING_ID only complicates the driver. The situation can be improved upon by providing a common pointer emulation code, thereby removing the need for the tracking id in the driver. This patch moves all tracking event handling over to the input core, simplifying both the existing drivers and the ones currently in preparation. Acked-by: Ping Cheng Acked-by: Jiri Kosina Signed-off-by: Henrik Rydberg --- drivers/hid/hid-3m-pct.c | 30 +------ drivers/input/input-mt.c | 115 ++++++++++++++++++++++-- drivers/input/tablet/wacom_wac.c | 23 ++--- drivers/input/tablet/wacom_wac.h | 4 - drivers/input/touchscreen/wacom_w8001.c | 21 ++--- include/linux/input.h | 3 + include/linux/input/mt.h | 13 +++ 7 files changed, 138 insertions(+), 71 deletions(-) diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c index ea475964d05a..4fb7c7528d16 100644 --- a/drivers/hid/hid-3m-pct.c +++ b/drivers/hid/hid-3m-pct.c @@ -28,7 +28,6 @@ MODULE_LICENSE("GPL"); #include "hid-ids.h" #define MAX_SLOTS 60 -#define MAX_TRKID USHRT_MAX /* estimated signal-to-noise ratios */ #define SN_MOVE 2048 @@ -36,14 +35,11 @@ MODULE_LICENSE("GPL"); struct mmm_finger { __s32 x, y, w, h; - __u16 id; - bool prev_touch; bool touch, valid; }; struct mmm_data { struct mmm_finger f[MAX_SLOTS]; - __u16 id; __u8 curid; __u8 nexp, nreal; bool touch, valid; @@ -117,11 +113,6 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, 0, 1, 0, 0); return 1; case HID_DG_CONTACTID: - field->logical_maximum = MAX_TRKID; - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TRACKING_ID); - input_set_abs_params(hi->input, ABS_MT_TRACKING_ID, - 0, MAX_TRKID, 0, 0); input_mt_init_slots(hi->input, MAX_SLOTS); return 1; } @@ -152,7 +143,6 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi, */ static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) { - struct mmm_finger *oldest = 0; int i; for (i = 0; i < MAX_SLOTS; ++i) { struct mmm_finger *f = &md->f[i]; @@ -161,6 +151,7 @@ static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) continue; } input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, f->touch); if (f->touch) { /* this finger is on the screen */ int wide = (f->w > f->h); @@ -168,33 +159,16 @@ static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) int major = max(f->w, f->h) >> 1; int minor = min(f->w, f->h) >> 1; - if (!f->prev_touch) - f->id = md->id++; - input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id); input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); - /* touchscreen emulation: pick the oldest contact */ - if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1))) - oldest = f; - } else { - /* this finger took off the screen */ - input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1); } - f->prev_touch = f->touch; f->valid = 0; } - /* touchscreen emulation */ - if (oldest) { - input_event(input, EV_KEY, BTN_TOUCH, 1); - input_event(input, EV_ABS, ABS_X, oldest->x); - input_event(input, EV_ABS, ABS_Y, oldest->y); - } else { - input_event(input, EV_KEY, BTN_TOUCH, 0); - } + input_mt_report_pointer_emulation(input, true); input_sync(input); } diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index f400e47092c4..c48c81f0308d 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -11,17 +11,18 @@ #include #include +#define TRKID_SGN ((TRKID_MAX + 1) >> 1) + /** * input_mt_init_slots() - initialize MT input slots * @dev: input device supporting MT events and finger tracking * @num_slots: number of slots used by the device * * This function allocates all necessary memory for MT slot handling - * in the input device, adds ABS_MT_SLOT to the device capabilities - * and sets up appropriate event buffers. All slots are initially - * marked as unused by setting ABS_MT_TRACKING_ID to -1. May be called - * repeatedly. Returns -EINVAL if attempting to reinitialize with a - * different number of slots. + * in the input device, prepares the ABS_MT_SLOT and + * ABS_MT_TRACKING_ID events for use and sets up appropriate buffers. + * May be called repeatedly. Returns -EINVAL if attempting to + * reinitialize with a different number of slots. */ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) { @@ -38,6 +39,7 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) dev->mtsize = num_slots; input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0); input_set_events_per_packet(dev, 6 * num_slots); /* Mark slots as 'unused' */ @@ -61,5 +63,108 @@ void input_mt_destroy_slots(struct input_dev *dev) dev->mt = NULL; dev->mtsize = 0; dev->slot = 0; + dev->trkid = 0; } EXPORT_SYMBOL(input_mt_destroy_slots); + +/** + * input_mt_report_slot_state() - report contact state + * @dev: input device with allocated MT slots + * @tool_type: the tool type to use in this slot + * @active: true if contact is active, false otherwise + * + * Reports a contact via ABS_MT_TRACKING_ID, and optionally + * ABS_MT_TOOL_TYPE. If active is true and the slot is currently + * inactive, or if the tool type is changed, a new tracking id is + * assigned to the slot. The tool type is only reported if the + * corresponding absbit field is set. + */ +void input_mt_report_slot_state(struct input_dev *dev, + unsigned int tool_type, bool active) +{ + struct input_mt_slot *mt; + int id; + + if (!dev->mt || !active) { + input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + return; + } + + mt = &dev->mt[dev->slot]; + id = input_mt_get_value(mt, ABS_MT_TRACKING_ID); + if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type) + id = input_mt_new_trkid(dev); + + input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); + input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type); +} +EXPORT_SYMBOL(input_mt_report_slot_state); + +/** + * input_mt_report_finger_count() - report contact count + * @dev: input device with allocated MT slots + * @count: the number of contacts + * + * Reports the contact count via BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, + * BTN_TOOL_TRIPLETAP and BTN_TOOL_QUADTAP. + * + * The input core ensures only the KEY events already setup for + * this device will produce output. + */ +void input_mt_report_finger_count(struct input_dev *dev, int count) +{ + input_event(dev, EV_KEY, BTN_TOOL_FINGER, count == 1); + input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2); + input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3); + input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4); +} +EXPORT_SYMBOL(input_mt_report_finger_count); + +/** + * input_mt_report_pointer_emulation() - common pointer emulation + * @dev: input device with allocated MT slots + * @use_count: report number of active contacts as finger count + * + * Performs legacy pointer emulation via BTN_TOUCH, ABS_X, ABS_Y and + * ABS_PRESSURE. Touchpad finger count is emulated if use_count is true. + * + * The input core ensures only the KEY and ABS axes already setup for + * this device will produce output. + */ +void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) +{ + struct input_mt_slot *oldest = 0; + int oldid = dev->trkid; + int count = 0; + int i; + + for (i = 0; i < dev->mtsize; ++i) { + struct input_mt_slot *ps = &dev->mt[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++; + } + + input_event(dev, EV_KEY, BTN_TOUCH, count > 0); + if (use_count) + input_mt_report_finger_count(dev, count); + + if (oldest) { + int x = input_mt_get_value(oldest, ABS_MT_POSITION_X); + int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y); + int p = input_mt_get_value(oldest, ABS_MT_PRESSURE); + + input_event(dev, EV_ABS, ABS_X, x); + input_event(dev, EV_ABS, ABS_Y, y); + input_event(dev, EV_ABS, ABS_PRESSURE, p); + } else { + input_event(dev, EV_ABS, ABS_PRESSURE, 0); + } +} +EXPORT_SYMBOL(input_mt_report_pointer_emulation); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index f26e2238f6ca..0b0525486711 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -863,19 +863,21 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) struct wacom_features *features = &wacom->features; struct input_dev *input = wacom->input; unsigned char *data = wacom->data; - int sp = 0, sx = 0, sy = 0, count = 0; int i; for (i = 0; i < 2; i++) { int p = data[9 * i + 2]; + bool touch = p && !wacom->shared->stylus_in_proximity; + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); /* * Touch events need to be disabled while stylus is * in proximity because user's hand is resting on touchpad * and sending unwanted events. User expects tablet buttons * to continue working though. */ - if (p && !wacom->shared->stylus_in_proximity) { + if (touch) { int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff; int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff; if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { @@ -885,23 +887,10 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) input_report_abs(input, ABS_MT_PRESSURE, p); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - if (wacom->id[i] < 0) - wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID; - if (!count++) - sp = p, sx = x, sy = y; - } else { - wacom->id[i] = -1; } - input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]); } - input_report_key(input, BTN_TOUCH, count > 0); - input_report_key(input, BTN_TOOL_FINGER, count == 1); - input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2); - - input_report_abs(input, ABS_PRESSURE, sp); - input_report_abs(input, ABS_X, sx); - input_report_abs(input, ABS_Y, sy); + input_mt_report_pointer_emulation(input, true); input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); @@ -1283,8 +1272,6 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, features->pressure_max, features->pressure_fuzz, 0); - input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, - MAX_TRACKING_ID, 0, 0); } else if (features->device_type == BTN_TOOL_PEN) { __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 00ca01541d89..b1310ec9720c 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -42,9 +42,6 @@ #define WACOM_QUIRK_MULTI_INPUT 0x0001 #define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 -/* largest reported tracking id */ -#define MAX_TRACKING_ID 0xfff - enum { PENPARTNER = 0, GRAPHIRE, @@ -100,7 +97,6 @@ struct wacom_wac { int id[3]; __u32 serial[2]; int last_finger; - int trk_id; struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 4a2e8cf4c8ef..2a0bec12d12a 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -48,8 +48,6 @@ MODULE_LICENSE("GPL"); #define W8001_PKTLEN_TPCCTL 11 /* control packet */ #define W8001_PKTLEN_TOUCH2FG 13 -#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */ - struct w8001_coord { u8 rdy; u8 tsw; @@ -87,7 +85,6 @@ struct w8001 { char phys[32]; int type; unsigned int pktlen; - int trkid[2]; }; static void parse_data(u8 *data, struct w8001_coord *coord) @@ -116,28 +113,23 @@ static void parse_data(u8 *data, struct w8001_coord *coord) static void parse_touch(struct w8001 *w8001) { - static int trkid; struct input_dev *dev = w8001->dev; unsigned char *data = w8001->data; int i; for (i = 0; i < 2; i++) { - input_mt_slot(dev, i); + bool touch = data[0] & (1 << i); - if (data[0] & (1 << i)) { + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); + if (touch) { int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); /* data[5,6] and [11,12] is finger capacity */ input_report_abs(dev, ABS_MT_POSITION_X, x); input_report_abs(dev, ABS_MT_POSITION_Y, y); - input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); - if (w8001->trkid[i] < 0) - w8001->trkid[i] = trkid++ & MAX_TRACKING_ID; - } else { - w8001->trkid[i] = -1; } - input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]); } input_sync(dev); @@ -319,14 +311,12 @@ static int w8001_setup(struct w8001 *w8001) w8001->pktlen = W8001_PKTLEN_TOUCH2FG; input_mt_init_slots(dev, 2); - input_set_abs_params(dev, ABS_MT_TRACKING_ID, - 0, MAX_TRACKING_ID, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, 0, touch.x, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, touch.y, 0, 0); input_set_abs_params(dev, ABS_MT_TOOL_TYPE, - 0, 0, 0, 0); + 0, MT_TOOL_MAX, 0, 0); break; } } @@ -372,7 +362,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) w8001->serio = serio; w8001->id = serio->id.id; w8001->dev = input_dev; - w8001->trkid[0] = w8001->trkid[1] = -1; init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); diff --git a/include/linux/input.h b/include/linux/input.h index 99e2a52c0509..6de145df4c1c 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -848,6 +848,7 @@ struct input_keymap_entry { */ #define MT_TOOL_FINGER 0 #define MT_TOOL_PEN 1 +#define MT_TOOL_MAX 1 /* * Values describing the status of a force-feedback effect @@ -1122,6 +1123,7 @@ struct ff_effect { * of tracked contacts * @mtsize: number of MT slots the device uses * @slot: MT slot currently being transmitted + * @trkid: stores MT tracking ID for the current contact * @absinfo: array of &struct absinfo elements holding information * about absolute axes (current value, min, max, flat, fuzz, * resolution) @@ -1206,6 +1208,7 @@ struct input_dev { struct input_mt_slot *mt; int mtsize; int slot; + int trkid; struct input_absinfo *absinfo; diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index d7f6518e3222..b3ac06a4435d 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -13,6 +13,8 @@ #include +#define TRKID_MAX 0xffff + /** * struct input_mt_slot - represents the state of an input MT slot * @abs: holds current values of ABS_MT axes for this slot @@ -36,9 +38,20 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot, int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots); void input_mt_destroy_slots(struct input_dev *dev); +static inline int input_mt_new_trkid(struct input_dev *dev) +{ + return dev->trkid++ & TRKID_MAX; +} + static inline void input_mt_slot(struct input_dev *dev, int slot) { input_event(dev, EV_ABS, ABS_MT_SLOT, slot); } +void input_mt_report_slot_state(struct input_dev *dev, + unsigned int tool_type, bool active); + +void input_mt_report_finger_count(struct input_dev *dev, int count); +void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count); + #endif From e42a98b520bb22535687ead3120e80edc268279a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 6 Dec 2010 10:05:43 +0100 Subject: [PATCH 04/14] input: mt: Add hovering distance axis Touch devices capable of hovering, i.e., fingers detected a distance from the surface, are not supported by the current input MT protocol. This patch adds ABS_MT_DISTANCE, which may be used to indicate the distance between the contact and the surface. Signed-off-by: Henrik Rydberg --- Documentation/input/multi-touch-protocol.txt | 9 ++++++++- include/linux/input.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/input/multi-touch-protocol.txt b/Documentation/input/multi-touch-protocol.txt index bdcba154b83e..07215fa0c588 100644 --- a/Documentation/input/multi-touch-protocol.txt +++ b/Documentation/input/multi-touch-protocol.txt @@ -161,7 +161,8 @@ against the glass. The inner region will increase, and in general, the ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller than unity, is related to the contact pressure. For pressure-based devices, ABS_MT_PRESSURE may be used to provide the pressure on the contact area -instead. +instead. Devices capable of contact hovering can use ABS_MT_DISTANCE to +indicate the distance between the contact and the surface. In addition to the MAJOR parameters, the oval shape of the contact can be described by adding the MINOR parameters, such that MAJOR and MINOR are the @@ -213,6 +214,12 @@ The pressure, in arbitrary units, on the contact area. May be used instead of TOUCH and WIDTH for pressure-based devices or any device with a spatial signal intensity distribution. +ABS_MT_DISTANCE + +The distance, in surface units, between the contact and the surface. Zero +distance means the contact is touching the surface. A positive number means +the contact is hovering above the surface. + ABS_MT_ORIENTATION The orientation of the ellipse. The value should describe a signed quarter diff --git a/include/linux/input.h b/include/linux/input.h index 6de145df4c1c..b3a1e02080c0 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -733,11 +733,12 @@ struct input_keymap_entry { #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ +#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ #ifdef __KERNEL__ /* Implementation details, userspace should not care about these */ #define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR -#define ABS_MT_LAST ABS_MT_PRESSURE +#define ABS_MT_LAST ABS_MT_DISTANCE #endif #define ABS_MAX 0x3f From 4a864183fb28ddf553e5b0b47858bc3c518dae94 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 27 Nov 2010 17:56:17 +0100 Subject: [PATCH 05/14] hid: egalax: Setup input device manually The hid core does not yet handle input filtering. Take over the setup of the input device, so that proper signal-to-noise ratios can be used. Signed-off-by: Jiri Kosina Cc: Stephane Chatty Signed-off-by: Henrik Rydberg --- drivers/hid/hid-egalax.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c index 54b017ad258d..5be513c363e3 100644 --- a/drivers/hid/hid-egalax.c +++ b/drivers/hid/hid-egalax.c @@ -34,10 +34,21 @@ struct egalax_data { __u16 lastx, lasty, lastz; /* latest valid (x, y, z) in the frame */ }; +static void set_abs(struct input_dev *input, unsigned int code, + struct hid_field *field, int snratio) +{ + int fmin = field->logical_minimum; + int fmax = field->logical_maximum; + int fuzz = snratio ? (fmax - fmin) / snratio : 0; + input_set_abs_params(input, code, fmin, fmax, fuzz, 0); +} + static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { + struct input_dev *input = hi->input; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: @@ -45,18 +56,16 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_GD_X: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); + set_abs(input, ABS_MT_POSITION_X, field, 0); /* touchscreen emulation */ - input_set_abs_params(hi->input, ABS_X, - field->logical_minimum, - field->logical_maximum, 0, 0); + set_abs(input, ABS_X, field, 0); return 1; case HID_GD_Y: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); + set_abs(input, ABS_MT_POSITION_Y, field, 0); /* touchscreen emulation */ - input_set_abs_params(hi->input, ABS_Y, - field->logical_minimum, - field->logical_maximum, 0, 0); + set_abs(input, ABS_Y, field, 0); return 1; } return 0; @@ -66,6 +75,7 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_TIPSWITCH: /* touchscreen emulation */ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + input_set_capability(input, EV_KEY, BTN_TOUCH); return 1; case HID_DG_INRANGE: case HID_DG_CONFIDENCE: @@ -75,14 +85,14 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_CONTACTID: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TRACKING_ID); + set_abs(input, ABS_MT_TRACKING_ID, field, 0); return 1; case HID_DG_TIPPRESSURE: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE); + set_abs(input, ABS_MT_PRESSURE, field, 0); /* touchscreen emulation */ - input_set_abs_params(hi->input, ABS_PRESSURE, - field->logical_minimum, - field->logical_maximum, 0, 0); + set_abs(input, ABS_PRESSURE, field, 0); return 1; } return 0; @@ -96,10 +106,10 @@ static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { + /* tell hid-input to skip setup of these event types */ if (usage->type == EV_KEY || usage->type == EV_ABS) - clear_bit(usage->code, *bit); - - return 0; + set_bit(usage->type, hi->input->evbit); + return -1; } /* From b88cbd3a007608a224fad5413de6170c75afd5a7 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 13 Oct 2010 22:18:32 +0200 Subject: [PATCH 06/14] hid: egalax: Correct for device resolution report error The firmware of both supported devices report a X/Y maximum of 4095, whereas in reality, it is eight times larger. Fixed with this patch. Signed-off-by: Jiri Kosina Cc: Stephane Chatty Signed-off-by: Henrik Rydberg --- drivers/hid/hid-egalax.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c index 5be513c363e3..733bebc885db 100644 --- a/drivers/hid/hid-egalax.c +++ b/drivers/hid/hid-egalax.c @@ -54,6 +54,7 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: + field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); set_abs(input, ABS_MT_POSITION_X, field, 0); @@ -61,6 +62,7 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, set_abs(input, ABS_X, field, 0); return 1; case HID_GD_Y: + field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); set_abs(input, ABS_MT_POSITION_Y, field, 0); From 80a469e4f4d01ad96447f85cc71a379ec82ffe2d Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 11 Oct 2010 21:16:43 +0200 Subject: [PATCH 07/14] hid: egalax: Report zero as minimum pressure The firmware reports a logical minimum of one, but in order for userspace applications to correctly map all reported values to non-zero pressure, the driver needs to report a logical minimum of zero. Fixed with this patch. Tested-by: Philipp Merkel Cc: Stephane Chatty Signed-off-by: Jiri Kosina Signed-off-by: Henrik Rydberg --- drivers/hid/hid-egalax.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c index 733bebc885db..aac8a6326bbd 100644 --- a/drivers/hid/hid-egalax.c +++ b/drivers/hid/hid-egalax.c @@ -90,6 +90,7 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, set_abs(input, ABS_MT_TRACKING_ID, field, 0); return 1; case HID_DG_TIPPRESSURE: + field->logical_minimum = 0; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE); set_abs(input, ABS_MT_PRESSURE, field, 0); From 17c760687f1270af9bd798d938198caa7d5aa3eb Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 11 Oct 2010 21:20:51 +0200 Subject: [PATCH 08/14] hid: egalax: Add event filtering Use estimated signal-to-noise ratios to reduce noise and limit the amount of events emitted. Signed-off-by: Jiri Kosina Cc: Stephane Chatty Signed-off-by: Henrik Rydberg --- drivers/hid/hid-egalax.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c index aac8a6326bbd..66b2c5d919af 100644 --- a/drivers/hid/hid-egalax.c +++ b/drivers/hid/hid-egalax.c @@ -25,6 +25,10 @@ MODULE_LICENSE("GPL"); #include "hid-ids.h" +/* estimated signal-to-noise ratios */ +#define SN_MOVE 4096 +#define SN_PRESSURE 32 + struct egalax_data { __u16 x, y, z; __u8 id; @@ -57,17 +61,17 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); - set_abs(input, ABS_MT_POSITION_X, field, 0); + set_abs(input, ABS_MT_POSITION_X, field, SN_MOVE); /* touchscreen emulation */ - set_abs(input, ABS_X, field, 0); + set_abs(input, ABS_X, field, SN_MOVE); return 1; case HID_GD_Y: field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); - set_abs(input, ABS_MT_POSITION_Y, field, 0); + set_abs(input, ABS_MT_POSITION_Y, field, SN_MOVE); /* touchscreen emulation */ - set_abs(input, ABS_Y, field, 0); + set_abs(input, ABS_Y, field, SN_MOVE); return 1; } return 0; @@ -93,9 +97,9 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->logical_minimum = 0; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE); - set_abs(input, ABS_MT_PRESSURE, field, 0); + set_abs(input, ABS_MT_PRESSURE, field, SN_PRESSURE); /* touchscreen emulation */ - set_abs(input, ABS_PRESSURE, field, 0); + set_abs(input, ABS_PRESSURE, field, SN_PRESSURE); return 1; } return 0; From f7bc8046b33b9fb2e61318f885cc5d94e0a6b805 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 6 Dec 2010 14:04:21 +0100 Subject: [PATCH 09/14] hid: egalax: Convert to MT slots The firmware in the joojoo reports touches sequentially, one per report, which confuses the current driver. A further complication is the absense of any indication of a touch frame. This patch converts the driver to the MT slots protocol, and outputs one full touch frame per report. This way, proper handling for both firmwares is ensured. Tested-by: Philipp Merkel Cc: Stephane Chatty Signed-off-by: Jiri Kosina Signed-off-by: Henrik Rydberg --- drivers/hid/hid-egalax.c | 86 +++++++++++----------------------------- 1 file changed, 23 insertions(+), 63 deletions(-) diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c index 66b2c5d919af..38fdaadacdd4 100644 --- a/drivers/hid/hid-egalax.c +++ b/drivers/hid/hid-egalax.c @@ -2,6 +2,8 @@ * HID driver for eGalax dual-touch panels * * Copyright (c) 2010 Stephane Chatty + * Copyright (c) 2010 Henrik Rydberg + * Copyright (c) 2010 Canonical, Ltd. * */ @@ -16,6 +18,7 @@ #include #include #include +#include #include #include "usbhid/usbhid.h" @@ -25,17 +28,17 @@ MODULE_LICENSE("GPL"); #include "hid-ids.h" +#define MAX_SLOTS 2 + /* estimated signal-to-noise ratios */ #define SN_MOVE 4096 #define SN_PRESSURE 32 struct egalax_data { - __u16 x, y, z; - __u8 id; - bool first; /* is this the first finger in the frame? */ - bool valid; /* valid finger data, or just placeholder? */ - bool activity; /* at least one active finger previously? */ - __u16 lastx, lasty, lastz; /* latest valid (x, y, z) in the frame */ + int valid; + int slot; + int touch; + int x, y, z; }; static void set_abs(struct input_dev *input, unsigned int code, @@ -89,9 +92,7 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_CONTACTMAX: return -1; case HID_DG_CONTACTID: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TRACKING_ID); - set_abs(input, ABS_MT_TRACKING_ID, field, 0); + input_mt_init_slots(input, MAX_SLOTS); return 1; case HID_DG_TIPPRESSURE: field->logical_minimum = 0; @@ -125,58 +126,16 @@ static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi, */ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input) { - td->first = !td->first; /* touchscreen emulation */ - - if (td->valid) { - /* emit multitouch events */ - input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id); - input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x >> 3); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y >> 3); + input_mt_slot(input, td->slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, td->touch); + if (td->touch) { + input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y); input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z); - - input_mt_sync(input); - - /* - * touchscreen emulation: store (x, y) as - * the last valid values in this frame - */ - td->lastx = td->x; - td->lasty = td->y; - td->lastz = td->z; - } - - /* - * touchscreen emulation: if this is the second finger and at least - * one in this frame is valid, the latest valid in the frame is - * the oldest on the panel, the one we want for single touch - */ - if (!td->first && td->activity) { - input_event(input, EV_ABS, ABS_X, td->lastx >> 3); - input_event(input, EV_ABS, ABS_Y, td->lasty >> 3); - input_event(input, EV_ABS, ABS_PRESSURE, td->lastz); - } - - if (!td->valid) { - /* - * touchscreen emulation: if the first finger is invalid - * and there previously was finger activity, this is a release - */ - if (td->first && td->activity) { - input_event(input, EV_KEY, BTN_TOUCH, 0); - td->activity = false; - } - return; - } - - - /* touchscreen emulation: if no previous activity, emit touch event */ - if (!td->activity) { - input_event(input, EV_KEY, BTN_TOUCH, 1); - td->activity = true; } + input_mt_report_pointer_emulation(input, true); } - static int egalax_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { @@ -186,25 +145,26 @@ static int egalax_event(struct hid_device *hid, struct hid_field *field, * uses a standard parallel multitouch protocol (product ID == * 48xx). The second is capacitive and uses an unusual "serial" * protocol with a different message for each multitouch finger - * (product ID == 72xx). We do not yet generate a correct event - * sequence for the capacitive/serial protocol. + * (product ID == 72xx). */ if (hid->claimed & HID_CLAIMED_INPUT) { struct input_dev *input = field->hidinput->input; switch (usage->hid) { case HID_DG_INRANGE: + td->valid = value; + break; case HID_DG_CONFIDENCE: /* avoid interference from generic hidinput handling */ break; case HID_DG_TIPSWITCH: - td->valid = value; + td->touch = value; break; case HID_DG_TIPPRESSURE: td->z = value; break; case HID_DG_CONTACTID: - td->id = value; + td->slot = clamp_val(value, 0, MAX_SLOTS - 1); break; case HID_GD_X: td->x = value; @@ -212,11 +172,11 @@ static int egalax_event(struct hid_device *hid, struct hid_field *field, case HID_GD_Y: td->y = value; /* this is the last field in a finger */ - egalax_filter_event(td, input); + if (td->valid) + egalax_filter_event(td, input); break; case HID_DG_CONTACTCOUNT: /* touch emulation: this is the last field in a frame */ - td->first = false; break; default: From 4e93db23bf95cd763c9182b5d6187412fe6ce747 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 29 Nov 2010 07:27:49 +0100 Subject: [PATCH 10/14] hid: egalax: Add support for Wetab The Wetab tablet dual-touch controller works the same way as the one in the Joojoo tablet. This patch adds the Wetab to the list of supported devices, and grabs it accordingly in hid-core. Signed-off-by: Jiri Kosina Cc: Stephane Chatty Signed-off-by: Henrik Rydberg --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-egalax.c | 2 ++ drivers/hid/hid-ids.h | 1 + 3 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 515345b11ac9..9762de5a2f37 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1300,6 +1300,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c index 38fdaadacdd4..e367cb233f9d 100644 --- a/drivers/hid/hid-egalax.c +++ b/drivers/hid/hid-egalax.c @@ -238,6 +238,8 @@ static const struct hid_device_id egalax_devices[] = { USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, { } }; MODULE_DEVICE_TABLE(hid, egalax_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3341baa86a30..b76166a10176 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -196,6 +196,7 @@ #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 0x720c +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 0x72a1 #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 From e12b355b57b4d330be3479b1e9a518cfed59866c Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 29 Nov 2010 14:40:11 +0100 Subject: [PATCH 11/14] hid: egalax: Document the new devices in Kconfig Add the new supported devices to the kernel menu config help text. Signed-off-by: Jiri Kosina Cc: Stephane Chatty Signed-off-by: Henrik Rydberg --- drivers/hid/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 3052e2969ad0..401acecc7f32 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -154,7 +154,8 @@ config HID_EGALAX tristate "eGalax multi-touch panel" depends on USB_HID ---help--- - Support for the eGalax dual-touch panel. + Support for the eGalax dual-touch panels, including the + Joojoo and Wetab tablets. config HID_ELECOM tristate "ELECOM BM084 bluetooth mouse" From 28906ad6d7c8a8090f720cf7c39461df0b1460f6 Mon Sep 17 00:00:00 2001 From: Richard Nauber Date: Tue, 14 Dec 2010 22:36:18 +0100 Subject: [PATCH 12/14] hid: egalax: Add support for Samsung NB30 netbook The Samsung NB30 touch has a DWAV dual-touch device. This patch adds the NB30 to the list of supported devices, and grabs it accordingly in hid-core. [rydberg@euromail.se: rename and log message changes] Signed-off-by: Richard Nauber Signed-off-by: Jiri Kosina Signed-off-by: Henrik Rydberg --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-egalax.c | 2 ++ drivers/hid/hid-ids.h | 1 + 3 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 9762de5a2f37..f4a37f842cfe 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1301,6 +1301,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c index e367cb233f9d..87878509f25e 100644 --- a/drivers/hid/hid-egalax.c +++ b/drivers/hid/hid-egalax.c @@ -240,6 +240,8 @@ static const struct hid_device_id egalax_devices[] = { USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, { } }; MODULE_DEVICE_TABLE(hid, egalax_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b76166a10176..a95719b24756 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -197,6 +197,7 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 0x720c #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 0x480e #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 From 7f9c2454010159e871e9416dcf64b1e6bfce78be Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Tue, 7 Dec 2010 09:18:28 +0100 Subject: [PATCH 13/14] MAINTAINERS: Update input-mt entry The input multitouch core is now located in its own file, and maintained via a git tree. Update the maintainers entry accordingly. Signed-off-by: Henrik Rydberg --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0094224ca79b..a9d3a1343698 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3020,8 +3020,10 @@ F: drivers/input/ INPUT MULTITOUCH (MT) PROTOCOL M: Henrik Rydberg L: linux-input@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/rydberg/input-mt.git S: Maintained F: Documentation/input/multi-touch-protocol.txt +F: drivers/input/input-mt.c K: \b(ABS|SYN)_MT_ INTEL IDLE DRIVER From 69479f8da68f1930b2078b2ebf6533fb00339918 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Dec 2010 01:08:26 -0800 Subject: [PATCH 14/14] Input: include MT library as source for kerneldoc Signed-off-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- Documentation/DocBook/device-drivers.tmpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 22edcbb9ddaf..35447e081736 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -303,6 +303,10 @@ X!Idrivers/video/console/fonts.c !Edrivers/input/input.c !Edrivers/input/ff-core.c !Edrivers/input/ff-memless.c + + Multitouch Library +!Iinclude/linux/input/mt.h +!Edrivers/input/input-mt.c Polled input devices !Iinclude/linux/input-polldev.h