Commit 2a0bd75e5e687a9c34921e942c18477ea7ec2d63

Authored by Arjan Opmeer
Committed by Dmitry Torokhov
1 parent 2c6f2cb83b

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>

Showing 7 changed files with 1251 additions and 2 deletions Side-by-side Diff

Documentation/input/elantech.txt
  1 +Elantech 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 +
  14 +Contents
  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 +
  31 +1. Introduction
  32 + ~~~~~~~~~~~~
  33 +
  34 +Currently the Linux Elantech touchpad driver is aware of two different
  35 +hardware versions unimaginatively called version 1 and version 2. Version 1
  36 +is found in "older" laptops and uses 4 bytes per packet. Version 2 seems to
  37 +be introduced with the EeePC and uses 6 bytes per packet.
  38 +
  39 +The driver tries to support both hardware versions and should be compatible
  40 +with the Xorg Synaptics touchpad driver and its graphical configuration
  41 +utilities.
  42 +
  43 +Additionally the operation of the touchpad can be altered by adjusting the
  44 +contents of some of its internal registers. These registers are represented
  45 +by the driver as sysfs entries under /sys/bus/serio/drivers/psmouse/serio?
  46 +that can be read from and written to.
  47 +
  48 +Currently only the registers for hardware version 1 are somewhat understood.
  49 +Hardware version 2 seems to use some of the same registers but it is not
  50 +known whether the bits in the registers represent the same thing or might
  51 +have changed their meaning.
  52 +
  53 +On top of that, some register settings have effect only when the touchpad is
  54 +in relative mode and not in absolute mode. As the Linux Elantech touchpad
  55 +driver always puts the hardware into absolute mode not all information
  56 +mentioned below can be used immediately. But because there is no freely
  57 +available Elantech documentation the information is provided here anyway for
  58 +completeness sake.
  59 +
  60 +
  61 +/////////////////////////////////////////////////////////////////////////////
  62 +
  63 +
  64 +2. Extra knobs
  65 + ~~~~~~~~~~~
  66 +
  67 +Currently 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 +
  105 +3. Hardware version 1
  106 + ==================
  107 +
  108 +3.1 Registers
  109 + ~~~~~~~~~
  110 +
  111 +By echoing a hexadecimal value to a register it contents can be altered.
  112 +
  113 +For 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 +
  171 +3.2 Native relative mode 4 byte packet format
  172 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  173 +
  174 +byte 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 +
  184 +byte 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 +
  191 +byte 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 +
  197 +byte 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 +
  229 +3.3 Native absolute mode 4 byte packet format
  230 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  231 +
  232 +byte 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 +
  251 +byte 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 +
  266 +byte 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 +
  272 +byte 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 +
  282 +4. Hardware version 2
  283 + ==================
  284 +
  285 +
  286 +4.1 Registers
  287 + ~~~~~~~~~
  288 +
  289 +By echoing a hexadecimal value to a register it contents can be altered.
  290 +
  291 +For 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 +
  319 +4.2 Native absolute mode 6 byte packet format
  320 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  321 +
  322 +4.2.1 One finger touch
  323 + ~~~~~~~~~~~~~~~~
  324 +
  325 +byte 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 +
  333 +byte 1:
  334 +
  335 + bit 7 6 5 4 3 2 1 0
  336 + x15 x14 x13 x12 x11 x10 x9 x8
  337 +
  338 +byte 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 +
  345 +byte 3:
  346 +
  347 + bit 7 6 5 4 3 2 1 0
  348 + . . . . . . . .
  349 +
  350 +byte 4:
  351 +
  352 + bit 7 6 5 4 3 2 1 0
  353 + y15 y14 y13 y12 y11 y10 y8 y8
  354 +
  355 +byte 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 +
  363 +4.2.2 Two finger touch
  364 + ~~~~~~~~~~~~~~~~
  365 +
  366 +byte 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 +
  374 +byte 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 +
  381 +byte 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 +
  388 +byte 3:
  389 +
  390 + bit 7 6 5 4 3 2 1 0
  391 + . . by8 bx8 . . . .
  392 +
  393 +byte 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 +
  400 +byte 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
drivers/input/mouse/Kconfig
... ... @@ -25,8 +25,8 @@
25 25 mice with wheels and extra buttons, Microsoft, Logitech or Genius
26 26 compatible.
27 27  
28   - Synaptics TouchPad users might be interested in a specialized
29   - XFree86 driver at:
  28 + Synaptics, ALPS or Elantech TouchPad users might be interested
  29 + in a specialized Xorg/XFree86 driver at:
