byd.c (13034B)
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 225struct 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 236static 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 252static 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 273static 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 339static 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 398static 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 424static 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 435int 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 464int 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}