linux/drivers/input/input-mt.c
Henrik Rydberg 55e49089f4 Input: MT - Handle frame synchronization in core
Most MT drivers perform the same actions on frame synchronization.
Some actions, like dropping unseen contacts, are also unnecessarily
complex. Collect common frame synchronization tasks in a new function,
input_mt_sync_frame(). Depending on the flags set, it drops unseen
contacts and performs pointer emulation.

With init flags and frame synchronization in place, most MT drivers
can be simplified. First out are the bcm5974 and hid-multitouch
drivers, following this patch.

Reviewed-and-tested-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
Tested-by: Ping Cheng <pingc@wacom.com>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
2012-09-19 19:50:19 +02:00

246 lines
6.9 KiB
C

/*
* 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/export.h>
#include <linux/slab.h>
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
{
if (dev->absinfo && test_bit(src, dev->absbit)) {
dev->absinfo[dst] = dev->absinfo[src];
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
}
}
/**
* 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,
unsigned int flags)
{
struct input_mt *mt = dev->mt;
int i;
if (!num_slots)
return 0;
if (mt)
return mt->num_slots != num_slots ? -EINVAL : 0;
mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL);
if (!mt)
return -ENOMEM;
mt->num_slots = num_slots;
mt->flags = flags;
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);
if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) {
__set_bit(EV_KEY, dev->evbit);
__set_bit(BTN_TOUCH, dev->keybit);
copy_abs(dev, ABS_X, ABS_MT_POSITION_X);
copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y);
copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE);
}
if (flags & INPUT_MT_POINTER) {
__set_bit(BTN_TOOL_FINGER, dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
if (num_slots >= 3)
__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
if (num_slots >= 4)
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
if (num_slots >= 5)
__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
__set_bit(INPUT_PROP_POINTER, dev->propbit);
}
if (flags & INPUT_MT_DIRECT)
__set_bit(INPUT_PROP_DIRECT, dev->propbit);
/* Mark slots as 'unused' */
for (i = 0; i < num_slots; i++)
input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
dev->mt = mt;
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;
}
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 *mt = dev->mt;
struct input_mt_slot *slot;
int id;
if (!mt)
return;
slot = &mt->slots[mt->slot];
slot->frame = mt->frame;
if (!active) {
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
return;
}
id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type)
id = input_mt_new_trkid(mt);
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);
input_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, count == 5);
}
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 *mt = dev->mt;
struct input_mt_slot *oldest;
int oldid, count, i;
if (!mt)
return;
oldest = 0;
oldid = mt->trkid;
count = 0;
for (i = 0; i < mt->num_slots; ++i) {
struct input_mt_slot *ps = &mt->slots[i];
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
if (id < 0)
continue;
if ((id - oldid) & TRKID_SGN) {
oldest = ps;
oldid = id;
}
count++;
}
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);
/**
* input_mt_sync_frame() - synchronize mt frame
* @dev: input device with allocated MT slots
*
* Close the frame and prepare the internal state for a new one.
* Depending on the flags, marks unused slots as inactive and performs
* pointer emulation.
*/
void input_mt_sync_frame(struct input_dev *dev)
{
struct input_mt *mt = dev->mt;
struct input_mt_slot *s;
if (!mt)
return;
if (mt->flags & INPUT_MT_DROP_UNUSED) {
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
if (s->frame == mt->frame)
continue;
input_mt_slot(dev, s - mt->slots);
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
}
}
input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER));
mt->frame++;
}
EXPORT_SYMBOL(input_mt_sync_frame);