2 * BYD TouchPad PS/2 mouse driver
4 * Copyright (C) 2015 Chris Diamand <chris@diamand.org>
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.
11 #include <linux/delay.h>
12 #include <linux/input.h>
13 #include <linux/libps2.h>
14 #include <linux/serio.h>
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)
29 * The touchpad reports gestures in the last byte of each packet. It can take
30 * any of the following values:
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
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. */
48 #define BYD_3DOWN 0x2D
49 #define BYD_3LEFT 0xD4
50 #define BYD_3RIGHT 0x2C
51 /* Four-finger swipe. */
53 #define BYD_4DOWN 0x33
55 int byd_detect(struct psmouse *psmouse, bool set_properties)
57 struct ps2dev *ps2dev = &psmouse->ps2dev;
58 unsigned char param[4];
65 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
67 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
69 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
71 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
73 if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
76 if (param[1] != 0x03 || param[2] != 0x64)
79 psmouse_dbg(psmouse, "BYD touchpad detected\n");
82 psmouse->vendor = "BYD";
83 psmouse->name = "TouchPad";
89 static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
91 struct input_dev *dev = psmouse->dev;
92 u8 *pkt = psmouse->packet;
94 if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
95 psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
97 return PSMOUSE_BAD_DATA;
100 if (psmouse->pktcnt < psmouse->pktsize)
101 return PSMOUSE_GOOD_DATA;
103 /* Otherwise, a full packet has been received */
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];
113 input_report_rel(psmouse->dev, REL_X, dx);
114 input_report_rel(psmouse->dev, REL_Y, -dy);
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);
124 input_report_rel(dev, REL_WHEEL, -1);
129 input_report_rel(dev, REL_WHEEL, 1);
134 input_report_rel(dev, REL_HWHEEL, -1);
137 case BYD_SCROLLRIGHT:
139 input_report_rel(dev, REL_HWHEEL, 1);
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;
162 return PSMOUSE_FULL_PACKET;
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)
170 for (i = 0; i < len; ++i) {
171 if (ps2_command(&psmouse->ps2dev, NULL, seq[i]))
177 /* Keep scrolling after fingers are removed. */
178 #define SCROLL_INERTIAL 0x01
179 #define SCROLL_NO_INERTIAL 0x02
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
186 static int byd_enable(struct psmouse *psmouse)
188 const u8 seq1[] = { 0xE2, 0x00, 0xE0, 0x02, 0xE0 };
193 /* Whether clicking is done by tapping or pressing. */
194 0xD4, CLICK_PRESS_ONLY,
197 /* Vertical and horizontal one-finger scroll zone inertia. */
198 0xD8, SCROLL_INERTIAL,
207 /* Vertical and horizontal two-finger scrolling inertia. */
208 0xE5, SCROLL_INERTIAL,
223 if (byd_send_sequence(psmouse, seq1, ARRAY_SIZE(seq1)))
226 /* Send a 0x01 command, which should return 4 bytes. */
227 if (ps2_command(&psmouse->ps2dev, param, 0x0401))
230 if (byd_send_sequence(psmouse, seq2, ARRAY_SIZE(seq2)))
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.
240 static int byd_send_intellimouse_sequence(struct psmouse *psmouse)
242 struct ps2dev *ps2dev = &psmouse->ps2dev;
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 }
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))
280 static int byd_reset_touchpad(struct psmouse *psmouse)
282 if (byd_send_intellimouse_sequence(psmouse))
285 if (byd_enable(psmouse))
291 static int byd_reconnect(struct psmouse *psmouse)
293 int retry = 0, error = 0;
295 psmouse_dbg(psmouse, "Reconnect\n");
297 psmouse_reset(psmouse);
300 error = byd_detect(psmouse, 0);
301 } while (error && ++retry < 3);
306 psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
308 error = byd_reset_touchpad(psmouse);
310 psmouse_err(psmouse, "Unable to initialize device\n");
317 int byd_init(struct psmouse *psmouse)
319 struct input_dev *dev = psmouse->dev;
321 if (psmouse_reset(psmouse))
324 if (byd_reset_touchpad(psmouse))
327 psmouse->reconnect = byd_reconnect;
328 psmouse->protocol_handler = byd_process_byte;
329 psmouse->pktsize = 4;
330 psmouse->resync_time = 0;
332 __set_bit(BTN_MIDDLE, dev->keybit);
333 __set_bit(REL_WHEEL, dev->relbit);
334 __set_bit(REL_HWHEEL, dev->relbit);