root/drivers/input/mouse/byd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. byd_report_input
  2. byd_clear_touch
  3. byd_process_byte
  4. byd_reset_touchpad
  5. byd_reconnect
  6. byd_disconnect
  7. byd_detect
  8. byd_init

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

/* [<][>][^][v][top][bottom][index][help] */