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