Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: byd - add BYD PS/2 touchpad driver

Driver for the BYD BTP10463 touchpad, found in PC Specialist `Lafite'
laptops. This patch sends the magic command sequence which causes the
touchpad to stream intellimouse-style packets.

Gestures are detected inside the touchpad, and exposed as special
values in the Z component of each packet - absolute coordinates are
not supported, even in the Windows driver. At present, this supports
two-finger vertical and horizontal scrolling, and provides the
framework to expose the other gestures it can recognize.

Signed-off-by: Chris Diamand <chris@diamand.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Chris Diamand and committed by
Dmitry Torokhov
98ee3771 8fb81d20

+381
+10
drivers/input/mouse/Kconfig
··· 48 48 49 49 If unsure, say Y. 50 50 51 + config MOUSE_PS2_BYD 52 + bool "BYD PS/2 mouse protocol extension" if EXPERT 53 + default y 54 + depends on MOUSE_PS2 55 + help 56 + Say Y here if you have a BYD PS/2 touchpad connected to 57 + your system. 58 + 59 + If unsure, say Y. 60 + 51 61 config MOUSE_PS2_LOGIPS2PP 52 62 bool "Logitech PS/2++ mouse protocol extension" if EXPERT 53 63 default y
+1
drivers/input/mouse/Makefile
··· 28 28 psmouse-objs := psmouse-base.o synaptics.o focaltech.o 29 29 30 30 psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o 31 + psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o 31 32 psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o 32 33 psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o 33 34 psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
+337
drivers/input/mouse/byd.c
··· 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 + 16 + #include "psmouse.h" 17 + #include "byd.h" 18 + 19 + #define PS2_Y_OVERFLOW BIT_MASK(7) 20 + #define PS2_X_OVERFLOW BIT_MASK(6) 21 + #define PS2_Y_SIGN BIT_MASK(5) 22 + #define PS2_X_SIGN BIT_MASK(4) 23 + #define PS2_ALWAYS_1 BIT_MASK(3) 24 + #define PS2_MIDDLE BIT_MASK(2) 25 + #define PS2_RIGHT BIT_MASK(1) 26 + #define PS2_LEFT BIT_MASK(0) 27 + 28 + /* 29 + * The touchpad reports gestures in the last byte of each packet. It can take 30 + * any of the following values: 31 + */ 32 + 33 + /* One-finger scrolling in one of the edge scroll zones. */ 34 + #define BYD_SCROLLUP 0xCA 35 + #define BYD_SCROLLDOWN 0x36 36 + #define BYD_SCROLLLEFT 0xCB 37 + #define BYD_SCROLLRIGHT 0x35 38 + /* Two-finger scrolling. */ 39 + #define BYD_2DOWN 0x2B 40 + #define BYD_2UP 0xD5 41 + #define BYD_2LEFT 0xD6 42 + #define BYD_2RIGHT 0x2A 43 + /* Pinching in or out. */ 44 + #define BYD_ZOOMOUT 0xD8 45 + #define BYD_ZOOMIN 0x28 46 + /* Three-finger swipe. */ 47 + #define BYD_3UP 0xD3 48 + #define BYD_3DOWN 0x2D 49 + #define BYD_3LEFT 0xD4 50 + #define BYD_3RIGHT 0x2C 51 + /* Four-finger swipe. */ 52 + #define BYD_4UP 0xCD 53 + #define BYD_4DOWN 0x33 54 + 55 + int byd_detect(struct psmouse *psmouse, bool set_properties) 56 + { 57 + struct ps2dev *ps2dev = &psmouse->ps2dev; 58 + unsigned char param[4]; 59 + 60 + param[0] = 0x03; 61 + param[1] = 0x00; 62 + param[2] = 0x00; 63 + param[3] = 0x00; 64 + 65 + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) 66 + return -1; 67 + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) 68 + return -1; 69 + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) 70 + return -1; 71 + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) 72 + return -1; 73 + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 74 + return -1; 75 + 76 + if (param[1] != 0x03 || param[2] != 0x64) 77 + return -ENODEV; 78 + 79 + psmouse_dbg(psmouse, "BYD touchpad detected\n"); 80 + 81 + if (set_properties) { 82 + psmouse->vendor = "BYD"; 83 + psmouse->name = "TouchPad"; 84 + } 85 + 86 + return 0; 87 + } 88 + 89 + static psmouse_ret_t byd_process_byte(struct psmouse *psmouse) 90 + { 91 + struct input_dev *dev = psmouse->dev; 92 + u8 *pkt = psmouse->packet; 93 + 94 + if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) { 95 + psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n", 96 + pkt[0]); 97 + return PSMOUSE_BAD_DATA; 98 + } 99 + 100 + if (psmouse->pktcnt < psmouse->pktsize) 101 + return PSMOUSE_GOOD_DATA; 102 + 103 + /* Otherwise, a full packet has been received */ 104 + switch (pkt[3]) { 105 + case 0: { 106 + /* Standard packet */ 107 + /* Sign-extend if a sign bit is set. */ 108 + unsigned int signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0; 109 + unsigned int signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0; 110 + int dx = signx | (int) pkt[1]; 111 + int dy = signy | (int) pkt[2]; 112 + 113 + input_report_rel(psmouse->dev, REL_X, dx); 114 + input_report_rel(psmouse->dev, REL_Y, -dy); 115 + 116 + input_report_key(psmouse->dev, BTN_LEFT, pkt[0] & PS2_LEFT); 117 + input_report_key(psmouse->dev, BTN_RIGHT, pkt[0] & PS2_RIGHT); 118 + input_report_key(psmouse->dev, BTN_MIDDLE, pkt[0] & PS2_MIDDLE); 119 + break; 120 + } 121 + 122 + case BYD_SCROLLDOWN: 123 + case BYD_2DOWN: 124 + input_report_rel(dev, REL_WHEEL, -1); 125 + break; 126 + 127 + case BYD_SCROLLUP: 128 + case BYD_2UP: 129 + input_report_rel(dev, REL_WHEEL, 1); 130 + break; 131 + 132 + case BYD_SCROLLLEFT: 133 + case BYD_2LEFT: 134 + input_report_rel(dev, REL_HWHEEL, -1); 135 + break; 136 + 137 + case BYD_SCROLLRIGHT: 138 + case BYD_2RIGHT: 139 + input_report_rel(dev, REL_HWHEEL, 1); 140 + break; 141 + 142 + case BYD_ZOOMOUT: 143 + case BYD_ZOOMIN: 144 + case BYD_3UP: 145 + case BYD_3DOWN: 146 + case BYD_3LEFT: 147 + case BYD_3RIGHT: 148 + case BYD_4UP: 149 + case BYD_4DOWN: 150 + break; 151 + 152 + default: 153 + psmouse_warn(psmouse, 154 + "Unrecognized Z: pkt = %02x %02x %02x %02x\n", 155 + psmouse->packet[0], psmouse->packet[1], 156 + psmouse->packet[2], psmouse->packet[3]); 157 + return PSMOUSE_BAD_DATA; 158 + } 159 + 160 + input_sync(dev); 161 + 162 + return PSMOUSE_FULL_PACKET; 163 + } 164 + 165 + /* Send a sequence of bytes, where each is ACKed before the next is sent. */ 166 + static int byd_send_sequence(struct psmouse *psmouse, const u8 *seq, size_t len) 167 + { 168 + unsigned int i; 169 + 170 + for (i = 0; i < len; ++i) { 171 + if (ps2_command(&psmouse->ps2dev, NULL, seq[i])) 172 + return -1; 173 + } 174 + return 0; 175 + } 176 + 177 + /* Keep scrolling after fingers are removed. */ 178 + #define SCROLL_INERTIAL 0x01 179 + #define SCROLL_NO_INERTIAL 0x02 180 + 181 + /* Clicking can be done by tapping or pressing. */ 182 + #define CLICK_BOTH 0x01 183 + /* Clicking can only be done by pressing. */ 184 + #define CLICK_PRESS_ONLY 0x02 185 + 186 + static int byd_enable(struct psmouse *psmouse) 187 + { 188 + const u8 seq1[] = { 0xE2, 0x00, 0xE0, 0x02, 0xE0 }; 189 + const u8 seq2[] = { 190 + 0xD3, 0x01, 191 + 0xD0, 0x00, 192 + 0xD0, 0x04, 193 + /* Whether clicking is done by tapping or pressing. */ 194 + 0xD4, CLICK_PRESS_ONLY, 195 + 0xD5, 0x01, 196 + 0xD7, 0x03, 197 + /* Vertical and horizontal one-finger scroll zone inertia. */ 198 + 0xD8, SCROLL_INERTIAL, 199 + 0xDA, 0x05, 200 + 0xDB, 0x02, 201 + 0xE4, 0x05, 202 + 0xD6, 0x01, 203 + 0xDE, 0x04, 204 + 0xE3, 0x01, 205 + 0xCF, 0x00, 206 + 0xD2, 0x03, 207 + /* Vertical and horizontal two-finger scrolling inertia. */ 208 + 0xE5, SCROLL_INERTIAL, 209 + 0xD9, 0x02, 210 + 0xD9, 0x07, 211 + 0xDC, 0x03, 212 + 0xDD, 0x03, 213 + 0xDF, 0x03, 214 + 0xE1, 0x03, 215 + 0xD1, 0x00, 216 + 0xCE, 0x00, 217 + 0xCC, 0x00, 218 + 0xE0, 0x00, 219 + 0xE2, 0x01 220 + }; 221 + u8 param[4]; 222 + 223 + if (byd_send_sequence(psmouse, seq1, ARRAY_SIZE(seq1))) 224 + return -1; 225 + 226 + /* Send a 0x01 command, which should return 4 bytes. */ 227 + if (ps2_command(&psmouse->ps2dev, param, 0x0401)) 228 + return -1; 229 + 230 + if (byd_send_sequence(psmouse, seq2, ARRAY_SIZE(seq2))) 231 + return -1; 232 + 233 + return 0; 234 + } 235 + 236 + /* 237 + * Send the set of PS/2 commands required to make it identify as an 238 + * intellimouse with 4-byte instead of 3-byte packets. 239 + */ 240 + static int byd_send_intellimouse_sequence(struct psmouse *psmouse) 241 + { 242 + struct ps2dev *ps2dev = &psmouse->ps2dev; 243 + u8 param[4]; 244 + int i; 245 + const struct { 246 + u16 command; 247 + u8 arg; 248 + } seq[] = { 249 + { PSMOUSE_CMD_RESET_BAT, 0 }, 250 + { PSMOUSE_CMD_RESET_BAT, 0 }, 251 + { PSMOUSE_CMD_GETID, 0 }, 252 + { PSMOUSE_CMD_SETSCALE11, 0 }, 253 + { PSMOUSE_CMD_SETSCALE11, 0 }, 254 + { PSMOUSE_CMD_SETSCALE11, 0 }, 255 + { PSMOUSE_CMD_GETINFO, 0 }, 256 + { PSMOUSE_CMD_SETRES, 0x03 }, 257 + { PSMOUSE_CMD_SETRATE, 0xC8 }, 258 + { PSMOUSE_CMD_SETRATE, 0x64 }, 259 + { PSMOUSE_CMD_SETRATE, 0x50 }, 260 + { PSMOUSE_CMD_GETID, 0 }, 261 + { PSMOUSE_CMD_SETRATE, 0xC8 }, 262 + { PSMOUSE_CMD_SETRATE, 0xC8 }, 263 + { PSMOUSE_CMD_SETRATE, 0x50 }, 264 + { PSMOUSE_CMD_GETID, 0 }, 265 + { PSMOUSE_CMD_SETRATE, 0x64 }, 266 + { PSMOUSE_CMD_SETRES, 0x03 }, 267 + { PSMOUSE_CMD_ENABLE, 0 } 268 + }; 269 + 270 + memset(param, 0, sizeof(param)); 271 + for (i = 0; i < ARRAY_SIZE(seq); ++i) { 272 + param[0] = seq[i].arg; 273 + if (ps2_command(ps2dev, param, seq[i].command)) 274 + return -1; 275 + } 276 + 277 + return 0; 278 + } 279 + 280 + static int byd_reset_touchpad(struct psmouse *psmouse) 281 + { 282 + if (byd_send_intellimouse_sequence(psmouse)) 283 + return -EIO; 284 + 285 + if (byd_enable(psmouse)) 286 + return -EIO; 287 + 288 + return 0; 289 + } 290 + 291 + static int byd_reconnect(struct psmouse *psmouse) 292 + { 293 + int retry = 0, error = 0; 294 + 295 + psmouse_dbg(psmouse, "Reconnect\n"); 296 + do { 297 + psmouse_reset(psmouse); 298 + if (retry) 299 + ssleep(1); 300 + error = byd_detect(psmouse, 0); 301 + } while (error && ++retry < 3); 302 + 303 + if (error) 304 + return error; 305 + 306 + psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry); 307 + 308 + error = byd_reset_touchpad(psmouse); 309 + if (error) { 310 + psmouse_err(psmouse, "Unable to initialize device\n"); 311 + return error; 312 + } 313 + 314 + return 0; 315 + } 316 + 317 + int byd_init(struct psmouse *psmouse) 318 + { 319 + struct input_dev *dev = psmouse->dev; 320 + 321 + if (psmouse_reset(psmouse)) 322 + return -EIO; 323 + 324 + if (byd_reset_touchpad(psmouse)) 325 + return -EIO; 326 + 327 + psmouse->reconnect = byd_reconnect; 328 + psmouse->protocol_handler = byd_process_byte; 329 + psmouse->pktsize = 4; 330 + psmouse->resync_time = 0; 331 + 332 + __set_bit(BTN_MIDDLE, dev->keybit); 333 + __set_bit(REL_WHEEL, dev->relbit); 334 + __set_bit(REL_HWHEEL, dev->relbit); 335 + 336 + return 0; 337 + }
+18
drivers/input/mouse/byd.h
··· 1 + #ifndef _BYD_H 2 + #define _BYD_H 3 + 4 + #ifdef CONFIG_MOUSE_PS2_BYD 5 + int byd_detect(struct psmouse *psmouse, bool set_properties); 6 + int byd_init(struct psmouse *psmouse); 7 + #else 8 + static inline int byd_detect(struct psmouse *psmouse, bool set_properties) 9 + { 10 + return -ENOSYS; 11 + } 12 + static inline int byd_init(struct psmouse *psmouse) 13 + { 14 + return -ENOSYS; 15 + } 16 + #endif /* CONFIG_MOUSE_PS2_BYD */ 17 + 18 + #endif /* _BYD_H */
+14
drivers/input/mouse/psmouse-base.c
··· 37 37 #include "cypress_ps2.h" 38 38 #include "focaltech.h" 39 39 #include "vmmouse.h" 40 + #include "byd.h" 40 41 41 42 #define DRIVER_DESC "PS/2 mouse driver" 42 43 ··· 843 842 .init = vmmouse_init, 844 843 }, 845 844 #endif 845 + #ifdef CONFIG_MOUSE_PS2_BYD 846 + { 847 + .type = PSMOUSE_BYD, 848 + .name = "BydPS/2", 849 + .alias = "byd", 850 + .detect = byd_detect, 851 + .init = byd_init, 852 + }, 853 + #endif 846 854 { 847 855 .type = PSMOUSE_AUTO, 848 856 .name = "auto", ··· 1115 1105 if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2, 1116 1106 &max_proto, set_properties, true)) 1117 1107 return PSMOUSE_TOUCHKIT_PS2; 1108 + 1109 + if (psmouse_try_protocol(psmouse, PSMOUSE_BYD, 1110 + &max_proto, set_properties, true)) 1111 + return PSMOUSE_BYD; 1118 1112 } 1119 1113 1120 1114 /*
+1
drivers/input/mouse/psmouse.h
··· 104 104 PSMOUSE_CYPRESS, 105 105 PSMOUSE_FOCALTECH, 106 106 PSMOUSE_VMMOUSE, 107 + PSMOUSE_BYD, 107 108 PSMOUSE_AUTO /* This one should always be last */ 108 109 }; 109 110