linux/drivers/input/mouse/byd.c
<<
>>
Prefs
   1/*
   2 * BYD TouchPad PS/2 mouse driver
   3 *
   4 * Copyright (C) 2015 Chris Diamand <chris@diamand.org>
   5 * Copyright (C) 2015 Richard Pospesel
   6 * Copyright (C) 2015 Tai Chi Minh Ralph Eastwood
   7 * Copyright (C) 2015 Martin Wimpress
   8 * Copyright (C) 2015 Jay Kuri
   9 *
  10 * This program is free software; you can redistribute it and/or modify it
  11 * under the terms of the GNU General Public License version 2 as published by
  12 * the Free Software Foundation.
  13 */
  14
  15#include <linux/delay.h>
  16#include <linux/input.h>
  17#include <linux/libps2.h>
  18#include <linux/serio.h>
  19#include <linux/slab.h>
  20
  21#include "psmouse.h"
  22#include "byd.h"
  23
  24/* PS2 Bits */
  25#define PS2_Y_OVERFLOW  BIT_MASK(7)
  26#define PS2_X_OVERFLOW  BIT_MASK(6)
  27#define PS2_Y_SIGN      BIT_MASK(5)
  28#define PS2_X_SIGN      BIT_MASK(4)
  29#define PS2_ALWAYS_1    BIT_MASK(3)
  30#define PS2_MIDDLE      BIT_MASK(2)
  31#define PS2_RIGHT       BIT_MASK(1)
  32#define PS2_LEFT        BIT_MASK(0)
  33
  34/*
  35 * BYD pad constants
  36 */
  37
  38/*
  39 * True device resolution is unknown, however experiments show the
  40 * resolution is about 111 units/mm.
  41 * Absolute coordinate packets are in the range 0-255 for both X and Y
  42 * we pick ABS_X/ABS_Y dimensions which are multiples of 256 and in
  43 * the right ballpark given the touchpad's physical dimensions and estimate
  44 * resolution per spec sheet, device active area dimensions are
  45 * 101.6 x 60.1 mm.
  46 */
  47#define BYD_PAD_WIDTH           11264
  48#define BYD_PAD_HEIGHT          6656
  49#define BYD_PAD_RESOLUTION      111
  50
  51/*
  52 * Given the above dimensions, relative packets velocity is in multiples of
  53 * 1 unit / 11 milliseconds.  We use this dt to estimate distance traveled
  54 */
  55#define BYD_DT                  11
  56/* Time in jiffies used to timeout various touch events (64 ms) */
  57#define BYD_TOUCH_TIMEOUT       msecs_to_jiffies(64)
  58
  59/* BYD commands reverse engineered from windows driver */
  60
  61/*
  62 * Swipe gesture from off-pad to on-pad
  63 *  0 : disable
  64 *  1 : enable
  65 */
  66#define BYD_CMD_SET_OFFSCREEN_SWIPE             0x10cc
  67/*
  68 * Tap and drag delay time
  69 *  0 : disable
  70 *  1 - 8 : least to most delay
  71 */
  72#define BYD_CMD_SET_TAP_DRAG_DELAY_TIME         0x10cf
  73/*
  74 * Physical buttons function mapping
  75 *  0 : enable
  76 *  4 : normal
  77 *  5 : left button custom command
  78 *  6 : right button custom command
  79 *  8 : disable
  80 */
  81#define BYD_CMD_SET_PHYSICAL_BUTTONS            0x10d0
  82/*
  83 * Absolute mode (1 byte X/Y resolution)
  84 *  0 : disable
  85 *  2 : enable
  86 */
  87#define BYD_CMD_SET_ABSOLUTE_MODE               0x10d1
  88/*
  89 * Two finger scrolling
  90 *  1 : vertical
  91 *  2 : horizontal
  92 *  3 : vertical + horizontal
  93 *  4 : disable
  94 */
  95#define BYD_CMD_SET_TWO_FINGER_SCROLL           0x10d2
  96/*
  97 * Handedness
  98 *  1 : right handed
  99 *  2 : left handed
 100 */
 101#define BYD_CMD_SET_HANDEDNESS                  0x10d3
 102/*
 103 * Tap to click
 104 *  1 : enable
 105 *  2 : disable
 106 */
 107#define BYD_CMD_SET_TAP                         0x10d4
 108/*
 109 * Tap and drag
 110 *  1 : tap and hold to drag
 111 *  2 : tap and hold to drag + lock
 112 *  3 : disable
 113 */
 114#define BYD_CMD_SET_TAP_DRAG                    0x10d5
 115/*
 116 * Touch sensitivity
 117 *  1 - 7 : least to most sensitive
 118 */
 119#define BYD_CMD_SET_TOUCH_SENSITIVITY           0x10d6
 120/*
 121 * One finger scrolling
 122 *  1 : vertical
 123 *  2 : horizontal
 124 *  3 : vertical + horizontal
 125 *  4 : disable
 126 */
 127#define BYD_CMD_SET_ONE_FINGER_SCROLL           0x10d7
 128/*
 129 * One finger scrolling function
 130 *  1 : free scrolling
 131 *  2 : edge motion
 132 *  3 : free scrolling + edge motion
 133 *  4 : disable
 134 */
 135#define BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC      0x10d8
 136/*
 137 * Sliding speed
 138 *  1 - 5 : slowest to fastest
 139 */
 140#define BYD_CMD_SET_SLIDING_SPEED               0x10da
 141/*
 142 * Edge motion
 143 *  1 : disable
 144 *  2 : enable when dragging
 145 *  3 : enable when dragging and pointing
 146 */
 147#define BYD_CMD_SET_EDGE_MOTION                 0x10db
 148/*
 149 * Left edge region size
 150 *  0 - 7 : smallest to largest width
 151 */
 152#define BYD_CMD_SET_LEFT_EDGE_REGION            0x10dc
 153/*
 154 * Top edge region size
 155 *  0 - 9 : smallest to largest height
 156 */
 157#define BYD_CMD_SET_TOP_EDGE_REGION             0x10dd
 158/*
 159 * Disregard palm press as clicks
 160 *  1 - 6 : smallest to largest
 161 */
 162#define BYD_CMD_SET_PALM_CHECK                  0x10de
 163/*
 164 * Right edge region size
 165 *  0 - 7 : smallest to largest width
 166 */
 167#define BYD_CMD_SET_RIGHT_EDGE_REGION           0x10df
 168/*
 169 * Bottom edge region size
 170 *  0 - 9 : smallest to largest height
 171 */
 172#define BYD_CMD_SET_BOTTOM_EDGE_REGION          0x10e1
 173/*
 174 * Multitouch gestures
 175 *  1 : enable
 176 *  2 : disable
 177 */
 178#define BYD_CMD_SET_MULTITOUCH                  0x10e3
 179/*
 180 * Edge motion speed
 181 *  0 : control with finger pressure
 182 *  1 - 9 : slowest to fastest
 183 */
 184#define BYD_CMD_SET_EDGE_MOTION_SPEED           0x10e4
 185/*
 186 * Two finger scolling function
 187 *  0 : free scrolling
 188 *  1 : free scrolling (with momentum)
 189 *  2 : edge motion
 190 *  3 : free scrolling (with momentum) + edge motion
 191 *  4 : disable
 192 */
 193#define BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC      0x10e5
 194
 195/*
 196 * The touchpad generates a mixture of absolute and relative packets, indicated
 197 * by the the last byte of each packet being set to one of the following:
 198 */
 199#define BYD_PACKET_ABSOLUTE                     0xf8
 200#define BYD_PACKET_RELATIVE                     0x00
 201/* Multitouch gesture packets */
 202#define BYD_PACKET_PINCH_IN                     0xd8
 203#define BYD_PACKET_PINCH_OUT                    0x28
 204#define BYD_PACKET_ROTATE_CLOCKWISE             0x29
 205#define BYD_PACKET_ROTATE_ANTICLOCKWISE         0xd7
 206#define BYD_PACKET_TWO_FINGER_SCROLL_RIGHT      0x2a
 207#define BYD_PACKET_TWO_FINGER_SCROLL_DOWN       0x2b
 208#define BYD_PACKET_TWO_FINGER_SCROLL_UP         0xd5
 209#define BYD_PACKET_TWO_FINGER_SCROLL_LEFT       0xd6
 210#define BYD_PACKET_THREE_FINGER_SWIPE_RIGHT     0x2c
 211#define BYD_PACKET_THREE_FINGER_SWIPE_DOWN      0x2d
 212#define BYD_PACKET_THREE_FINGER_SWIPE_UP        0xd3
 213#define BYD_PACKET_THREE_FINGER_SWIPE_LEFT      0xd4
 214#define BYD_PACKET_FOUR_FINGER_DOWN             0x33
 215#define BYD_PACKET_FOUR_FINGER_UP               0xcd
 216#define BYD_PACKET_REGION_SCROLL_RIGHT          0x35
 217#define BYD_PACKET_REGION_SCROLL_DOWN           0x36
 218#define BYD_PACKET_REGION_SCROLL_UP             0xca
 219#define BYD_PACKET_REGION_SCROLL_LEFT           0xcb
 220#define BYD_PACKET_RIGHT_CORNER_CLICK           0xd2
 221#define BYD_PACKET_LEFT_CORNER_CLICK            0x2e
 222#define BYD_PACKET_LEFT_AND_RIGHT_CORNER_CLICK  0x2f
 223#define BYD_PACKET_ONTO_PAD_SWIPE_RIGHT         0x37
 224#define BYD_PACKET_ONTO_PAD_SWIPE_DOWN          0x30
 225#define BYD_PACKET_ONTO_PAD_SWIPE_UP            0xd0
 226#define BYD_PACKET_ONTO_PAD_SWIPE_LEFT          0xc9
 227
 228struct byd_data {
 229        struct timer_list timer;
 230        s32 abs_x;
 231        s32 abs_y;
 232        typeof(jiffies) last_touch_time;
 233        bool btn_left;
 234        bool btn_right;
 235        bool touch;
 236};
 237
 238static void byd_report_input(struct psmouse *psmouse)
 239{
 240        struct byd_data *priv = psmouse->private;
 241        struct input_dev *dev = psmouse->dev;
 242
 243        input_report_key(dev, BTN_TOUCH, priv->touch);
 244        input_report_key(dev, BTN_TOOL_FINGER, priv->touch);
 245
 246        input_report_abs(dev, ABS_X, priv->abs_x);
 247        input_report_abs(dev, ABS_Y, priv->abs_y);
 248        input_report_key(dev, BTN_LEFT, priv->btn_left);
 249        input_report_key(dev, BTN_RIGHT, priv->btn_right);
 250
 251        input_sync(dev);
 252}
 253
 254static void byd_clear_touch(unsigned long data)
 255{
 256        struct psmouse *psmouse = (struct psmouse *)data;
 257        struct byd_data *priv = psmouse->private;
 258
 259        serio_pause_rx(psmouse->ps2dev.serio);
 260        priv->touch = false;
 261
 262        byd_report_input(psmouse);
 263
 264        serio_continue_rx(psmouse->ps2dev.serio);
 265
 266        /*
 267         * Move cursor back to center of pad when we lose touch - this
 268         * specifically improves user experience when moving cursor with one
 269         * finger, and pressing a button with another.
 270         */
 271        priv->abs_x = BYD_PAD_WIDTH / 2;
 272        priv->abs_y = BYD_PAD_HEIGHT / 2;
 273}
 274
 275static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
 276{
 277        struct byd_data *priv = psmouse->private;
 278        u8 *pkt = psmouse->packet;
 279
 280        if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
 281                psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
 282                             pkt[0]);
 283                return PSMOUSE_BAD_DATA;
 284        }
 285
 286        if (psmouse->pktcnt < psmouse->pktsize)
 287                return PSMOUSE_GOOD_DATA;
 288
 289        /* Otherwise, a full packet has been received */
 290        switch (pkt[3]) {
 291        case BYD_PACKET_ABSOLUTE:
 292                /* Only use absolute packets for the start of movement. */
 293                if (!priv->touch) {
 294                        /* needed to detect tap */
 295                        typeof(jiffies) tap_time =
 296                                priv->last_touch_time + BYD_TOUCH_TIMEOUT;
 297                        priv->touch = time_after(jiffies, tap_time);
 298
 299                        /* init abs position */
 300                        priv->abs_x = pkt[1] * (BYD_PAD_WIDTH / 256);
 301                        priv->abs_y = (255 - pkt[2]) * (BYD_PAD_HEIGHT / 256);
 302                }
 303                break;
 304        case BYD_PACKET_RELATIVE: {
 305                /* Standard packet */
 306                /* Sign-extend if a sign bit is set. */
 307                u32 signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0;
 308                u32 signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0;
 309                s32 dx = signx | (int) pkt[1];
 310                s32 dy = signy | (int) pkt[2];
 311
 312                /* Update position based on velocity */
 313                priv->abs_x += dx * BYD_DT;
 314                priv->abs_y -= dy * BYD_DT;
 315
 316                priv->touch = true;
 317                break;
 318        }
 319        default:
 320                psmouse_warn(psmouse,
 321                             "Unrecognized Z: pkt = %02x %02x %02x %02x\n",
 322                             psmouse->packet[0], psmouse->packet[1],
 323                             psmouse->packet[2], psmouse->packet[3]);
 324                return PSMOUSE_BAD_DATA;
 325        }
 326
 327        priv->btn_left = pkt[0] & PS2_LEFT;
 328        priv->btn_right = pkt[0] & PS2_RIGHT;
 329
 330        byd_report_input(psmouse);
 331
 332        /* Reset time since last touch. */
 333        if (priv->touch) {
 334                priv->last_touch_time = jiffies;
 335                mod_timer(&priv->timer, jiffies + BYD_TOUCH_TIMEOUT);
 336        }
 337
 338        return PSMOUSE_FULL_PACKET;
 339}
 340
 341static int byd_reset_touchpad(struct psmouse *psmouse)
 342{
 343        struct ps2dev *ps2dev = &psmouse->ps2dev;
 344        u8 param[4];
 345        size_t i;
 346
 347        const struct {
 348                u16 command;
 349                u8 arg;
 350        } seq[] = {
 351                /*
 352                 * Intellimouse initialization sequence, to get 4-byte instead
 353                 * of 3-byte packets.
 354                 */
 355                { PSMOUSE_CMD_SETRATE, 0xC8 },
 356                { PSMOUSE_CMD_SETRATE, 0x64 },
 357                { PSMOUSE_CMD_SETRATE, 0x50 },
 358                { PSMOUSE_CMD_GETID, 0 },
 359                { PSMOUSE_CMD_ENABLE, 0 },
 360                /*
 361                 * BYD-specific initialization, which enables absolute mode and
 362                 * (if desired), the touchpad's built-in gesture detection.
 363                 */
 364                { 0x10E2, 0x00 },
 365                { 0x10E0, 0x02 },
 366                /* The touchpad should reply with 4 seemingly-random bytes */
 367                { 0x14E0, 0x01 },
 368                /* Pairs of parameters and values. */
 369                { BYD_CMD_SET_HANDEDNESS, 0x01 },
 370                { BYD_CMD_SET_PHYSICAL_BUTTONS, 0x04 },
 371                { BYD_CMD_SET_TAP, 0x02 },
 372                { BYD_CMD_SET_ONE_FINGER_SCROLL, 0x04 },
 373                { BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC, 0x04 },
 374                { BYD_CMD_SET_EDGE_MOTION, 0x01 },
 375                { BYD_CMD_SET_PALM_CHECK, 0x00 },
 376                { BYD_CMD_SET_MULTITOUCH, 0x02 },
 377                { BYD_CMD_SET_TWO_FINGER_SCROLL, 0x04 },
 378                { BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC, 0x04 },
 379                { BYD_CMD_SET_LEFT_EDGE_REGION, 0x00 },
 380                { BYD_CMD_SET_TOP_EDGE_REGION, 0x00 },
 381                { BYD_CMD_SET_RIGHT_EDGE_REGION, 0x00 },
 382                { BYD_CMD_SET_BOTTOM_EDGE_REGION, 0x00 },
 383                { BYD_CMD_SET_ABSOLUTE_MODE, 0x02 },
 384                /* Finalize initialization. */
 385                { 0x10E0, 0x00 },
 386                { 0x10E2, 0x01 },
 387        };
 388
 389        for (i = 0; i < ARRAY_SIZE(seq); ++i) {
 390                memset(param, 0, sizeof(param));
 391                param[0] = seq[i].arg;
 392                if (ps2_command(ps2dev, param, seq[i].command))
 393                        return -EIO;
 394        }
 395
 396        psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
 397        return 0;
 398}
 399
 400static int byd_reconnect(struct psmouse *psmouse)
 401{
 402        int retry = 0, error = 0;
 403
 404        psmouse_dbg(psmouse, "Reconnect\n");
 405        do {
 406                psmouse_reset(psmouse);
 407                if (retry)
 408                        ssleep(1);
 409                error = byd_detect(psmouse, 0);
 410        } while (error && ++retry < 3);
 411
 412        if (error)
 413                return error;
 414
 415        psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
 416
 417        error = byd_reset_touchpad(psmouse);
 418        if (error) {
 419                psmouse_err(psmouse, "Unable to initialize device\n");
 420                return error;
 421        }
 422
 423        return 0;
 424}
 425
 426static void byd_disconnect(struct psmouse *psmouse)
 427{
 428        struct byd_data *priv = psmouse->private;
 429
 430        if (priv) {
 431                del_timer(&priv->timer);
 432                kfree(psmouse->private);
 433                psmouse->private = NULL;
 434        }
 435}
 436
 437int byd_detect(struct psmouse *psmouse, bool set_properties)
 438{
 439        struct ps2dev *ps2dev = &psmouse->ps2dev;
 440        u8 param[4] = {0x03, 0x00, 0x00, 0x00};
 441
 442        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
 443                return -1;
 444        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
 445                return -1;
 446        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
 447                return -1;
 448        if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
 449                return -1;
 450        if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
 451                return -1;
 452
 453        if (param[1] != 0x03 || param[2] != 0x64)
 454                return -ENODEV;
 455
 456        psmouse_dbg(psmouse, "BYD touchpad detected\n");
 457
 458        if (set_properties) {
 459                psmouse->vendor = "BYD";
 460                psmouse->name = "TouchPad";
 461        }
 462
 463        return 0;
 464}
 465
 466int byd_init(struct psmouse *psmouse)
 467{
 468        struct input_dev *dev = psmouse->dev;
 469        struct byd_data *priv;
 470
 471        if (psmouse_reset(psmouse))
 472                return -EIO;
 473
 474        if (byd_reset_touchpad(psmouse))
 475                return -EIO;
 476
 477        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 478        if (!priv)
 479                return -ENOMEM;
 480
 481        memset(priv, 0, sizeof(*priv));
 482        setup_timer(&priv->timer, byd_clear_touch, (unsigned long) psmouse);
 483
 484        psmouse->private = priv;
 485        psmouse->disconnect = byd_disconnect;
 486        psmouse->reconnect = byd_reconnect;
 487        psmouse->protocol_handler = byd_process_byte;
 488        psmouse->pktsize = 4;
 489        psmouse->resync_time = 0;
 490
 491        __set_bit(INPUT_PROP_POINTER, dev->propbit);
 492        /* Touchpad */
 493        __set_bit(BTN_TOUCH, dev->keybit);
 494        __set_bit(BTN_TOOL_FINGER, dev->keybit);
 495        /* Buttons */
 496        __set_bit(BTN_LEFT, dev->keybit);
 497        __set_bit(BTN_RIGHT, dev->keybit);
 498        __clear_bit(BTN_MIDDLE, dev->keybit);
 499
 500        /* Absolute position */
 501        __set_bit(EV_ABS, dev->evbit);
 502        input_set_abs_params(dev, ABS_X, 0, BYD_PAD_WIDTH, 0, 0);
 503        input_set_abs_params(dev, ABS_Y, 0, BYD_PAD_HEIGHT, 0, 0);
 504        input_abs_set_res(dev, ABS_X, BYD_PAD_RESOLUTION);
 505        input_abs_set_res(dev, ABS_Y, BYD_PAD_RESOLUTION);
 506        /* No relative support */
 507        __clear_bit(EV_REL, dev->evbit);
 508        __clear_bit(REL_X, dev->relbit);
 509        __clear_bit(REL_Y, dev->relbit);
 510
 511        return 0;
 512}
 513