30 30 <http://w1.894.telia.com/~u89404340/touchpad/index.html>
31 31 and a new version of GPM at:
32 32 <http://www.geocities.com/dt_or/gpm/gpm.html>
... ... @@ -86,6 +86,27 @@
86 86 to your system.
87 87  
88 88 If unsure, say Y.
  89 +
  90 +config 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 +
89 110  
90 111 config MOUSE_PS2_TOUCHKIT
91 112 bool "eGalax TouchKit PS/2 protocol extension"
drivers/input/mouse/Makefile
... ... @@ -21,6 +21,7 @@
21 21 psmouse-objs := psmouse-base.o synaptics.o
22 22  
23 23 psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
  24 +psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
24 25 psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
25 26 psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
26 27 psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
drivers/input/mouse/elantech.c
  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 + */
  30 +static 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 + */
  45 +static 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 + */
  72 +static 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 + */
  116 +static 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 + */
  161 +static 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 + */
  175 +static 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 + */
  225 +static 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 +
  281 +static 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 + */
  308 +static 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 + */
  338 +static 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 + */
  393 +static 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 +
  432 +struct 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 + */
  440 +static 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 + */
  457 +static 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 +
  499 +ELANTECH_INT_ATTR(reg_10, 0x10);
  500 +ELANTECH_INT_ATTR(reg_11, 0x11);
  501 +ELANTECH_INT_ATTR(reg_20, 0x20);
  502 +ELANTECH_INT_ATTR(reg_21, 0x21);
  503 +ELANTECH_INT_ATTR(reg_22, 0x22);
  504 +ELANTECH_INT_ATTR(reg_23, 0x23);
  505 +ELANTECH_INT_ATTR(reg_24, 0x24);
  506 +ELANTECH_INT_ATTR(reg_25, 0x25);
  507 +ELANTECH_INT_ATTR(reg_26, 0x26);
  508 +ELANTECH_INT_ATTR(debug, 0);
  509 +ELANTECH_INT_ATTR(paritycheck, 0);
  510 +
  511 +static 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 +
  526 +static struct attribute_group elantech_attr_group = {
  527 + .attrs = elantech_attrs,
  528 +};
  529 +
  530 +/*
  531 + * Use magic knock to detect Elantech touchpad
  532 + */
  533 +int 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 + */
  570 +static 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 + */
  581 +static 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 + */
  597 +int 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 +}
drivers/input/mouse/elantech.h
  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 +
  91 +struct 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
  111 +int elantech_detect(struct psmouse *psmouse, int set_properties);
  112 +int elantech_init(struct psmouse *psmouse);
  113 +#else
  114 +static inline int elantech_detect(struct psmouse *psmouse, int set_properties)
  115 +{
  116 + return -ENOSYS;
  117 +}
  118 +static inline int elantech_init(struct psmouse *psmouse)
  119 +{
  120 + return -ENOSYS;
  121 +}
  122 +#endif /* CONFIG_MOUSE_PS2_ELANTECH */
  123 +
  124 +#endif
drivers/input/mouse/psmouse-base.c
... ... @@ -29,6 +29,7 @@
29 29 #include "lifebook.h"
30 30 #include "trackpoint.h"
31 31 #include "touchkit_ps2.h"
  32 +#include "elantech.h"
32 33  
33 34 #define DRIVER_DESC "PS/2 mouse driver"
34 35  
... ... @@ -650,6 +651,19 @@
650 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 667 if (max_proto > PSMOUSE_IMEX) {
654 668 if (genius_detect(psmouse, set_properties) == 0)
655 669 return PSMOUSE_GENPS;
... ... @@ -789,6 +803,15 @@
789 803 .detect = hgpk_detect,
790 804 },
791 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 816 .type = PSMOUSE_CORTRON,
794 817 .name = "CortronPS/2",
drivers/input/mouse/psmouse.h
... ... @@ -90,6 +90,7 @@
90 90 PSMOUSE_TOUCHKIT_PS2,
91 91 PSMOUSE_CORTRON,
92 92 PSMOUSE_HGPK,
  93 + PSMOUSE_ELANTECH,
93 94 PSMOUSE_AUTO /* This one should always be last */
94 95 };
95 96