linux/drivers/input/input-mt.c
<<
>>
Prefs
   1/*
   2 * Input Multitouch Library
   3 *
   4 * Copyright (c) 2008-2010 Henrik Rydberg
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License version 2 as published by
   8 * the Free Software Foundation.
   9 */
  10
  11#include <linux/input/mt.h>
  12#include <linux/export.h>
  13#include <linux/slab.h>
  14
  15#define TRKID_SGN       ((TRKID_MAX + 1) >> 1)
  16
  17static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
  18{
  19        if (dev->absinfo && test_bit(src, dev->absbit)) {
  20                dev->absinfo[dst] = dev->absinfo[src];
  21                dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
  22        }
  23}
  24
  25/**
  26 * input_mt_init_slots() - initialize MT input slots
  27 * @dev: input device supporting MT events and finger tracking
  28 * @num_slots: number of slots used by the device
  29 * @flags: mt tasks to handle in core
  30 *
  31 * This function allocates all necessary memory for MT slot handling
  32 * in the input device, prepares the ABS_MT_SLOT and
  33 * ABS_MT_TRACKING_ID events for use and sets up appropriate buffers.
  34 * Depending on the flags set, it also performs pointer emulation and
  35 * frame synchronization.
  36 *
  37 * May be called repeatedly. Returns -EINVAL if attempting to
  38 * reinitialize with a different number of slots.
  39 */
  40int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
  41                        unsigned int flags)
  42{
  43        struct input_mt *mt = dev->mt;
  44        int i;
  45
  46        if (!num_slots)
  47                return 0;
  48        if (mt)
  49                return mt->num_slots != num_slots ? -EINVAL : 0;
  50
  51        mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL);
  52        if (!mt)
  53                goto err_mem;
  54
  55        mt->num_slots = num_slots;
  56        mt->flags = flags;
  57        input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
  58        input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
  59
  60        if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) {
  61                __set_bit(EV_KEY, dev->evbit);
  62                __set_bit(BTN_TOUCH, dev->keybit);
  63
  64                copy_abs(dev, ABS_X, ABS_MT_POSITION_X);
  65                copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y);
  66                copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE);
  67        }
  68        if (flags & INPUT_MT_POINTER) {
  69                __set_bit(BTN_TOOL_FINGER, dev->keybit);
  70                __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
  71                if (num_slots >= 3)
  72                        __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
  73                if (num_slots >= 4)
  74                        __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
  75                if (num_slots >= 5)
  76                        __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
  77                __set_bit(INPUT_PROP_POINTER, dev->propbit);
  78        }
  79        if (flags & INPUT_MT_DIRECT)
  80                __set_bit(INPUT_PROP_DIRECT, dev->propbit);
  81        if (flags & INPUT_MT_TRACK) {
  82                unsigned int n2 = num_slots * num_slots;
  83                mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
  84                if (!mt->red)
  85                        goto err_mem;
  86        }
  87
  88        /* Mark slots as 'unused' */
  89        for (i = 0; i < num_slots; i++)
  90                input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
  91
  92        dev->mt = mt;
  93        return 0;
  94err_mem:
  95        kfree(mt);
  96        return -ENOMEM;
  97}
  98EXPORT_SYMBOL(input_mt_init_slots);
  99
 100/**
 101 * input_mt_destroy_slots() - frees the MT slots of the input device
 102 * @dev: input device with allocated MT slots
 103 *
 104 * This function is only needed in error path as the input core will
 105 * automatically free the MT slots when the device is destroyed.
 106 */
 107void input_mt_destroy_slots(struct input_dev *dev)
 108{
 109        if (dev->mt) {
 110                kfree(dev->mt->red);
 111                kfree(dev->mt);
 112        }
 113        dev->mt = NULL;
 114}
 115EXPORT_SYMBOL(input_mt_destroy_slots);
 116
 117/**
 118 * input_mt_report_slot_state() - report contact state
 119 * @dev: input device with allocated MT slots
 120 * @tool_type: the tool type to use in this slot
 121 * @active: true if contact is active, false otherwise
 122 *
 123 * Reports a contact via ABS_MT_TRACKING_ID, and optionally
 124 * ABS_MT_TOOL_TYPE. If active is true and the slot is currently
 125 * inactive, or if the tool type is changed, a new tracking id is
 126 * assigned to the slot. The tool type is only reported if the
 127 * corresponding absbit field is set.
 128 */
 129void input_mt_report_slot_state(struct input_dev *dev,
 130                                unsigned int tool_type, bool active)
 131{
 132        struct input_mt *mt = dev->mt;
 133        struct input_mt_slot *slot;
 134        int id;
 135
 136        if (!mt)
 137                return;
 138
 139        slot = &mt->slots[mt->slot];
 140        slot->frame = mt->frame;
 141
 142        if (!active) {
 143                input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
 144                return;
 145        }
 146
 147        id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
 148        if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type)
 149                id = input_mt_new_trkid(mt);
 150
 151        input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
 152        input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);
 153}
 154EXPORT_SYMBOL(input_mt_report_slot_state);
 155
 156/**
 157 * input_mt_report_finger_count() - report contact count
 158 * @dev: input device with allocated MT slots
 159 * @count: the number of contacts
 160 *
 161 * Reports the contact count via BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP,
 162 * BTN_TOOL_TRIPLETAP and BTN_TOOL_QUADTAP.
 163 *
 164 * The input core ensures only the KEY events already setup for
 165 * this device will produce output.
 166 */
 167void input_mt_report_finger_count(struct input_dev *dev, int count)
 168{
 169        input_event(dev, EV_KEY, BTN_TOOL_FINGER, count == 1);
 170        input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2);
 171        input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3);
 172        input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4);
 173        input_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, count == 5);
 174}
 175EXPORT_SYMBOL(input_mt_report_finger_count);
 176
 177/**
 178 * input_mt_report_pointer_emulation() - common pointer emulation
 179 * @dev: input device with allocated MT slots
 180 * @use_count: report number of active contacts as finger count
 181 *
 182 * Performs legacy pointer emulation via BTN_TOUCH, ABS_X, ABS_Y and
 183 * ABS_PRESSURE. Touchpad finger count is emulated if use_count is true.
 184 *
 185 * The input core ensures only the KEY and ABS axes already setup for
 186 * this device will produce output.
 187 */
 188void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 189{
 190        struct input_mt *mt = dev->mt;
 191        struct input_mt_slot *oldest;
 192        int oldid, count, i;
 193
 194        if (!mt)
 195                return;
 196
 197        oldest = 0;
 198        oldid = mt->trkid;
 199        count = 0;
 200
 201        for (i = 0; i < mt->num_slots; ++i) {
 202                struct input_mt_slot *ps = &mt->slots[i];
 203                int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
 204
 205                if (id < 0)
 206                        continue;
 207                if ((id - oldid) & TRKID_SGN) {
 208                        oldest = ps;
 209                        oldid = id;
 210                }
 211                count++;
 212        }
 213
 214        input_event(dev, EV_KEY, BTN_TOUCH, count > 0);
 215        if (use_count)
 216                input_mt_report_finger_count(dev, count);
 217
 218        if (oldest) {
 219                int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
 220                int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y);
 221
 222                input_event(dev, EV_ABS, ABS_X, x);
 223                input_event(dev, EV_ABS, ABS_Y, y);
 224
 225                if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
 226                        int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
 227                        input_event(dev, EV_ABS, ABS_PRESSURE, p);
 228                }
 229        } else {
 230                if (test_bit(ABS_MT_PRESSURE, dev->absbit))
 231                        input_event(dev, EV_ABS, ABS_PRESSURE, 0);
 232        }
 233}
 234EXPORT_SYMBOL(input_mt_report_pointer_emulation);
 235
 236/**
 237 * input_mt_sync_frame() - synchronize mt frame
 238 * @dev: input device with allocated MT slots
 239 *
 240 * Close the frame and prepare the internal state for a new one.
 241 * Depending on the flags, marks unused slots as inactive and performs
 242 * pointer emulation.
 243 */
 244void input_mt_sync_frame(struct input_dev *dev)
 245{
 246        struct input_mt *mt = dev->mt;
 247        struct input_mt_slot *s;
 248
 249        if (!mt)
 250                return;
 251
 252        if (mt->flags & INPUT_MT_DROP_UNUSED) {
 253                for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
 254                        if (s->frame == mt->frame)
 255                                continue;
 256                        input_mt_slot(dev, s - mt->slots);
 257                        input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
 258                }
 259        }
 260
 261        input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER));
 262
 263        mt->frame++;
 264}
 265EXPORT_SYMBOL(input_mt_sync_frame);
 266
 267static int adjust_dual(int *begin, int step, int *end, int eq)
 268{
 269        int f, *p, s, c;
 270
 271        if (begin == end)
 272                return 0;
 273
 274        f = *begin;
 275        p = begin + step;
 276        s = p == end ? f + 1 : *p;
 277
 278        for (; p != end; p += step)
 279                if (*p < f)
 280                        s = f, f = *p;
 281                else if (*p < s)
 282                        s = *p;
 283
 284        c = (f + s + 1) / 2;
 285        if (c == 0 || (c > 0 && !eq))
 286                return 0;
 287        if (s < 0)
 288                c *= 2;
 289
 290        for (p = begin; p != end; p += step)
 291                *p -= c;
 292
 293        return (c < s && s <= 0) || (f >= 0 && f < c);
 294}
 295
 296static void find_reduced_matrix(int *w, int nr, int nc, int nrc)
 297{
 298        int i, k, sum;
 299
 300        for (k = 0; k < nrc; k++) {
 301                for (i = 0; i < nr; i++)
 302                        adjust_dual(w + i, nr, w + i + nrc, nr <= nc);
 303                sum = 0;
 304                for (i = 0; i < nrc; i += nr)
 305                        sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr);
 306                if (!sum)
 307                        break;
 308        }
 309}
 310
 311static int input_mt_set_matrix(struct input_mt *mt,
 312                               const struct input_mt_pos *pos, int num_pos)
 313{
 314        const struct input_mt_pos *p;
 315        struct input_mt_slot *s;
 316        int *w = mt->red;
 317        int x, y;
 318
 319        for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
 320                if (!input_mt_is_active(s))
 321                        continue;
 322                x = input_mt_get_value(s, ABS_MT_POSITION_X);
 323                y = input_mt_get_value(s, ABS_MT_POSITION_Y);
 324                for (p = pos; p != pos + num_pos; p++) {
 325                        int dx = x - p->x, dy = y - p->y;
 326                        *w++ = dx * dx + dy * dy;
 327                }
 328        }
 329
 330        return w - mt->red;
 331}
 332
 333static void input_mt_set_slots(struct input_mt *mt,
 334                               int *slots, int num_pos)
 335{
 336        struct input_mt_slot *s;
 337        int *w = mt->red, *p;
 338
 339        for (p = slots; p != slots + num_pos; p++)
 340                *p = -1;
 341
 342        for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
 343                if (!input_mt_is_active(s))
 344                        continue;
 345                for (p = slots; p != slots + num_pos; p++)
 346                        if (*w++ < 0)
 347                                *p = s - mt->slots;
 348        }
 349
 350        for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
 351                if (input_mt_is_active(s))
 352                        continue;
 353                for (p = slots; p != slots + num_pos; p++)
 354                        if (*p < 0) {
 355                                *p = s - mt->slots;
 356                                break;
 357                        }
 358        }
 359}
 360
 361/**
 362 * input_mt_assign_slots() - perform a best-match assignment
 363 * @dev: input device with allocated MT slots
 364 * @slots: the slot assignment to be filled
 365 * @pos: the position array to match
 366 * @num_pos: number of positions
 367 *
 368 * Performs a best match against the current contacts and returns
 369 * the slot assignment list. New contacts are assigned to unused
 370 * slots.
 371 *
 372 * Returns zero on success, or negative error in case of failure.
 373 */
 374int input_mt_assign_slots(struct input_dev *dev, int *slots,
 375                          const struct input_mt_pos *pos, int num_pos)
 376{
 377        struct input_mt *mt = dev->mt;
 378        int nrc;
 379
 380        if (!mt || !mt->red)
 381                return -ENXIO;
 382        if (num_pos > mt->num_slots)
 383                return -EINVAL;
 384        if (num_pos < 1)
 385                return 0;
 386
 387        nrc = input_mt_set_matrix(mt, pos, num_pos);
 388        find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc);
 389        input_mt_set_slots(mt, slots, num_pos);
 390
 391        return 0;
 392}
 393EXPORT_SYMBOL(input_mt_assign_slots);
 394
 395/**
 396 * input_mt_get_slot_by_key() - return slot matching key
 397 * @dev: input device with allocated MT slots
 398 * @key: the key of the sought slot
 399 *
 400 * Returns the slot of the given key, if it exists, otherwise
 401 * set the key on the first unused slot and return.
 402 *
 403 * If no available slot can be found, -1 is returned.
 404 */
 405int input_mt_get_slot_by_key(struct input_dev *dev, int key)
 406{
 407        struct input_mt *mt = dev->mt;
 408        struct input_mt_slot *s;
 409
 410        if (!mt)
 411                return -1;
 412
 413        for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
 414                if (input_mt_is_active(s) && s->key == key)
 415                        return s - mt->slots;
 416
 417        for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
 418                if (!input_mt_is_active(s)) {
 419                        s->key = key;
 420                        return s - mt->slots;
 421                }
 422
 423        return -1;
 424}
 425EXPORT_SYMBOL(input_mt_get_slot_by_key);
 426
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.