summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenrik Rydberg <rydberg@euromail.se>2010-07-15 23:10:10 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2010-07-15 23:52:03 -0700
commit40d007e7df1dab17bf1ecf91e718218354d963d7 (patch)
treed8c12e13e71f411b4f66aa44ec9b23935156e49d
parenta8aef622929bbba4d89498fb41dd445c14fae1f7 (diff)
Input: introduce MT event slots
With the rapidly increasing number of intelligent multi-contact and multi-user devices, the need to send digested, filtered information from a set of different sources within the same device is imminent. This patch adds the concept of slots to the MT protocol. The slots enumerate a set of identified sources, such that all MT events can be passed independently and selectively per identified source. The protocol works like this: Instead of sending a SYN_MT_REPORT event immediately after the contact data, one sends an ABS_MT_SLOT event immediately before the contact data. The input core will only emit events for slots with modified MT events. It is assumed that the same slot is used for the duration of an initiated contact. Acked-by: Ping Cheng <pingc@wacom.com> Acked-by: Chase Douglas <chase.douglas@canonical.com> Acked-by: Rafi Rubin <rafi@seas.upenn.edu> Signed-off-by: Henrik Rydberg <rydberg@euromail.se> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r--drivers/input/evdev.c4
-rw-r--r--drivers/input/input.c135
-rw-r--r--include/linux/input.h33
3 files changed, 129 insertions, 43 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index cd323254ca6f..fc5afbd78625 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -686,6 +686,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
sizeof(struct input_absinfo))))
return -EFAULT;
+ /* We can't change number of reserved MT slots */
+ if (t == ABS_MT_SLOT)
+ return -EINVAL;
+
/*
* Take event lock to ensure that we are not
* changing device parameters in the middle
diff --git a/drivers/input/input.c b/drivers/input/input.c
index a3d5485154e7..54109c33e36c 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -33,25 +33,6 @@ MODULE_LICENSE("GPL");
#define INPUT_DEVICES 256
-/*
- * EV_ABS events which should not be cached are listed here.
- */
-static unsigned int input_abs_bypass_init_data[] __initdata = {
- ABS_MT_TOUCH_MAJOR,
- ABS_MT_TOUCH_MINOR,
- ABS_MT_WIDTH_MAJOR,
- ABS_MT_WIDTH_MINOR,
- ABS_MT_ORIENTATION,
- ABS_MT_POSITION_X,
- ABS_MT_POSITION_Y,
- ABS_MT_TOOL_TYPE,
- ABS_MT_BLOB_ID,
- ABS_MT_TRACKING_ID,
- ABS_MT_PRESSURE,
- 0
-};
-static unsigned long input_abs_bypass[BITS_TO_LONGS(ABS_CNT)];
-
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
@@ -181,6 +162,56 @@ static void input_stop_autorepeat(struct input_dev *dev)
#define INPUT_PASS_TO_DEVICE 2
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
+static int input_handle_abs_event(struct input_dev *dev,
+ unsigned int code, int *pval)
+{
+ bool is_mt_event;
+ int *pold;
+
+ if (code == ABS_MT_SLOT) {
+ /*
+ * "Stage" the event; we'll flush it later, when we
+ * get actiual touch data.
+ */
+ if (*pval >= 0 && *pval < dev->mtsize)
+ dev->slot = *pval;
+
+ return INPUT_IGNORE_EVENT;
+ }
+
+ is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST;
+
+ if (!is_mt_event) {
+ pold = &dev->abs[code];
+ } else if (dev->mt) {
+ struct input_mt_slot *mtslot = &dev->mt[dev->slot];
+ pold = &mtslot->abs[code - ABS_MT_FIRST];
+ } else {
+ /*
+ * Bypass filtering for multitouch events when
+ * not employing slots.
+ */
+ pold = NULL;
+ }
+
+ if (pold) {
+ *pval = input_defuzz_abs_event(*pval, *pold,
+ dev->absfuzz[code]);
+ if (*pold == *pval)
+ return INPUT_IGNORE_EVENT;
+
+ *pold = *pval;
+ }
+
+ /* Flush pending "slot" event */
+ if (is_mt_event && dev->slot != dev->abs[ABS_MT_SLOT]) {
+ dev->abs[ABS_MT_SLOT] = dev->slot;
+ input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
+ }
+
+ return INPUT_PASS_TO_HANDLERS;
+}
+
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
@@ -233,21 +264,9 @@ static void input_handle_event(struct input_dev *dev,
break;
case EV_ABS:
- if (is_event_supported(code, dev->absbit, ABS_MAX)) {
-
- if (test_bit(code, input_abs_bypass)) {
- disposition = INPUT_PASS_TO_HANDLERS;
- break;
- }
+ if (is_event_supported(code, dev->absbit, ABS_MAX))
+ disposition = input_handle_abs_event(dev, code, &value);
- value = input_defuzz_abs_event(value,
- dev->abs[code], dev->absfuzz[code]);
-
- if (dev->abs[code] != value) {
- dev->abs[code] = value;
- disposition = INPUT_PASS_TO_HANDLERS;
- }
- }
break;
case EV_REL:
@@ -1288,6 +1307,7 @@ static void input_dev_release(struct device *device)
struct input_dev *dev = to_input_dev(device);
input_ff_destroy(dev);
+ input_mt_destroy_slots(dev);
kfree(dev);
module_put(THIS_MODULE);
@@ -1537,6 +1557,45 @@ 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.
+ */
+int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots)
+{
+ 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);
+
+ 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
* @type: type of the event (EV_KEY, EV_REL, etc...)
@@ -1945,20 +2004,10 @@ static const struct file_operations input_fops = {
.open = input_open_file,
};
-static void __init input_init_abs_bypass(void)
-{
- const unsigned int *p;
-
- for (p = input_abs_bypass_init_data; *p; p++)
- input_abs_bypass[BIT_WORD(*p)] |= BIT_MASK(*p);
-}
-
static int __init input_init(void)
{
int err;
- input_init_abs_bypass();
-
err = class_register(&input_class);
if (err) {
printk(KERN_ERR "input: unable to register input_dev class\n");
diff --git a/include/linux/input.h b/include/linux/input.h
index cc524c8b6703..a14de64ed16a 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -691,9 +691,12 @@ struct input_absinfo {
#define ABS_TILT_X 0x1a
#define ABS_TILT_Y 0x1b
#define ABS_TOOL_WIDTH 0x1c
+
#define ABS_VOLUME 0x20
+
#define ABS_MISC 0x28
+#define ABS_MT_SLOT 0x2f /* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
@@ -706,6 +709,12 @@ struct input_absinfo {
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
+#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
+#endif
+
#define ABS_MAX 0x3f
#define ABS_CNT (ABS_MAX+1)
@@ -1048,6 +1057,14 @@ struct ff_effect {
#include <linux/mod_devicetable.h>
/**
+ * 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
* @phys: physical path to the device in the system hierarchy
@@ -1085,6 +1102,10 @@ struct ff_effect {
* @sync: set to 1 when there were no new events since last EV_SYNC
* @abs: current values for reports from absolute axes
* @rep: current values for autorepeat parameters (delay, rate)
+ * @mt: pointer to array of struct input_mt_slot holding current values
+ * of tracked contacts
+ * @mtsize: number of MT slots the device uses
+ * @slot: MT slot currently being transmitted
* @key: reflects current state of device's keys/buttons
* @led: reflects current state of device's LEDs
* @snd: reflects current state of sound effects
@@ -1164,6 +1185,10 @@ struct input_dev {
int abs[ABS_CNT];
int rep[REP_MAX + 1];
+ struct input_mt_slot *mt;
+ int mtsize;
+ int slot;
+
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
@@ -1412,6 +1437,11 @@ 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);
/**
@@ -1506,5 +1536,8 @@ 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