aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArjan Opmeer <arjan@opmeer.net>2008-10-16 22:10:19 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2008-10-16 22:54:35 -0400
commit2a0bd75e5e687a9c34921e942c18477ea7ec2d63 (patch)
treeca2f113e5bb231aca30b139cee03c20332e5c55b
parent2c6f2cb83b239b7d45da9246cafd27ee615ee35b (diff)
Input: psmouse - add support for Elantech touchpads
This is version 5 of the driver. Relative mode support has been dropped (users wishing to use touchpad in relative mode can use standard PS/2 protocol emulation done in hardware). The driver supports both original version of Elantech protocol and the newer one used by touchpads installed in EeePC. Signed-off-by: Arjan Opmeer <arjan@opmeer.net> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r--Documentation/input/elantech.txt405
-rw-r--r--drivers/input/mouse/Kconfig25
-rw-r--r--drivers/input/mouse/Makefile1
-rw-r--r--drivers/input/mouse/elantech.c674
-rw-r--r--drivers/input/mouse/elantech.h124
-rw-r--r--drivers/input/mouse/psmouse-base.c23
-rw-r--r--drivers/input/mouse/psmouse.h1
7 files changed, 1251 insertions, 2 deletions
diff --git a/Documentation/input/elantech.txt b/Documentation/input/elantech.txt
new file mode 100644
index 000000000000..a10c3b6ba7c4
--- /dev/null
+++ b/Documentation/input/elantech.txt
@@ -0,0 +1,405 @@
1Elantech Touchpad Driver
2========================
3
4 Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
5
6 Extra information for hardware version 1 found and
7 provided by Steve Havelka
8
9 Version 2 (EeePC) hardware support based on patches
10 received from Woody at Xandros and forwarded to me
11 by user StewieGriffin at the eeeuser.com forum
12
13
14Contents
15~~~~~~~~
16
17 1. Introduction
18 2. Extra knobs
19 3. Hardware version 1
20 3.1 Registers
21 3.2 Native relative mode 4 byte packet format
22 3.3 Native absolute mode 4 byte packet format
23 4. Hardware version 2
24 4.1 Registers
25 4.2 Native absolute mode 6 byte packet format
26 4.2.1 One finger touch
27 4.2.2 Two finger touch
28
29
30
311. Introduction
32 ~~~~~~~~~~~~
33
34Currently the Linux Elantech touchpad driver is aware of two different
35hardware versions unimaginatively called version 1 and version 2. Version 1
36is found in "older" laptops and uses 4 bytes per packet. Version 2 seems to
37be introduced with the EeePC and uses 6 bytes per packet.
38
39The driver tries to support both hardware versions and should be compatible
40with the Xorg Synaptics touchpad driver and its graphical configuration
41utilities.
42
43Additionally the operation of the touchpad can be altered by adjusting the
44contents of some of its internal registers. These registers are represented
45by the driver as sysfs entries under /sys/bus/serio/drivers/psmouse/serio?
46that can be read from and written to.
47
48Currently only the registers for hardware version 1 are somewhat understood.
49Hardware version 2 seems to use some of the same registers but it is not
50known whether the bits in the registers represent the same thing or might
51have changed their meaning.
52
53On top of that, some register settings have effect only when the touchpad is
54in relative mode and not in absolute mode. As the Linux Elantech touchpad
55driver always puts the hardware into absolute mode not all information
56mentioned below can be used immediately. But because there is no freely
57available Elantech documentation the information is provided here anyway for
58completeness sake.
59
60
61/////////////////////////////////////////////////////////////////////////////
62
63
642. Extra knobs
65 ~~~~~~~~~~~
66
67Currently the Linux Elantech touchpad driver provides two extra knobs under
68/sys/bus/serio/drivers/psmouse/serio? for the user.
69
70* debug
71
72 Turn different levels of debugging ON or OFF.
73
74 By echoing "0" to this file all debugging will be turned OFF.
75
76 Currently a value of "1" will turn on some basic debugging and a value of
77 "2" will turn on packet debugging. For hardware version 1 the default is
78 OFF. For version 2 the default is "1".
79
80 Turning packet debugging on will make the driver dump every packet
81 received to the syslog before processing it. Be warned that this can
82 generate quite a lot of data!
83
84* paritycheck
85
86 Turns parity checking ON or OFF.
87
88 By echoing "0" to this file parity checking will be turned OFF. Any
89 non-zero value will turn it ON. For hardware version 1 the default is ON.
90 For version 2 the default it is OFF.
91
92 Hardware version 1 provides basic data integrity verification by
93 calculating a parity bit for the last 3 bytes of each packet. The driver
94 can check these bits and reject any packet that appears corrupted. Using
95 this knob you can bypass that check.
96
97 It is not known yet whether hardware version 2 provides the same parity
98 bits. Hence checking is disabled by default. Currently even turning it on
99 will do nothing.
100
101
102/////////////////////////////////////////////////////////////////////////////
103
104
1053. Hardware version 1
106 ==================
107
1083.1 Registers
109 ~~~~~~~~~
110
111By echoing a hexadecimal value to a register it contents can be altered.
112
113For example:
114
115 echo -n 0x16 > reg_10
116
117* reg_10
118
119 bit 7 6 5 4 3 2 1 0
120 B C T D L A S E
121
122 E: 1 = enable smart edges unconditionally
123 S: 1 = enable smart edges only when dragging
124 A: 1 = absolute mode (needs 4 byte packets, see reg_11)
125 L: 1 = enable drag lock (see reg_22)
126 D: 1 = disable dynamic resolution
127 T: 1 = disable tapping
128 C: 1 = enable corner tap
129 B: 1 = swap left and right button
130
131* reg_11
132
133 bit 7 6 5 4 3 2 1 0
134 1 0 0 H V 1 F P
135
136 P: 1 = enable parity checking for relative mode
137 F: 1 = enable native 4 byte packet mode
138 V: 1 = enable vertical scroll area
139 H: 1 = enable horizontal scroll area
140
141* reg_20
142
143 single finger width?
144
145* reg_21
146
147 scroll area width (small: 0x40 ... wide: 0xff)
148
149* reg_22
150
151 drag lock time out (short: 0x14 ... long: 0xfe;
152 0xff = tap again to release)
153
154* reg_23
155
156 tap make timeout?
157
158* reg_24
159
160 tap release timeout?
161
162* reg_25
163
164 smart edge cursor speed (0x02 = slow, 0x03 = medium, 0x04 = fast)
165
166* reg_26
167
168 smart edge activation area width?
169
170
1713.2 Native relative mode 4 byte packet format
172 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173
174byte 0:
175 bit 7 6 5 4 3 2 1 0
176 c c p2 p1 1 M R L
177
178 L, R, M = 1 when Left, Right, Middle mouse button pressed
179 some models have M as byte 3 odd parity bit
180 when parity checking is enabled (reg_11, P = 1):
181 p1..p2 = byte 1 and 2 odd parity bit
182 c = 1 when corner tap detected
183
184byte 1:
185 bit 7 6 5 4 3 2 1 0
186 dx7 dx6 dx5 dx4 dx3 dx2 dx1 dx0
187
188 dx7..dx0 = x movement; positive = right, negative = left
189 byte 1 = 0xf0 when corner tap detected
190
191byte 2:
192 bit 7 6 5 4 3 2 1 0
193 dy7 dy6 dy5 dy4 dy3 dy2 dy1 dy0
194
195 dy7..dy0 = y movement; positive = up, negative = down
196
197byte 3:
198 parity checking enabled (reg_11, P = 1):
199
200 bit 7 6 5 4 3 2 1 0
201 w h n1 n0 ds3 ds2 ds1 ds0
202
203 normally:
204 ds3..ds0 = scroll wheel amount and direction
205 positive = down or left
206 negative = up or right
207 when corner tap detected:
208 ds0 = 1 when top right corner tapped
209 ds1 = 1 when bottom right corner tapped
210 ds2 = 1 when bottom left corner tapped
211 ds3 = 1 when top left corner tapped
212 n1..n0 = number of fingers on touchpad
213 only models with firmware 2.x report this, models with
214 firmware 1.x seem to map one, two and three finger taps
215 directly to L, M and R mouse buttons
216 h = 1 when horizontal scroll action
217 w = 1 when wide finger touch?
218
219 otherwise (reg_11, P = 0):
220
221 bit 7 6 5 4 3 2 1 0
222 ds7 ds6 ds5 ds4 ds3 ds2 ds1 ds0
223
224 ds7..ds0 = vertical scroll amount and direction
225 negative = up
226 positive = down
227
228
2293.3 Native absolute mode 4 byte packet format
230 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
231
232byte 0:
233 firmware version 1.x:
234
235 bit 7 6 5 4 3 2 1 0
236 D U p1 p2 1 p3 R L
237
238 L, R = 1 when Left, Right mouse button pressed
239 p1..p3 = byte 1..3 odd parity bit
240 D, U = 1 when rocker switch pressed Up, Down
241
242 firmware version 2.x:
243
244 bit 7 6 5 4 3 2 1 0
245 n1 n0 p2 p1 1 p3 R L
246
247 L, R = 1 when Left, Right mouse button pressed
248 p1..p3 = byte 1..3 odd parity bit
249 n1..n0 = number of fingers on touchpad
250
251byte 1:
252 firmware version 1.x:
253
254 bit 7 6 5 4 3 2 1 0
255 f 0 th tw x9 x8 y9 y8
256
257 tw = 1 when two finger touch
258 th = 1 when three finger touch
259 f = 1 when finger touch
260
261 firmware version 2.x:
262
263 bit 7 6 5 4 3 2 1 0
264 . . . . x9 x8 y9 y8
265
266byte 2:
267 bit 7 6 5 4 3 2 1 0
268 x7 x6 x5 x4 x3 x2 x1 x0
269
270 x9..x0 = absolute x value (horizontal)
271
272byte 3:
273 bit 7 6 5 4 3 2 1 0
274 y7 y6 y5 y4 y3 y2 y1 y0
275
276 y9..y0 = absolute y value (vertical)
277
278
279/////////////////////////////////////////////////////////////////////////////
280
281
2824. Hardware version 2
283 ==================
284
285
2864.1 Registers
287 ~~~~~~~~~
288
289By echoing a hexadecimal value to a register it contents can be altered.
290
291For example:
292
293 echo -n 0x56 > reg_10
294
295* reg_10
296
297 bit 7 6 5 4 3 2 1 0
298 0 1 0 1 0 1 D 0
299
300 D: 1 = enable drag and drop
301
302* reg_11
303
304 bit 7 6 5 4 3 2 1 0
305 1 0 0 0 S 0 1 0
306
307 S: 1 = enable vertical scroll
308
309* reg_21
310
311 unknown (0x00)
312
313* reg_22
314
315 drag and drop release time out (short: 0x70 ... long 0x7e;
316 0x7f = never i.e. tap again to release)
317
318
3194.2 Native absolute mode 6 byte packet format
320 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
321
3224.2.1 One finger touch
323 ~~~~~~~~~~~~~~~~
324
325byte 0:
326
327 bit 7 6 5 4 3 2 1 0
328 n1 n0 . . . . R L
329
330 L, R = 1 when Left, Right mouse button pressed
331 n1..n0 = numbers of fingers on touchpad
332
333byte 1:
334
335 bit 7 6 5 4 3 2 1 0
336 x15 x14 x13 x12 x11 x10 x9 x8
337
338byte 2:
339
340 bit 7 6 5 4 3 2 1 0
341 x7 x6 x5 x4 x4 x2 x1 x0
342
343 x15..x0 = absolute x value (horizontal)
344
345byte 3:
346
347 bit 7 6 5 4 3 2 1 0
348 . . . . . . . .
349
350byte 4:
351
352 bit 7 6 5 4 3 2 1 0
353 y15 y14 y13 y12 y11 y10 y8 y8
354
355byte 5:
356
357 bit 7 6 5 4 3 2 1 0
358 y7 y6 y5 y4 y3 y2 y1 y0
359
360 y15..y0 = absolute y value (vertical)
361
362
3634.2.2 Two finger touch
364 ~~~~~~~~~~~~~~~~
365
366byte 0:
367
368 bit 7 6 5 4 3 2 1 0
369 n1 n0 ay8 ax8 . . R L
370
371 L, R = 1 when Left, Right mouse button pressed
372 n1..n0 = numbers of fingers on touchpad
373
374byte 1:
375
376 bit 7 6 5 4 3 2 1 0
377 ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0
378
379 ax8..ax0 = first finger absolute x value
380
381byte 2:
382
383 bit 7 6 5 4 3 2 1 0
384 ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0
385
386 ay8..ay0 = first finger absolute y value
387
388byte 3:
389
390 bit 7 6 5 4 3 2 1 0
391 . . by8 bx8 . . . .
392
393byte 4:
394
395 bit 7 6 5 4 3 2 1 0
396 bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
397
398 bx8..bx0 = second finger absolute x value
399
400byte 5:
401
402 bit 7 6 5 4 3 2 1 0
403 by7 by8 by5 by4 by3 by2 by1 by0
404
405 by8..by0 = second finger absolute y value
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index f488b6852baf..4e9934259775 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -25,8 +25,8 @@ config MOUSE_PS2
25 mice with wheels and extra buttons, Microsoft, Logitech or Genius 25 mice with wheels and extra buttons, Microsoft, Logitech or Genius
26 compatible. 26 compatible.
27 27
28 Synaptics TouchPad users might be interested in a specialized 28 Synaptics, ALPS or Elantech TouchPad users might be interested
29 XFree86 driver at: 29 in a specialized Xorg/XFree86 driver at:
30 <http://w1.894.telia.com/~u89404340/touchpad/index.html> 30 <http://w1.894.telia.com/~u89404340/touchpad/index.html>
31 and a new version of GPM at: 31 and a new version of GPM at:
32 <http://www.geocities.com/dt_or/gpm/gpm.html> 32 <http://www.geocities.com/dt_or/gpm/gpm.html>
@@ -87,6 +87,27 @@ config MOUSE_PS2_TRACKPOINT
87 87
88 If unsure, say Y. 88 If unsure, say Y.
89 89
90config MOUSE_PS2_ELANTECH
91 bool "Elantech PS/2 protocol extension"
92 depends on MOUSE_PS2
93 help
94 Say Y here if you have an Elantech PS/2 touchpad connected
95 to your system.
96
97 Note that if you enable this driver you will need an updated
98 X.org Synaptics driver that does not require ABS_PRESSURE
99 reports from the touchpad (i.e. post 1.5.0 version). You can
100 grab a patch for the driver here:
101
102 http://userweb.kernel.org/~dtor/synaptics-no-abspressure.patch
103
104 If unsure, say N.
105
106 This driver exposes some configuration registers via sysfs
107 entries. For further information,
108 see <file:Documentation/input/elantech.txt>.
109
110
90config MOUSE_PS2_TOUCHKIT 111config MOUSE_PS2_TOUCHKIT
91 bool "eGalax TouchKit PS/2 protocol extension" 112 bool "eGalax TouchKit PS/2 protocol extension"
92 depends on MOUSE_PS2 113 depends on MOUSE_PS2
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 8e6e69097801..96f1dd8037f8 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
21psmouse-objs := psmouse-base.o synaptics.o 21psmouse-objs := psmouse-base.o synaptics.o
22 22
23psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o 23psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
24psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
24psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o 25psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
25psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o 26psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
26psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o 27psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
new file mode 100644
index 000000000000..b9a25d57bc5e
--- /dev/null
+++ b/drivers/input/mouse/elantech.c
@@ -0,0 +1,674 @@
1/*
2 * Elantech Touchpad driver (v5)
3 *
4 * Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
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
8 * by the Free Software Foundation.
9 *
10 * Trademarks are the property of their respective owners.
11 */
12
13#include <linux/delay.h>
14#include <linux/module.h>
15#include <linux/input.h>
16#include <linux/serio.h>
17#include <linux/libps2.h>
18#include "psmouse.h"
19#include "elantech.h"
20
21#define elantech_debug(format, arg...) \
22 do { \
23 if (etd->debug) \
24 printk(KERN_DEBUG format, ##arg); \
25 } while (0)
26
27/*
28 * Send a Synaptics style sliced query command
29 */
30static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
31 unsigned char *param)
32{
33 if (psmouse_sliced_command(psmouse, c) ||
34 ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
35 pr_err("elantech.c: synaptics_send_cmd query 0x%02x failed.\n", c);
36 return -1;
37 }
38
39 return 0;
40}
41
42/*
43 * A retrying version of ps2_command
44 */
45static int elantech_ps2_command(struct psmouse *psmouse,
46 unsigned char *param, int command)
47{
48 struct ps2dev *ps2dev = &psmouse->ps2dev;
49 struct elantech_data *etd = psmouse->private;
50 int rc;
51 int tries = ETP_PS2_COMMAND_TRIES;
52
53 do {
54 rc = ps2_command(ps2dev, param, command);
55 if (rc == 0)
56 break;
57 tries--;
58 elantech_debug("elantech.c: retrying ps2 command 0x%02x (%d).\n",
59 command, tries);
60 msleep(ETP_PS2_COMMAND_DELAY);
61 } while (tries > 0);
62
63 if (rc)
64 pr_err("elantech.c: ps2 command 0x%02x failed.\n", command);
65
66 return rc;
67}
68
69/*
70 * Send an Elantech style special command to read a value from a register
71 */
72static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
73 unsigned char *val)
74{
75 struct elantech_data *etd = psmouse->private;
76 unsigned char param[3];
77 int rc = 0;
78
79 if (reg < 0x10 || reg > 0x26)
80 return -1;
81
82 if (reg > 0x11 && reg < 0x20)
83 return -1;
84
85 switch (etd->hw_version) {
86 case 1:
87 if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||
88 psmouse_sliced_command(psmouse, reg) ||
89 ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
90 rc = -1;
91 }
92 break;
93
94 case 2:
95 if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
96 elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) ||
97 elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
98 elantech_ps2_command(psmouse, NULL, reg) ||
99 elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
100 rc = -1;
101 }
102 break;
103 }
104
105 if (rc)
106 pr_err("elantech.c: failed to read register 0x%02x.\n", reg);
107 else
108 *val = param[0];
109
110 return rc;
111}
112
113/*
114 * Send an Elantech style special command to write a register with a value
115 */
116static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
117 unsigned char val)
118{
119 struct elantech_data *etd = psmouse->private;
120 int rc = 0;
121
122 if (reg < 0x10 || reg > 0x26)
123 return -1;
124
125 if (reg > 0x11 && reg < 0x20)
126 return -1;
127
128 switch (etd->hw_version) {
129 case 1:
130 if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
131 psmouse_sliced_command(psmouse, reg) ||
132 psmouse_sliced_command(psmouse, val) ||
133 ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
134 rc = -1;
135 }
136 break;
137
138 case 2:
139 if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
140 elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) ||
141 elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
142 elantech_ps2_command(psmouse, NULL, reg) ||
143 elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
144 elantech_ps2_command(psmouse, NULL, val) ||
145 elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
146 rc = -1;
147 }
148 break;
149 }
150
151 if (rc)
152 pr_err("elantech.c: failed to write register 0x%02x with value 0x%02x.\n",
153 reg, val);
154
155 return rc;
156}
157
158/*
159 * Dump a complete mouse movement packet to the syslog
160 */
161static void elantech_packet_dump(unsigned char *packet, int size)
162{
163 int i;
164
165 printk(KERN_DEBUG "elantech.c: PS/2 packet [");
166 for (i = 0; i < size; i++)
167 printk("%s0x%02x ", (i) ? ", " : " ", packet[i]);
168 printk("]\n");
169}
170
171/*
172 * Interpret complete data packets and report absolute mode input events for
173 * hardware version 1. (4 byte packets)
174 */
175static void elantech_report_absolute_v1(struct psmouse *psmouse)
176{
177 struct input_dev *dev = psmouse->dev;
178 struct elantech_data *etd = psmouse->private;
179 unsigned char *packet = psmouse->packet;
180 int fingers;
181
182 if (etd->fw_version_maj == 0x01) {
183 /* byte 0: D U p1 p2 1 p3 R L
184 byte 1: f 0 th tw x9 x8 y9 y8 */
185 fingers = ((packet[1] & 0x80) >> 7) +
186 ((packet[1] & 0x30) >> 4);
187 } else {
188 /* byte 0: n1 n0 p2 p1 1 p3 R L
189 byte 1: 0 0 0 0 x9 x8 y9 y8 */
190 fingers = (packet[0] & 0xc0) >> 6;
191 }
192
193 input_report_key(dev, BTN_TOUCH, fingers != 0);
194
195 /* byte 2: x7 x6 x5 x4 x3 x2 x1 x0
196 byte 3: y7 y6 y5 y4 y3 y2 y1 y0 */
197 if (fingers) {
198 input_report_abs(dev, ABS_X,
199 ((packet[1] & 0x0c) << 6) | packet[2]);
200 input_report_abs(dev, ABS_Y, ETP_YMAX_V1 -
201 (((packet[1] & 0x03) << 8) | packet[3]));
202 }
203
204 input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
205 input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
206 input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
207 input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
208 input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
209
210 if ((etd->fw_version_maj == 0x01) &&
211 (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
212 /* rocker up */
213 input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
214 /* rocker down */
215 input_report_key(dev, BTN_BACK, packet[0] & 0x80);
216 }
217
218 input_sync(dev);
219}
220
221/*
222 * Interpret complete data packets and report absolute mode input events for
223 * hardware version 2. (6 byte packets)
224 */
225static void elantech_report_absolute_v2(struct psmouse *psmouse)
226{
227 struct input_dev *dev = psmouse->dev;
228 unsigned char *packet = psmouse->packet;
229 int fingers, x1, y1, x2, y2;
230
231 /* byte 0: n1 n0 . . . . R L */
232 fingers = (packet[0] & 0xc0) >> 6;
233 input_report_key(dev, BTN_TOUCH, fingers != 0);
234
235 switch (fingers) {
236 case 1:
237 /* byte 1: x15 x14 x13 x12 x11 x10 x9 x8
238 byte 2: x7 x6 x5 x4 x4 x2 x1 x0 */
239 input_report_abs(dev, ABS_X, (packet[1] << 8) | packet[2]);
240 /* byte 4: y15 y14 y13 y12 y11 y10 y8 y8
241 byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */
242 input_report_abs(dev, ABS_Y, ETP_YMAX_V2 -
243 ((packet[4] << 8) | packet[5]));
244 break;
245
246 case 2:
247 /* The coordinate of each finger is reported separately with
248 a lower resolution for two finger touches */
249 /* byte 0: . . ay8 ax8 . . . .
250 byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 */
251 x1 = ((packet[0] & 0x10) << 4) | packet[1];
252 /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
253 y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]);
254 /* byte 3: . . by8 bx8 . . . .
255 byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 */
256 x2 = ((packet[3] & 0x10) << 4) | packet[4];
257 /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
258 y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]);
259 /* For compatibility with the X Synaptics driver scale up one
260 coordinate and report as ordinary mouse movent */
261 input_report_abs(dev, ABS_X, x1 << 2);
262 input_report_abs(dev, ABS_Y, y1 << 2);
263 /* For compatibility with the proprietary X Elantech driver
264 report both coordinates as hat coordinates */
265 input_report_abs(dev, ABS_HAT0X, x1);
266 input_report_abs(dev, ABS_HAT0Y, y1);
267 input_report_abs(dev, ABS_HAT1X, x2);
268 input_report_abs(dev, ABS_HAT1Y, y2);
269 break;
270 }
271
272 input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
273 input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
274 input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
275 input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
276 input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
277
278 input_sync(dev);
279}
280
281static int elantech_check_parity_v1(struct psmouse *psmouse)
282{
283 struct elantech_data *etd = psmouse->private;
284 unsigned char *packet = psmouse->packet;
285 unsigned char p1, p2, p3;
286
287 /* Parity bits are placed differently */
288 if (etd->fw_version_maj == 0x01) {
289 /* byte 0: D U p1 p2 1 p3 R L */
290 p1 = (packet[0] & 0x20) >> 5;
291 p2 = (packet[0] & 0x10) >> 4;
292 } else {
293 /* byte 0: n1 n0 p2 p1 1 p3 R L */
294 p1 = (packet[0] & 0x10) >> 4;
295 p2 = (packet[0] & 0x20) >> 5;
296 }
297
298 p3 = (packet[0] & 0x04) >> 2;
299
300 return etd->parity[packet[1]] == p1 &&
301 etd->parity[packet[2]] == p2 &&
302 etd->parity[packet[3]] == p3;
303}
304
305/*
306 * Process byte stream from mouse and handle complete packets
307 */
308static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
309{
310 struct elantech_data *etd = psmouse->private;
311
312 if (psmouse->pktcnt < psmouse->pktsize)
313 return PSMOUSE_GOOD_DATA;
314
315 if (etd->debug > 1)
316 elantech_packet_dump(psmouse->packet, psmouse->pktsize);
317
318 switch (etd->hw_version) {
319 case 1:
320 if (etd->paritycheck && !elantech_check_parity_v1(psmouse))
321 return PSMOUSE_BAD_DATA;
322
323 elantech_report_absolute_v1(psmouse);
324 break;
325
326 case 2:
327 /* We don't know how to check parity in protocol v2 */
328 elantech_report_absolute_v2(psmouse);
329 break;
330 }
331
332 return PSMOUSE_FULL_PACKET;
333}
334
335/*
336 * Put the touchpad into absolute mode
337 */
338static int elantech_set_absolute_mode(struct psmouse *psmouse)
339{
340 struct elantech_data *etd = psmouse->private;
341 unsigned char val;
342 int tries = ETP_READ_BACK_TRIES;
343 int rc = 0;
344
345 switch (etd->hw_version) {
346 case 1:
347 etd->reg_10 = 0x16;
348 etd->reg_11 = 0x8f;
349 if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
350 elantech_write_reg(psmouse, 0x11, etd->reg_11)) {
351 rc = -1;
352 }
353 break;
354
355 case 2:
356 /* Windows driver values */
357 etd->reg_10 = 0x54;
358 etd->reg_11 = 0x88; /* 0x8a */
359 etd->reg_21 = 0x60; /* 0x00 */
360 if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
361 elantech_write_reg(psmouse, 0x11, etd->reg_11) ||
362 elantech_write_reg(psmouse, 0x21, etd->reg_21)) {
363 rc = -1;
364 break;
365 }
366 /*
367 * Read back reg 0x10. The touchpad is probably initalising
368 * and not ready until we read back the value we just wrote.
369 */
370 do {
371 rc = elantech_read_reg(psmouse, 0x10, &val);
372 if (rc == 0)
373 break;
374 tries--;
375 elantech_debug("elantech.c: retrying read (%d).\n",
376 tries);
377 msleep(ETP_READ_BACK_DELAY);
378 } while (tries > 0);
379 if (rc)
380 pr_err("elantech.c: failed to read back register 0x10.\n");
381 break;
382 }
383
384 if (rc)
385 pr_err("elantech.c: failed to initialise registers.\n");
386
387 return rc;
388}
389
390/*
391 * Set the appropriate event bits for the input subsystem
392 */
393static void elantech_set_input_params(struct psmouse *psmouse)
394{
395 struct input_dev *dev = psmouse->dev;
396 struct elantech_data *etd = psmouse->private;
397
398 __set_bit(EV_KEY, dev->evbit);
399 __set_bit(EV_ABS, dev->evbit);
400
401 __set_bit(BTN_LEFT, dev->keybit);
402 __set_bit(BTN_RIGHT, dev->keybit);
403
404 __set_bit(BTN_TOUCH, dev->keybit);
405 __set_bit(BTN_TOOL_FINGER, dev->keybit);
406 __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
407 __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
408
409 switch (etd->hw_version) {
410 case 1:
411 /* Rocker button */
412 if ((etd->fw_version_maj == 0x01) &&
413 (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
414 __set_bit(BTN_FORWARD, dev->keybit);
415 __set_bit(BTN_BACK, dev->keybit);
416 }
417 input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, ETP_XMAX_V1, 0, 0);
418 input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, ETP_YMAX_V1, 0, 0);
419 break;
420
421 case 2:
422 input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
423 input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
424 input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
425 input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0);
426 input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
427 input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0);
428 break;
429 }
430}
431
432struct elantech_attr_data {
433 size_t field_offset;
434 unsigned char reg;
435};
436
437/*
438 * Display a register value by reading a sysfs entry
439 */
440static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data,
441 char *buf)
442{
443 struct elantech_data *etd = psmouse->private;
444 struct elantech_attr_data *attr = data;
445 unsigned char *reg = (unsigned char *) etd + attr->field_offset;
446 int rc = 0;
447
448 if (attr->reg)
449 rc = elantech_read_reg(psmouse, attr->reg, reg);
450
451 return sprintf(buf, "0x%02x\n", (attr->reg && rc) ? -1 : *reg);
452}
453
454/*
455 * Write a register value by writing a sysfs entry
456 */
457static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
458 void *data, const char *buf, size_t count)
459{
460 struct elantech_data *etd = psmouse->private;
461 struct elantech_attr_data *attr = data;
462 unsigned char *reg = (unsigned char *) etd + attr->field_offset;
463 unsigned long value;
464 int err;
465
466 err = strict_strtoul(buf, 16, &value);
467 if (err)
468 return err;
469
470 if (value > 0xff)
471 return -EINVAL;
472
473 /* Do we need to preserve some bits for version 2 hardware too? */
474 if (etd->hw_version == 1) {
475 if (attr->reg == 0x10)
476 /* Force absolute mode always on */
477 value |= ETP_R10_ABSOLUTE_MODE;
478 else if (attr->reg == 0x11)
479 /* Force 4 byte mode always on */
480 value |= ETP_R11_4_BYTE_MODE;
481 }
482
483 if (!attr->reg || elantech_write_reg(psmouse, attr->reg, value) == 0)
484 *reg = value;
485
486 return count;
487}
488
489#define ELANTECH_INT_ATTR(_name, _register) \
490 static struct elantech_attr_data elantech_attr_##_name = { \
491 .field_offset = offsetof(struct elantech_data, _name), \
492 .reg = _register, \
493 }; \
494 PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
495 &elantech_attr_##_name, \
496 elantech_show_int_attr, \
497 elantech_set_int_attr)
498
499ELANTECH_INT_ATTR(reg_10, 0x10);
500ELANTECH_INT_ATTR(reg_11, 0x11);
501ELANTECH_INT_ATTR(reg_20, 0x20);
502ELANTECH_INT_ATTR(reg_21, 0x21);
503ELANTECH_INT_ATTR(reg_22, 0x22);
504ELANTECH_INT_ATTR(reg_23, 0x23);
505ELANTECH_INT_ATTR(reg_24, 0x24);
506ELANTECH_INT_ATTR(reg_25, 0x25);
507ELANTECH_INT_ATTR(reg_26, 0x26);
508ELANTECH_INT_ATTR(debug, 0);
509ELANTECH_INT_ATTR(paritycheck, 0);
510
511static struct attribute *elantech_attrs[] = {
512 &psmouse_attr_reg_10.dattr.attr,
513 &psmouse_attr_reg_11.dattr.attr,
514 &psmouse_attr_reg_20.dattr.attr,
515 &psmouse_attr_reg_21.dattr.attr,
516 &psmouse_attr_reg_22.dattr.attr,
517 &psmouse_attr_reg_23.dattr.attr,
518 &psmouse_attr_reg_24.dattr.attr,
519 &psmouse_attr_reg_25.dattr.attr,
520 &psmouse_attr_reg_26.dattr.attr,
521 &psmouse_attr_debug.dattr.attr,
522 &psmouse_attr_paritycheck.dattr.attr,
523 NULL
524};
525
526static struct attribute_group elantech_attr_group = {
527 .attrs = elantech_attrs,
528};
529
530/*
531 * Use magic knock to detect Elantech touchpad
532 */
533int elantech_detect(struct psmouse *psmouse, int set_properties)
534{
535 struct ps2dev *ps2dev = &psmouse->ps2dev;
536 unsigned char param[3];
537
538 ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
539
540 if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
541 ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
542 ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
543 ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
544 ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
545 pr_err("elantech.c: sending Elantech magic knock failed.\n");
546 return -1;
547 }
548
549 /*
550 * Report this in case there are Elantech models that use a different
551 * set of magic numbers
552 */
553 if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) {
554 pr_info("elantech.c: unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
555 param[0], param[1], param[2]);
556 return -1;
557 }
558
559 if (set_properties) {
560 psmouse->vendor = "Elantech";
561 psmouse->name = "Touchpad";
562 }
563
564 return 0;
565}
566
567/*
568 * Clean up sysfs entries when disconnecting
569 */
570static void elantech_disconnect(struct psmouse *psmouse)
571{
572 sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
573 &elantech_attr_group);
574 kfree(psmouse->private);
575 psmouse->private = NULL;
576}
577
578/*
579 * Put the touchpad back into absolute mode when reconnecting
580 */
581static int elantech_reconnect(struct psmouse *psmouse)
582{
583 if (elantech_detect(psmouse, 0))
584 return -1;
585
586 if (elantech_set_absolute_mode(psmouse)) {
587 pr_err("elantech.c: failed to put touchpad back into absolute mode.\n");
588 return -1;
589 }
590
591 return 0;
592}
593
594/*
595 * Initialize the touchpad and create sysfs entries
596 */
597int elantech_init(struct psmouse *psmouse)
598{
599 struct elantech_data *etd;
600 int i, error;
601 unsigned char param[3];
602
603 etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
604 psmouse->private = etd;
605 if (!etd)
606 return -1;
607
608 etd->parity[0] = 1;
609 for (i = 1; i < 256; i++)
610 etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
611
612 /*
613 * Find out what version hardware this is
614 */
615 if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
616 pr_err("elantech.c: failed to query firmware version.\n");
617 goto init_fail;
618 }
619 pr_info("elantech.c: Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n",
620 param[0], param[1], param[2]);
621 etd->fw_version_maj = param[0];
622 etd->fw_version_min = param[2];
623
624 /*
625 * Assume every version greater than this is new EeePC style
626 * hardware with 6 byte packets
627 */
628 if (etd->fw_version_maj >= 0x02 && etd->fw_version_min >= 0x30) {
629 etd->hw_version = 2;
630 /* For now show extra debug information */
631 etd->debug = 1;
632 /* Don't know how to do parity checking for version 2 */
633 etd->paritycheck = 0;
634 } else {
635 etd->hw_version = 1;
636 etd->paritycheck = 1;
637 }
638 pr_info("elantech.c: assuming hardware version %d, firmware version %d.%d\n",
639 etd->hw_version, etd->fw_version_maj, etd->fw_version_min);
640
641 if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) {
642 pr_err("elantech.c: failed to query capabilities.\n");
643 goto init_fail;
644 }
645 pr_info("elantech.c: Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
646 param[0], param[1], param[2]);
647 etd->capabilities = param[0];
648
649 if (elantech_set_absolute_mode(psmouse)) {
650 pr_err("elantech.c: failed to put touchpad into absolute mode.\n");
651 goto init_fail;
652 }
653
654 elantech_set_input_params(psmouse);
655
656 error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
657 &elantech_attr_group);
658 if (error) {
659 pr_err("elantech.c: failed to create sysfs attributes, error: %d.\n",
660 error);
661 goto init_fail;
662 }
663
664 psmouse->protocol_handler = elantech_process_byte;
665 psmouse->disconnect = elantech_disconnect;
666 psmouse->reconnect = elantech_reconnect;
667 psmouse->pktsize = etd->hw_version == 2 ? 6 : 4;
668
669 return 0;
670
671 init_fail:
672 kfree(etd);
673 return -1;
674}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
new file mode 100644
index 000000000000..bee282b540bc
--- /dev/null
+++ b/drivers/input/mouse/elantech.h
@@ -0,0 +1,124 @@
1/*
2 * Elantech Touchpad driver (v5)
3 *
4 * Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
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
8 * by the Free Software Foundation.
9 *
10 * Trademarks are the property of their respective owners.
11 */
12
13#ifndef _ELANTECH_H
14#define _ELANTECH_H
15
16/*
17 * Command values for Synaptics style queries
18 */
19#define ETP_FW_VERSION_QUERY 0x01
20#define ETP_CAPABILITIES_QUERY 0x02
21
22/*
23 * Command values for register reading or writing
24 */
25#define ETP_REGISTER_READ 0x10
26#define ETP_REGISTER_WRITE 0x11
27
28/*
29 * Hardware version 2 custom PS/2 command value
30 */
31#define ETP_PS2_CUSTOM_COMMAND 0xf8
32
33/*
34 * Times to retry a ps2_command and millisecond delay between tries
35 */
36#define ETP_PS2_COMMAND_TRIES 3
37#define ETP_PS2_COMMAND_DELAY 500
38
39/*
40 * Times to try to read back a register and millisecond delay between tries
41 */
42#define ETP_READ_BACK_TRIES 5
43#define ETP_READ_BACK_DELAY 2000
44
45/*
46 * Register bitmasks for hardware version 1
47 */
48#define ETP_R10_ABSOLUTE_MODE 0x04
49#define ETP_R11_4_BYTE_MODE 0x02
50
51/*
52 * Capability bitmasks
53 */
54#define ETP_CAP_HAS_ROCKER 0x04
55
56/*
57 * One hard to find application note states that X axis range is 0 to 576
58 * and Y axis range is 0 to 384 for harware version 1.
59 * Edge fuzz might be necessary because of bezel around the touchpad
60 */
61#define ETP_EDGE_FUZZ_V1 32
62
63#define ETP_XMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1)
64#define ETP_XMAX_V1 (576 - ETP_EDGE_FUZZ_V1)
65#define ETP_YMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1)
66#define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1)
67
68/*
69 * It seems the resolution for hardware version 2 doubled.
70 * Hence the X and Y ranges are doubled too.
71 * The bezel around the pad also appears to be smaller
72 */
73#define ETP_EDGE_FUZZ_V2 8
74
75#define ETP_XMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2)
76#define ETP_XMAX_V2 (1152 - ETP_EDGE_FUZZ_V2)
77#define ETP_YMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2)
78#define ETP_YMAX_V2 ( 768 - ETP_EDGE_FUZZ_V2)
79
80/*
81 * For two finger touches the coordinate of each finger gets reported
82 * separately but with reduced resolution.
83 */
84#define ETP_2FT_FUZZ 4
85
86#define ETP_2FT_XMIN ( 0 + ETP_2FT_FUZZ)
87#define ETP_2FT_XMAX (288 - ETP_2FT_FUZZ)
88#define ETP_2FT_YMIN ( 0 + ETP_2FT_FUZZ)
89#define ETP_2FT_YMAX (192 - ETP_2FT_FUZZ)
90
91struct elantech_data {
92 unsigned char reg_10;
93 unsigned char reg_11;
94 unsigned char reg_20;
95 unsigned char reg_21;
96 unsigned char reg_22;
97 unsigned char reg_23;
98 unsigned char reg_24;
99 unsigned char reg_25;
100 unsigned char reg_26;
101 unsigned char debug;
102 unsigned char capabilities;
103 unsigned char fw_version_maj;
104 unsigned char fw_version_min;
105 unsigned char hw_version;
106 unsigned char paritycheck;
107 unsigned char parity[256];
108};
109
110#ifdef CONFIG_MOUSE_PS2_ELANTECH
111int elantech_detect(struct psmouse *psmouse, int set_properties);
112int elantech_init(struct psmouse *psmouse);
113#else
114static inline int elantech_detect(struct psmouse *psmouse, int set_properties)
115{
116 return -ENOSYS;
117}
118static inline int elantech_init(struct psmouse *psmouse)
119{
120 return -ENOSYS;
121}
122#endif /* CONFIG_MOUSE_PS2_ELANTECH */
123
124#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 126e977e199e..f8f86de694bb 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -29,6 +29,7 @@
29#include "lifebook.h" 29#include "lifebook.h"
30#include "trackpoint.h" 30#include "trackpoint.h"
31#include "touchkit_ps2.h" 31#include "touchkit_ps2.h"
32#include "elantech.h"
32 33
33#define DRIVER_DESC "PS/2 mouse driver" 34#define DRIVER_DESC "PS/2 mouse driver"
34 35
@@ -650,6 +651,19 @@ static int psmouse_extensions(struct psmouse *psmouse,
650 max_proto = PSMOUSE_IMEX; 651 max_proto = PSMOUSE_IMEX;
651 } 652 }
652 653
654/*
655 * Try Elantech touchpad.
656 */
657 if (max_proto > PSMOUSE_IMEX &&
658 elantech_detect(psmouse, set_properties) == 0) {
659 if (!set_properties || elantech_init(psmouse) == 0)
660 return PSMOUSE_ELANTECH;
661/*
662 * Init failed, try basic relative protocols
663 */
664 max_proto = PSMOUSE_IMEX;
665 }
666
653 if (max_proto > PSMOUSE_IMEX) { 667 if (max_proto > PSMOUSE_IMEX) {
654 if (genius_detect(psmouse, set_properties) == 0) 668 if (genius_detect(psmouse, set_properties) == 0)
655 return PSMOUSE_GENPS; 669 return PSMOUSE_GENPS;
@@ -789,6 +803,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
789 .detect = hgpk_detect, 803 .detect = hgpk_detect,
790 }, 804 },
791#endif 805#endif
806#ifdef CONFIG_MOUSE_PS2_ELANTECH
807 {
808 .type = PSMOUSE_ELANTECH,
809 .name = "ETPS/2",
810 .alias = "elantech",
811 .detect = elantech_detect,
812 .init = elantech_init,
813 },
814 #endif
792 { 815 {
793 .type = PSMOUSE_CORTRON, 816 .type = PSMOUSE_CORTRON,
794 .name = "CortronPS/2", 817 .name = "CortronPS/2",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 8b608a1cdd12..54ed267894bd 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -90,6 +90,7 @@ enum psmouse_type {
90 PSMOUSE_TOUCHKIT_PS2, 90 PSMOUSE_TOUCHKIT_PS2,
91 PSMOUSE_CORTRON, 91 PSMOUSE_CORTRON,
92 PSMOUSE_HGPK, 92 PSMOUSE_HGPK,
93 PSMOUSE_ELANTECH,
93 PSMOUSE_AUTO /* This one should always be last */ 94 PSMOUSE_AUTO /* This one should always be last */
94}; 95};
95 96