summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2011-01-06 22:34:59 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2011-01-06 22:34:59 -0800
commit554738da71004d96e06fb75f4772dfc3b0f47810 (patch)
tree149a96ce3727025d3b9260961ec94ba8306db278 /drivers/input
parent7b4b30689d688d9ca2e5c3859db6bbe1c35e6014 (diff)
parenta6d38f889750ed6290728a19d9dad577b147c6d0 (diff)
Merge branch 'next' into for-linus
Conflicts: include/linux/input.h
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/apm-power.c14
-rw-r--r--drivers/input/evbug.c18
-rw-r--r--drivers/input/evdev.c19
-rw-r--r--drivers/input/ff-core.c11
-rw-r--r--drivers/input/ff-memless.c31
-rw-r--r--drivers/input/gameport/gameport.c152
-rw-r--r--drivers/input/input-mt.c170
-rw-r--r--drivers/input/input-polldev.c5
-rw-r--r--drivers/input/input.c91
-rw-r--r--drivers/input/joydev.c4
-rw-r--r--drivers/input/joystick/iforce/Makefile15
-rw-r--r--drivers/input/joystick/xpad.c123
-rw-r--r--drivers/input/keyboard/Kconfig19
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/spear-keyboard.c344
-rw-r--r--drivers/input/keyboard/tca6416-keypad.c13
-rw-r--r--drivers/input/misc/Kconfig24
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/cma3000_d0x.c398
-rw-r--r--drivers/input/misc/cma3000_d0x.h42
-rw-r--r--drivers/input/misc/cma3000_d0x_i2c.c143
-rw-r--r--drivers/input/misc/pcf8574_keypad.c19
-rw-r--r--drivers/input/misc/uinput.c8
-rw-r--r--drivers/input/mouse/hgpk.c695
-rw-r--r--drivers/input/mouse/hgpk.h31
-rw-r--r--drivers/input/mouse/psmouse-base.c1
-rw-r--r--drivers/input/mouse/synaptics.c129
-rw-r--r--drivers/input/mouse/synaptics.h3
-rw-r--r--drivers/input/mousedev.c10
-rw-r--r--drivers/input/serio/ams_delta_serio.c1
-rw-r--r--drivers/input/serio/ct82c710.c8
-rw-r--r--drivers/input/serio/hil_mlc.c5
-rw-r--r--drivers/input/serio/hp_sdc_mlc.c18
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h37
-rw-r--r--drivers/input/serio/i8042.c92
-rw-r--r--drivers/input/serio/i8042.h14
-rw-r--r--drivers/input/serio/ps2mult.c2
-rw-r--r--drivers/input/serio/serio.c155
-rw-r--r--drivers/input/tablet/wacom_wac.c26
-rw-r--r--drivers/input/tablet/wacom_wac.h4
-rw-r--r--drivers/input/touchscreen/Kconfig22
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c2
-rw-r--r--drivers/input/touchscreen/qt602240_ts.c21
-rw-r--r--drivers/input/touchscreen/st1232.c274
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c71
-rw-r--r--drivers/input/xen-kbdfront.c10
48 files changed, 2690 insertions, 610 deletions
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 7ad212d31f99..09614ce74961 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-y := input.o input-compat.o input-mt.o ff-core.o
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
diff --git a/drivers/input/apm-power.c b/drivers/input/apm-power.c
index 7d61a9660806..e90ee3d30613 100644
--- a/drivers/input/apm-power.c
+++ b/drivers/input/apm-power.c
@@ -9,6 +9,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/input.h>
#include <linux/slab.h>
@@ -23,8 +25,7 @@ static void system_power_event(unsigned int keycode)
switch (keycode) {
case KEY_SUSPEND:
apm_queue_event(APM_USER_SUSPEND);
-
- printk(KERN_INFO "apm-power: Requesting system suspend...\n");
+ pr_info("Requesting system suspend...\n");
break;
default:
break;
@@ -65,18 +66,15 @@ static int apmpower_connect(struct input_handler *handler,
error = input_register_handle(handle);
if (error) {
- printk(KERN_ERR
- "apm-power: Failed to register input power handler, "
- "error %d\n", error);
+ pr_err("Failed to register input power handler, error %d\n",
+ error);
kfree(handle);
return error;
}
error = input_open_device(handle);
if (error) {
- printk(KERN_ERR
- "apm-power: Failed to open input power device, "
- "error %d\n", error);
+ pr_err("Failed to open input power device, error %d\n", error);
input_unregister_handle(handle);
kfree(handle);
return error;
diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c
index f7c5c14ec12a..cd4e6679d61a 100644
--- a/drivers/input/evbug.c
+++ b/drivers/input/evbug.c
@@ -26,6 +26,8 @@
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
@@ -38,8 +40,8 @@ MODULE_LICENSE("GPL");
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
- printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
- dev_name(&handle->dev->dev), type, code, value);
+ printk(KERN_DEBUG pr_fmt("Event. Dev: %s, Type: %d, Code: %d, Value: %d\n"),
+ dev_name(&handle->dev->dev), type, code, value);
}
static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
@@ -64,10 +66,10 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
if (error)
goto err_unregister_handle;
- printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n",
- dev_name(&dev->dev),
- dev->name ?: "unknown",
- dev->phys ?: "unknown");
+ printk(KERN_DEBUG pr_fmt("Connected device: %s (%s at %s)\n"),
+ dev_name(&dev->dev),
+ dev->name ?: "unknown",
+ dev->phys ?: "unknown");
return 0;
@@ -80,8 +82,8 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
static void evbug_disconnect(struct input_handle *handle)
{
- printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n",
- dev_name(&handle->dev->dev));
+ printk(KERN_DEBUG pr_fmt("Disconnected device: %s\n"),
+ dev_name(&handle->dev->dev));
input_close_device(handle);
input_unregister_handle(handle);
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 68f09a868434..c8471a2552e7 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -8,6 +8,8 @@
* the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
#define EVDEV_MIN_BUFFER_SIZE 64U
@@ -522,12 +524,11 @@ static int handle_eviocgbit(struct input_dev *dev,
if (type == EV_KEY && size == OLD_KEY_MAX) {
len = OLD_KEY_MAX;
if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
- printk(KERN_WARNING
- "evdev.c(EVIOCGBIT): Suspicious buffer size %u, "
- "limiting output to %zu bytes. See "
- "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n",
- OLD_KEY_MAX,
- BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
+ pr_warning("(EVIOCGBIT): Suspicious buffer size %u, "
+ "limiting output to %zu bytes. See "
+ "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n",
+ OLD_KEY_MAX,
+ BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
}
return bits_to_user(bits, len, size, p, compat_mode);
@@ -686,6 +687,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
switch (EVIOC_MASK_SIZE(cmd)) {
+ case EVIOCGPROP(0):
+ return bits_to_user(dev->propbit, INPUT_PROP_MAX,
+ size, p, compat_mode);
+
case EVIOCGKEY(0):
return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
@@ -897,7 +902,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
break;
if (minor == EVDEV_MINORS) {
- printk(KERN_ERR "evdev: no more free evdev devices\n");
+ pr_err("no more free evdev devices\n");
return -ENFILE;
}
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index 03078c08309a..3367f760d75a 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -23,7 +23,7 @@
/* #define DEBUG */
-#define debug(format, arg...) pr_debug("ff-core: " format "\n", ## arg)
+#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
#include <linux/input.h>
#include <linux/module.h>
@@ -116,7 +116,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
!test_bit(effect->type, dev->ffbit)) {
- debug("invalid or not supported effect type in upload");
+ pr_debug("invalid or not supported effect type in upload\n");
return -EINVAL;
}
@@ -124,7 +124,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
(effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
!test_bit(effect->u.periodic.waveform, dev->ffbit))) {
- debug("invalid or not supported wave form in upload");
+ pr_debug("invalid or not supported wave form in upload\n");
return -EINVAL;
}
@@ -246,7 +246,7 @@ static int flush_effects(struct input_dev *dev, struct file *file)
struct ff_device *ff = dev->ff;
int i;
- debug("flushing now");
+ pr_debug("flushing now\n");
mutex_lock(&ff->mutex);
@@ -315,8 +315,7 @@ int input_ff_create(struct input_dev *dev, int max_effects)
int i;
if (!max_effects) {
- printk(KERN_ERR
- "ff-core: cannot allocate device without any effects\n");
+ pr_err("cannot allocate device without any effects\n");
return -EINVAL;
}
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
index 1d881c96ba8f..117a59aaa70e 100644
--- a/drivers/input/ff-memless.c
+++ b/drivers/input/ff-memless.c
@@ -23,7 +23,7 @@
/* #define DEBUG */
-#define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg)
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/input.h>
@@ -129,7 +129,7 @@ static void ml_schedule_timer(struct ml_device *ml)
int events = 0;
int i;
- debug("calculating next timer");
+ pr_debug("calculating next timer\n");
for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
@@ -149,10 +149,10 @@ static void ml_schedule_timer(struct ml_device *ml)
}
if (!events) {
- debug("no actions");
+ pr_debug("no actions\n");
del_timer(&ml->timer);
} else {
- debug("timer set");
+ pr_debug("timer set\n");
mod_timer(&ml->timer, earliest);
}
}
@@ -173,8 +173,8 @@ static int apply_envelope(struct ml_effect_state *state, int value,
if (envelope->attack_length &&
time_before(now,
state->play_at + msecs_to_jiffies(envelope->attack_length))) {
- debug("value = 0x%x, attack_level = 0x%x", value,
- envelope->attack_level);
+ pr_debug("value = 0x%x, attack_level = 0x%x\n",
+ value, envelope->attack_level);
time_from_level = jiffies_to_msecs(now - state->play_at);
time_of_envelope = envelope->attack_length;
envelope_level = min_t(__s16, envelope->attack_level, 0x7fff);
@@ -191,13 +191,13 @@ static int apply_envelope(struct ml_effect_state *state, int value,
difference = abs(value) - envelope_level;
- debug("difference = %d", difference);
- debug("time_from_level = 0x%x", time_from_level);
- debug("time_of_envelope = 0x%x", time_of_envelope);
+ pr_debug("difference = %d\n", difference);
+ pr_debug("time_from_level = 0x%x\n", time_from_level);
+ pr_debug("time_of_envelope = 0x%x\n", time_of_envelope);
difference = difference * time_from_level / time_of_envelope;
- debug("difference = %d", difference);
+ pr_debug("difference = %d\n", difference);
return value < 0 ?
-(difference + envelope_level) : (difference + envelope_level);
@@ -215,8 +215,7 @@ static int get_compatible_type(struct ff_device *ff, int effect_type)
if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit))
return FF_RUMBLE;
- printk(KERN_ERR
- "ff-memless: invalid type in get_compatible_type()\n");
+ pr_err("invalid type in get_compatible_type()\n");
return 0;
}
@@ -312,7 +311,7 @@ static void ml_combine_effects(struct ff_effect *effect,
break;
default:
- printk(KERN_ERR "ff-memless: invalid type in ml_combine_effects()\n");
+ pr_err("invalid type in ml_combine_effects()\n");
break;
}
@@ -406,7 +405,7 @@ static void ml_effect_timer(unsigned long timer_data)
struct ml_device *ml = dev->ff->private;
unsigned long flags;
- debug("timer: updating effects");
+ pr_debug("timer: updating effects\n");
spin_lock_irqsave(&dev->event_lock, flags);
ml_play_effects(ml);
@@ -438,7 +437,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
struct ml_effect_state *state = &ml->states[effect_id];
if (value > 0) {
- debug("initiated play");
+ pr_debug("initiated play\n");
__set_bit(FF_EFFECT_STARTED, &state->flags);
state->count = value;
@@ -449,7 +448,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
state->adj_at = state->play_at;
} else {
- debug("initiated stop");
+ pr_debug("initiated stop\n");
if (test_bit(FF_EFFECT_PLAYING, &state->flags))
__set_bit(FF_EFFECT_ABORTING, &state->flags);
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index 46239e47a260..dbf741c95835 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -18,13 +18,11 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/gameport.h>
-#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
#include <linux/sched.h> /* HZ */
#include <linux/mutex.h>
-#include <linux/freezer.h>
/*#include <asm/io.h>*/
@@ -234,58 +232,22 @@ struct gameport_event {
static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */
static LIST_HEAD(gameport_event_list);
-static DECLARE_WAIT_QUEUE_HEAD(gameport_wait);
-static struct task_struct *gameport_task;
-static int gameport_queue_event(void *object, struct module *owner,
- enum gameport_event_type event_type)
+static struct gameport_event *gameport_get_event(void)
{
+ struct gameport_event *event = NULL;
unsigned long flags;
- struct gameport_event *event;
- int retval = 0;
spin_lock_irqsave(&gameport_event_lock, flags);
- /*
- * Scan event list for the other events for the same gameport port,
- * starting with the most recent one. If event is the same we
- * do not need add new one. If event is of different type we
- * need to add this event and should not look further because
- * we need to preseve sequence of distinct events.
- */
- list_for_each_entry_reverse(event, &gameport_event_list, node) {
- if (event->object == object) {
- if (event->type == event_type)
- goto out;
- break;
- }
- }
-
- event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
- if (!event) {
- pr_err("Not enough memory to queue event %d\n", event_type);
- retval = -ENOMEM;
- goto out;
- }
-
- if (!try_module_get(owner)) {
- pr_warning("Can't get module reference, dropping event %d\n",
- event_type);
- kfree(event);
- retval = -EINVAL;
- goto out;
+ if (!list_empty(&gameport_event_list)) {
+ event = list_first_entry(&gameport_event_list,
+ struct gameport_event, node);
+ list_del_init(&event->node);
}
- event->type = event_type;
- event->object = object;
- event->owner = owner;
-
- list_add_tail(&event->node, &gameport_event_list);
- wake_up(&gameport_wait);
-
-out:
spin_unlock_irqrestore(&gameport_event_lock, flags);
- return retval;
+ return event;
}
static void gameport_free_event(struct gameport_event *event)
@@ -319,24 +281,8 @@ static void gameport_remove_duplicate_events(struct gameport_event *event)
spin_unlock_irqrestore(&gameport_event_lock, flags);
}
-static struct gameport_event *gameport_get_event(void)
-{
- struct gameport_event *event = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&gameport_event_lock, flags);
-
- if (!list_empty(&gameport_event_list)) {
- event = list_first_entry(&gameport_event_list,
- struct gameport_event, node);
- list_del_init(&event->node);
- }
-
- spin_unlock_irqrestore(&gameport_event_lock, flags);
- return event;
-}
-static void gameport_handle_event(void)
+static void gameport_handle_events(struct work_struct *work)
{
struct gameport_event *event;
@@ -368,6 +314,59 @@ static void gameport_handle_event(void)
mutex_unlock(&gameport_mutex);
}
+static DECLARE_WORK(gameport_event_work, gameport_handle_events);
+
+static int gameport_queue_event(void *object, struct module *owner,
+ enum gameport_event_type event_type)
+{
+ unsigned long flags;
+ struct gameport_event *event;
+ int retval = 0;
+
+ spin_lock_irqsave(&gameport_event_lock, flags);
+
+ /*
+ * Scan event list for the other events for the same gameport port,
+ * starting with the most recent one. If event is the same we
+ * do not need add new one. If event is of different type we
+ * need to add this event and should not look further because
+ * we need to preserve sequence of distinct events.
+ */
+ list_for_each_entry_reverse(event, &gameport_event_list, node) {
+ if (event->object == object) {
+ if (event->type == event_type)
+ goto out;
+ break;
+ }
+ }
+
+ event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
+ if (!event) {
+ pr_err("Not enough memory to queue event %d\n", event_type);
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ if (!try_module_get(owner)) {
+ pr_warning("Can't get module reference, dropping event %d\n",
+ event_type);
+ kfree(event);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ event->type = event_type;
+ event->object = object;
+ event->owner = owner;
+
+ list_add_tail(&event->node, &gameport_event_list);
+ schedule_work(&gameport_event_work);
+
+out:
+ spin_unlock_irqrestore(&gameport_event_lock, flags);
+ return retval;
+}
+
/*
* Remove all events that have been submitted for a given object,
* be it a gameport port or a driver.
@@ -419,19 +418,6 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent)
return child;
}
-static int gameport_thread(void *nothing)
-{
- set_freezable();
- do {
- gameport_handle_event();
- wait_event_freezable(gameport_wait,
- kthread_should_stop() || !list_empty(&gameport_event_list));
- } while (!kthread_should_stop());
-
- return 0;
-}
-
-
/*
* Gameport port operations
*/
@@ -814,13 +800,6 @@ static int __init gameport_init(void)
return error;
}
- gameport_task = kthread_run(gameport_thread, NULL, "kgameportd");
- if (IS_ERR(gameport_task)) {
- bus_unregister(&gameport_bus);
- error = PTR_ERR(gameport_task);
- pr_err("Failed to start kgameportd, error: %d\n", error);
- return error;
- }
return 0;
}
@@ -828,7 +807,12 @@ static int __init gameport_init(void)
static void __exit gameport_exit(void)
{
bus_unregister(&gameport_bus);
- kthread_stop(gameport_task);
+
+ /*
+ * There should not be any outstanding events but work may
+ * still be scheduled so simply cancel it.
+ */
+ cancel_work_sync(&gameport_event_work);
}
subsys_initcall(gameport_init);
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
new file mode 100644
index 000000000000..c48c81f0308d
--- /dev/null
+++ b/drivers/input/input-mt.c
@@ -0,0 +1,170 @@
+/*
+ * 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 <linux/input/mt.h>
+#include <linux/slab.h>
+
+#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, 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)
+{
+ 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)
+ return -ENOMEM;
+
+ 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' */
+ 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_init_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;
+ 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);
+ }