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 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 guard(serio_pause_rx)(psmouse->ps2dev.serio); 258 259 priv->touch = false; 260 261 byd_report_input(psmouse); 262 263 /* 264 * Move cursor back to center of pad when we lose touch - this 265 * specifically improves user experience when moving cursor with one 266 * finger, and pressing a button with another. 267 */ 268 priv->abs_x = BYD_PAD_WIDTH / 2; 269 priv->abs_y = BYD_PAD_HEIGHT / 2; 270 } 271 272 static psmouse_ret_t byd_process_byte(struct psmouse *psmouse) 273 { 274 struct byd_data *priv = psmouse->private; 275 u8 *pkt = psmouse->packet; 276 277 if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) { 278 psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n", 279 pkt[0]); 280 return PSMOUSE_BAD_DATA; 281 } 282 283 if (psmouse->pktcnt < psmouse->pktsize) 284 return PSMOUSE_GOOD_DATA; 285 286 /* Otherwise, a full packet has been received */ 287 switch (pkt[3]) { 288 case BYD_PACKET_ABSOLUTE: 289 /* Only use absolute packets for the start of movement. */ 290 if (!priv->touch) { 291 /* needed to detect tap */ 292 typeof(jiffies) tap_time = 293 priv->last_touch_time + BYD_TOUCH_TIMEOUT; 294 priv->touch = time_after(jiffies, tap_time); 295 296 /* init abs position */ 297 priv->abs_x = pkt[1] * (BYD_PAD_WIDTH / 256); 298 priv->abs_y = (255 - pkt[2]) * (BYD_PAD_HEIGHT / 256); 299 } 300 break; 301 case BYD_PACKET_RELATIVE: { 302 /* Standard packet */ 303 /* Sign-extend if a sign bit is set. */ 304 u32 signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0; 305 u32 signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0; 306 s32 dx = signx | (int) pkt[1]; 307 s32 dy = signy | (int) pkt[2]; 308 309 /* Update position based on velocity */ 310 priv->abs_x += dx * BYD_DT; 311 priv->abs_y -= dy * BYD_DT; 312 313 priv->touch = true; 314 break; 315 } 316 default: 317 psmouse_warn(psmouse, 318 "Unrecognized Z: pkt = %02x %02x %02x %02x\n", 319 psmouse->packet[0], psmouse->packet[1], 320 psmouse->packet[2], psmouse->packet[3]); 321 return PSMOUSE_BAD_DATA; 322 } 323 324 priv->btn_left = pkt[0] & PS2_LEFT; 325 priv->btn_right = pkt[0] & PS2_RIGHT; 326 327 byd_report_input(psmouse); 328 329 /* Reset time since last touch. */ 330 if (priv->touch) { 331 priv->last_touch_time = jiffies; 332 mod_timer(&priv->timer, jiffies + BYD_TOUCH_TIMEOUT); 333 } 334 335 return PSMOUSE_FULL_PACKET; 336 } 337 338 static int byd_reset_touchpad(struct psmouse *psmouse) 339 { 340 struct ps2dev *ps2dev = &psmouse->ps2dev; 341 u8 param[4]; 342 size_t i; 343 344 static const struct { 345 u16 command; 346 u8 arg; 347 } seq[] = { 348 /* 349 * Intellimouse initialization sequence, to get 4-byte instead 350 * of 3-byte packets. 351 */ 352 { PSMOUSE_CMD_SETRATE, 0xC8 }, 353 { PSMOUSE_CMD_SETRATE, 0x64 }, 354 { PSMOUSE_CMD_SETRATE, 0x50 }, 355 { PSMOUSE_CMD_GETID, 0 }, 356 { PSMOUSE_CMD_ENABLE, 0 }, 357 /* 358 * BYD-specific initialization, which enables absolute mode and 359 * (if desired), the touchpad's built-in gesture detection. 360 */ 361 { 0x10E2, 0x00 }, 362 { 0x10E0, 0x02 }, 363 /* The touchpad should reply with 4 seemingly-random bytes */ 364 { 0x14E0, 0x01 }, 365 /* Pairs of parameters and values. */ 366 { BYD_CMD_SET_HANDEDNESS, 0x01 }, 367 { BYD_CMD_SET_PHYSICAL_BUTTONS, 0x04 }, 368 { BYD_CMD_SET_TAP, 0x02 }, 369 { BYD_CMD_SET_ONE_FINGER_SCROLL, 0x04 }, 370 { BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC, 0x04 }, 371 { BYD_CMD_SET_EDGE_MOTION, 0x01 }, 372 { BYD_CMD_SET_PALM_CHECK, 0x00 }, 373 { BYD_CMD_SET_MULTITOUCH, 0x02 }, 374 { BYD_CMD_SET_TWO_FINGER_SCROLL, 0x04 }, 375 { BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC, 0x04 }, 376 { BYD_CMD_SET_LEFT_EDGE_REGION, 0x00 }, 377 { BYD_CMD_SET_TOP_EDGE_REGION, 0x00 }, 378 { BYD_CMD_SET_RIGHT_EDGE_REGION, 0x00 }, 379 { BYD_CMD_SET_BOTTOM_EDGE_REGION, 0x00 }, 380 { BYD_CMD_SET_ABSOLUTE_MODE, 0x02 }, 381 /* Finalize initialization. */ 382 { 0x10E0, 0x00 }, 383 { 0x10E2, 0x01 }, 384 }; 385 386 for (i = 0; i < ARRAY_SIZE(seq); ++i) { 387 memset(param, 0, sizeof(param)); 388 param[0] = seq[i].arg; 389 if (ps2_command(ps2dev, param, seq[i].command)) 390 return -EIO; 391 } 392 393 psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 394 return 0; 395 } 396 397 static int byd_reconnect(struct psmouse *psmouse) 398 { 399 int retry = 0, error = 0; 400 401 psmouse_dbg(psmouse, "Reconnect\n"); 402 do { 403 psmouse_reset(psmouse); 404 if (retry) 405 ssleep(1); 406 error = byd_detect(psmouse, 0); 407 } while (error && ++retry < 3); 408 409 if (error) 410 return error; 411 412 psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry); 413 414 error = byd_reset_touchpad(psmouse); 415 if (error) { 416 psmouse_err(psmouse, "Unable to initialize device\n"); 417 return error; 418 } 419 420 return 0; 421 } 422 423 static void byd_disconnect(struct psmouse *psmouse) 424 { 425 struct byd_data *priv = psmouse->private; 426 427 if (priv) { 428 del_timer(&priv->timer); 429 kfree(psmouse->private); 430 psmouse->private = NULL; 431 } 432 } 433 434 int byd_detect(struct psmouse *psmouse, bool set_properties) 435 { 436 struct ps2dev *ps2dev = &psmouse->ps2dev; 437 u8 param[4] = {0x03, 0x00, 0x00, 0x00}; 438 439 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) 440 return -1; 441 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) 442 return -1; 443 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) 444 return -1; 445 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) 446 return -1; 447 if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 448 return -1; 449 450 if (param[1] != 0x03 || param[2] != 0x64) 451 return -ENODEV; 452 453 psmouse_dbg(psmouse, "BYD touchpad detected\n"); 454 455 if (set_properties) { 456 psmouse->vendor = "BYD"; 457 psmouse->name = "TouchPad"; 458 } 459 460 return 0; 461 } 462 463 int byd_init(struct psmouse *psmouse) 464 { 465 struct input_dev *dev = psmouse->dev; 466 struct byd_data *priv; 467 468 if (psmouse_reset(psmouse)) 469 return -EIO; 470 471 if (byd_reset_touchpad(psmouse)) 472 return -EIO; 473 474 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 475 if (!priv) 476 return -ENOMEM; 477 478 priv->psmouse = psmouse; 479 timer_setup(&priv->timer, byd_clear_touch, 0); 480 481 psmouse->private = priv; 482 psmouse->disconnect = byd_disconnect; 483 psmouse->reconnect = byd_reconnect; 484 psmouse->protocol_handler = byd_process_byte; 485 psmouse->pktsize = 4; 486 psmouse->resync_time = 0; 487 488 __set_bit(INPUT_PROP_POINTER, dev->propbit); 489 /* Touchpad */ 490 __set_bit(BTN_TOUCH, dev->keybit); 491 __set_bit(BTN_TOOL_FINGER, dev->keybit); 492 /* Buttons */ 493 __set_bit(BTN_LEFT, dev->keybit); 494 __set_bit(BTN_RIGHT, dev->keybit); 495 __clear_bit(BTN_MIDDLE, dev->keybit); 496 497 /* Absolute position */ 498 __set_bit(EV_ABS, dev->evbit); 499 input_set_abs_params(dev, ABS_X, 0, BYD_PAD_WIDTH, 0, 0); 500 input_set_abs_params(dev, ABS_Y, 0, BYD_PAD_HEIGHT, 0, 0); 501 input_abs_set_res(dev, ABS_X, BYD_PAD_RESOLUTION); 502 input_abs_set_res(dev, ABS_Y, BYD_PAD_RESOLUTION); 503 /* No relative support */ 504 __clear_bit(EV_REL, dev->evbit); 505 __clear_bit(REL_X, dev->relbit); 506 __clear_bit(REL_Y, dev->relbit); 507 508 return 0; 509 } 510