linux/drivers/input/mouse/focaltech.c
<<
>>
Prefs
   1/*
   2 * Focaltech TouchPad PS/2 mouse driver
   3 *
   4 * Copyright (c) 2014 Red Hat Inc.
   5 * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * Red Hat authors:
  13 *
  14 * Hans de Goede <hdegoede@redhat.com>
  15 */
  16
  17
  18#include <linux/device.h>
  19#include <linux/libps2.h>
  20#include <linux/input/mt.h>
  21#include <linux/serio.h>
  22#include <linux/slab.h>
  23#include "psmouse.h"
  24#include "focaltech.h"
  25
  26static const char * const focaltech_pnp_ids[] = {
  27        "FLT0101",
  28        "FLT0102",
  29        "FLT0103",
  30        NULL
  31};
  32
  33/*
  34 * Even if the kernel is built without support for Focaltech PS/2 touchpads (or
  35 * when the real driver fails to recognize the device), we still have to detect
  36 * them in order to avoid further detection attempts confusing the touchpad.
  37 * This way it at least works in PS/2 mouse compatibility mode.
  38 */
  39int focaltech_detect(struct psmouse *psmouse, bool set_properties)
  40{
  41        if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
  42                return -ENODEV;
  43
  44        if (set_properties) {
  45                psmouse->vendor = "FocalTech";
  46                psmouse->name = "FocalTech Touchpad";
  47        }
  48
  49        return 0;
  50}
  51
  52static void focaltech_reset(struct psmouse *psmouse)
  53{
  54        ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
  55        psmouse_reset(psmouse);
  56}
  57
  58#ifdef CONFIG_MOUSE_PS2_FOCALTECH
  59
  60/*
  61 * Packet types - the numbers are not consecutive, so we might be missing
  62 * something here.
  63 */
  64#define FOC_TOUCH 0x3 /* bitmap of active fingers */
  65#define FOC_ABS 0x6 /* absolute position of one finger */
  66#define FOC_REL 0x9 /* relative position of 1-2 fingers */
  67
  68#define FOC_MAX_FINGERS 5
  69
  70/*
  71 * Current state of a single finger on the touchpad.
  72 */
  73struct focaltech_finger_state {
  74        /* The touchpad has generated a touch event for the finger */
  75        bool active;
  76
  77        /*
  78         * The touchpad has sent position data for the finger. The
  79         * flag is 0 when the finger is not active, and there is a
  80         * time between the first touch event for the finger and the
  81         * following absolute position packet for the finger where the
  82         * touchpad has declared the finger to be valid, but we do not
  83         * have any valid position yet.
  84         */
  85        bool valid;
  86
  87        /*
  88         * Absolute position (from the bottom left corner) of the
  89         * finger.
  90         */
  91        unsigned int x;
  92        unsigned int y;
  93};
  94
  95/*
  96 * Description of the current state of the touchpad hardware.
  97 */
  98struct focaltech_hw_state {
  99        /*
 100         * The touchpad tracks the positions of the fingers for us,
 101         * the array indices correspond to the finger indices returned
 102         * in the report packages.
 103         */
 104        struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
 105
 106        /*
 107         * Finger width 0-7 and 15 for a very big contact area.
 108         * 15 value stays until the finger is released.
 109         * Width is reported only in absolute packets.
 110         * Since hardware reports width only for last touching finger,
 111         * there is no need to store width for every specific finger,
 112         * so we keep only last value reported.
 113         */
 114        unsigned int width;
 115
 116        /* True if the clickpad has been pressed. */
 117        bool pressed;
 118};
 119
 120struct focaltech_data {
 121        unsigned int x_max, y_max;
 122        struct focaltech_hw_state state;
 123};
 124
 125static void focaltech_report_state(struct psmouse *psmouse)
 126{
 127        struct focaltech_data *priv = psmouse->private;
 128        struct focaltech_hw_state *state = &priv->state;
 129        struct input_dev *dev = psmouse->dev;
 130        int i;
 131
 132        for (i = 0; i < FOC_MAX_FINGERS; i++) {
 133                struct focaltech_finger_state *finger = &state->fingers[i];
 134                bool active = finger->active && finger->valid;
 135
 136                input_mt_slot(dev, i);
 137                input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
 138                if (active) {
 139                        unsigned int clamped_x, clamped_y;
 140                        /*
 141                         * The touchpad might report invalid data, so we clamp
 142                         * the resulting values so that we do not confuse
 143                         * userspace.
 144                         */
 145                        clamped_x = clamp(finger->x, 0U, priv->x_max);
 146                        clamped_y = clamp(finger->y, 0U, priv->y_max);
 147                        input_report_abs(dev, ABS_MT_POSITION_X, clamped_x);
 148                        input_report_abs(dev, ABS_MT_POSITION_Y,
 149                                         priv->y_max - clamped_y);
 150                        input_report_abs(dev, ABS_TOOL_WIDTH, state->width);
 151                }
 152        }
 153        input_mt_report_pointer_emulation(dev, true);
 154
 155        input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
 156        input_sync(psmouse->dev);
 157}
 158
 159static void focaltech_process_touch_packet(struct psmouse *psmouse,
 160                                           unsigned char *packet)
 161{
 162        struct focaltech_data *priv = psmouse->private;
 163        struct focaltech_hw_state *state = &priv->state;
 164        unsigned char fingers = packet[1];
 165        int i;
 166
 167        state->pressed = (packet[0] >> 4) & 1;
 168
 169        /* the second byte contains a bitmap of all fingers touching the pad */
 170        for (i = 0; i < FOC_MAX_FINGERS; i++) {
 171                state->fingers[i].active = fingers & 0x1;
 172                if (!state->fingers[i].active) {
 173                        /*
 174                         * Even when the finger becomes active again, we still
 175                         * will have to wait for the first valid position.
 176                         */
 177                        state->fingers[i].valid = false;
 178                }
 179                fingers >>= 1;
 180        }
 181}
 182
 183static void focaltech_process_abs_packet(struct psmouse *psmouse,
 184                                         unsigned char *packet)
 185{
 186        struct focaltech_data *priv = psmouse->private;
 187        struct focaltech_hw_state *state = &priv->state;
 188        unsigned int finger;
 189
 190        finger = (packet[1] >> 4) - 1;
 191        if (finger >= FOC_MAX_FINGERS) {
 192                psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
 193                            finger);
 194                return;
 195        }
 196
 197        state->pressed = (packet[0] >> 4) & 1;
 198
 199        state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
 200        state->fingers[finger].y = (packet[3] << 8) | packet[4];
 201        state->width = packet[5] >> 4;
 202        state->fingers[finger].valid = true;
 203}
 204
 205static void focaltech_process_rel_packet(struct psmouse *psmouse,
 206                                         unsigned char *packet)
 207{
 208        struct focaltech_data *priv = psmouse->private;
 209        struct focaltech_hw_state *state = &priv->state;
 210        int finger1, finger2;
 211
 212        state->pressed = packet[0] >> 7;
 213        finger1 = ((packet[0] >> 4) & 0x7) - 1;
 214        if (finger1 < FOC_MAX_FINGERS) {
 215                state->fingers[finger1].x += (char)packet[1];
 216                state->fingers[finger1].y += (char)packet[2];
 217        } else {
 218                psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
 219                            finger1);
 220        }
 221
 222        /*
 223         * If there is an odd number of fingers, the last relative
 224         * packet only contains one finger. In this case, the second
 225         * finger index in the packet is 0 (we subtract 1 in the lines
 226         * above to create array indices, so the finger will overflow
 227         * and be above FOC_MAX_FINGERS).
 228         */
 229        finger2 = ((packet[3] >> 4) & 0x7) - 1;
 230        if (finger2 < FOC_MAX_FINGERS) {
 231                state->fingers[finger2].x += (char)packet[4];
 232                state->fingers[finger2].y += (char)packet[5];
 233        }
 234}
 235
 236static void focaltech_process_packet(struct psmouse *psmouse)
 237{
 238        unsigned char *packet = psmouse->packet;
 239
 240        switch (packet[0] & 0xf) {
 241        case FOC_TOUCH:
 242                focaltech_process_touch_packet(psmouse, packet);
 243                break;
 244
 245        case FOC_ABS:
 246                focaltech_process_abs_packet(psmouse, packet);
 247                break;
 248
 249        case FOC_REL:
 250                focaltech_process_rel_packet(psmouse, packet);
 251                break;
 252
 253        default:
 254                psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
 255                break;
 256        }
 257
 258        focaltech_report_state(psmouse);
 259}
 260
 261static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
 262{
 263        if (psmouse->pktcnt >= 6) { /* Full packet received */
 264                focaltech_process_packet(psmouse);
 265                return PSMOUSE_FULL_PACKET;
 266        }
 267
 268        /*
 269         * We might want to do some validation of the data here, but
 270         * we do not know the protocol well enough
 271         */
 272        return PSMOUSE_GOOD_DATA;
 273}
 274
 275static int focaltech_switch_protocol(struct psmouse *psmouse)
 276{
 277        struct ps2dev *ps2dev = &psmouse->ps2dev;
 278        unsigned char param[3];
 279
 280        param[0] = 0;
 281        if (ps2_command(ps2dev, param, 0x10f8))
 282                return -EIO;
 283
 284        if (ps2_command(ps2dev, param, 0x10f8))
 285                return -EIO;
 286
 287        if (ps2_command(ps2dev, param, 0x10f8))
 288                return -EIO;
 289
 290        param[0] = 1;
 291        if (ps2_command(ps2dev, param, 0x10f8))
 292                return -EIO;
 293
 294        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
 295                return -EIO;
 296
 297        if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
 298                return -EIO;
 299
 300        return 0;
 301}
 302
 303static void focaltech_disconnect(struct psmouse *psmouse)
 304{
 305        focaltech_reset(psmouse);
 306        kfree(psmouse->private);
 307        psmouse->private = NULL;
 308}
 309
 310static int focaltech_reconnect(struct psmouse *psmouse)
 311{
 312        int error;
 313
 314        focaltech_reset(psmouse);
 315
 316        error = focaltech_switch_protocol(psmouse);
 317        if (error) {
 318                psmouse_err(psmouse, "Unable to initialize the device\n");
 319                return error;
 320        }
 321
 322        return 0;
 323}
 324
 325static void focaltech_set_input_params(struct psmouse *psmouse)
 326{
 327        struct input_dev *dev = psmouse->dev;
 328        struct focaltech_data *priv = psmouse->private;
 329
 330        /*
 331         * Undo part of setup done for us by psmouse core since touchpad
 332         * is not a relative device.
 333         */
 334        __clear_bit(EV_REL, dev->evbit);
 335        __clear_bit(REL_X, dev->relbit);
 336        __clear_bit(REL_Y, dev->relbit);
 337        __clear_bit(BTN_RIGHT, dev->keybit);
 338        __clear_bit(BTN_MIDDLE, dev->keybit);
 339
 340        /*
 341         * Now set up our capabilities.
 342         */
 343        __set_bit(EV_ABS, dev->evbit);
 344        input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
 345        input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
 346        input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
 347        input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
 348        __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
 349}
 350
 351static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
 352                                   unsigned char *param)
 353{
 354        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
 355                return -EIO;
 356
 357        param[0] = 0;
 358        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
 359                return -EIO;
 360
 361        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
 362                return -EIO;
 363
 364        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
 365                return -EIO;
 366
 367        param[0] = reg;
 368        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
 369                return -EIO;
 370
 371        if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
 372                return -EIO;
 373
 374        return 0;
 375}
 376
 377static int focaltech_read_size(struct psmouse *psmouse)
 378{
 379        struct ps2dev *ps2dev = &psmouse->ps2dev;
 380        struct focaltech_data *priv = psmouse->private;
 381        char param[3];
 382
 383        if (focaltech_read_register(ps2dev, 2, param))
 384                return -EIO;
 385
 386        /* not sure whether this is 100% correct */
 387        priv->x_max = (unsigned char)param[1] * 128;
 388        priv->y_max = (unsigned char)param[2] * 128;
 389
 390        return 0;
 391}
 392
 393void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution)
 394{
 395        /* not supported yet */
 396}
 397
 398static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate)
 399{
 400        /* not supported yet */
 401}
 402
 403static void focaltech_set_scale(struct psmouse *psmouse,
 404                                enum psmouse_scale scale)
 405{
 406        /* not supported yet */
 407}
 408
 409int focaltech_init(struct psmouse *psmouse)
 410{
 411        struct focaltech_data *priv;
 412        int error;
 413
 414        psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
 415                                          GFP_KERNEL);
 416        if (!priv)
 417                return -ENOMEM;
 418
 419        focaltech_reset(psmouse);
 420
 421        error = focaltech_read_size(psmouse);
 422        if (error) {
 423                psmouse_err(psmouse,
 424                            "Unable to read the size of the touchpad\n");
 425                goto fail;
 426        }
 427
 428        error = focaltech_switch_protocol(psmouse);
 429        if (error) {
 430                psmouse_err(psmouse, "Unable to initialize the device\n");
 431                goto fail;
 432        }
 433
 434        focaltech_set_input_params(psmouse);
 435
 436        psmouse->protocol_handler = focaltech_process_byte;
 437        psmouse->pktsize = 6;
 438        psmouse->disconnect = focaltech_disconnect;
 439        psmouse->reconnect = focaltech_reconnect;
 440        psmouse->cleanup = focaltech_reset;
 441        /* resync is not supported yet */
 442        psmouse->resync_time = 0;
 443        /*
 444         * rate/resolution/scale changes are not supported yet, and
 445         * the generic implementations of these functions seem to
 446         * confuse some touchpads
 447         */
 448        psmouse->set_resolution = focaltech_set_resolution;
 449        psmouse->set_rate = focaltech_set_rate;
 450        psmouse->set_scale = focaltech_set_scale;
 451
 452        return 0;
 453
 454fail:
 455        focaltech_reset(psmouse);
 456        kfree(priv);
 457        return error;
 458}
 459
 460#else /* CONFIG_MOUSE_PS2_FOCALTECH */
 461
 462int focaltech_init(struct psmouse *psmouse)
 463{
 464        focaltech_reset(psmouse);
 465
 466        return 0;
 467}
 468
 469#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
 470