From: Arjan Opmeer <arjan@xxxxxxxxxx> Update the Elantech touchpad driver to v5 - Put the Elantech entry last in the psmouse_protocols[] list as it is also the last one in the psmouse_type enum. - Remove support for relative mode. The driver now always uses the touchpad in absolute mode. - Add support for the new Elantech touchpad model as found in the EeePC that uses a new protocol. Signed-off-by: Arjan Opmeer <arjan@xxxxxxxxxx> --- diff --git a/Documentation/input/elantech.txt b/Documentation/input/elantech.txt index 679e74b..a10c3b6 100644 --- a/Documentation/input/elantech.txt +++ b/Documentation/input/elantech.txt @@ -1,27 +1,125 @@ Elantech Touchpad Driver ======================== - Copyright (C) 2007 Arjan Opmeer <arjan@xxxxxxxxxx> - Extra information found and provided by Steve Havelka + Copyright (C) 2007-2008 Arjan Opmeer <arjan@xxxxxxxxxx> -Configuration of the touchpad is performed by writing values to registers -found under sysfs entries /sys/bus/serio/drivers/psmouse/serio?/reg_*. + Extra information for hardware version 1 found and + provided by Steve Havelka -E.g. to disable tapping while leaving the other settings to the default -Windows driver value one would: + Version 2 (EeePC) hardware support based on patches + received from Woody at Xandros and forwarded to me + by user StewieGriffin at the eeeuser.com forum - echo -n 0x32 > reg_10 +Contents +~~~~~~~~ -Registers -~~~~~~~~~ + 1. Introduction + 2. Extra knobs + 3. Hardware version 1 + 3.1 Registers + 3.2 Native relative mode 4 byte packet format + 3.3 Native absolute mode 4 byte packet format + 4. Hardware version 2 + 4.1 Registers + 4.2 Native absolute mode 6 byte packet format + 4.2.1 One finger touch + 4.2.2 Two finger touch -* reg_10 (Windows driver default value 0x12) + + +1. Introduction + ~~~~~~~~~~~~ + +Currently the Linux Elantech touchpad driver is aware of two different +hardware versions unimaginatively called version 1 and version 2. Version 1 +is found in "older" laptops and uses 4 bytes per packet. Version 2 seems to +be introduced with the EeePC and uses 6 bytes per packet. + +The driver tries to support both hardware versions and should be compatible +with the Xorg Synaptics touchpad driver and its graphical configuration +utilities. + +Additionally the operation of the touchpad can be altered by adjusting the +contents of some of its internal registers. These registers are represented +by the driver as sysfs entries under /sys/bus/serio/drivers/psmouse/serio? +that can be read from and written to. + +Currently only the registers for hardware version 1 are somewhat understood. +Hardware version 2 seems to use some of the same registers but it is not +known whether the bits in the registers represent the same thing or might +have changed their meaning. + +On top of that, some register settings have effect only when the touchpad is +in relative mode and not in absolute mode. As the Linux Elantech touchpad +driver always puts the hardware into absolute mode not all information +mentioned below can be used immediately. But because there is no freely +available Elantech documentation the information is provided here anyway for +completeness sake. + + +///////////////////////////////////////////////////////////////////////////// + + +2. Extra knobs + ~~~~~~~~~~~ + +Currently the Linux Elantech touchpad driver provides two extra knobs under +/sys/bus/serio/drivers/psmouse/serio? for the user. + +* debug + + Turn different levels of debugging ON or OFF. + + By echoing "0" to this file all debugging will be turned OFF. + + Currently a value of "1" will turn on some basic debugging and a value of + "2" will turn on packet debugging. For hardware version 1 the default is + OFF. For version 2 the default is "1". + + Turning packet debugging on will make the driver dump every packet + received to the syslog before processing it. Be warned that this can + generate quite a lot of data! + +* paritycheck + + Turns parity checking ON or OFF. + + By echoing "0" to this file parity checking will be turned OFF. Any + non-zero value will turn it ON. For hardware version 1 the default is ON. + For version 2 the default it is OFF. + + Hardware version 1 provides basic data integrity verification by + calculating a parity bit for the last 3 bytes of each packet. The driver + can check these bits and reject any packet that appears corrupted. Using + this knob you can bypass that check. + + It is not known yet whether hardware version 2 provides the same parity + bits. Hence checking is disabled by default. Currently even turning it on + will do nothing. + + +///////////////////////////////////////////////////////////////////////////// + + +3. Hardware version 1 + ================== + +3.1 Registers + ~~~~~~~~~ + +By echoing a hexadecimal value to a register it contents can be altered. + +For example: + + echo -n 0x16 > reg_10 + +* reg_10 bit 7 6 5 4 3 2 1 0 B C T D L A S E - E: 1 = enable smart edges in other cases + E: 1 = enable smart edges unconditionally S: 1 = enable smart edges only when dragging A: 1 = absolute mode (needs 4 byte packets, see reg_11) L: 1 = enable drag lock (see reg_22) @@ -30,7 +128,7 @@ Registers C: 1 = enable corner tap B: 1 = swap left and right button -* reg_11 (Windows driver default value 0x8f) +* reg_11 bit 7 6 5 4 3 2 1 0 1 0 0 H V 1 F P @@ -38,68 +136,49 @@ Registers P: 1 = enable parity checking for relative mode F: 1 = enable native 4 byte packet mode V: 1 = enable vertical scroll area - H: 1 = enable horizonal scroll area + H: 1 = enable horizontal scroll area - -* reg_20 (Windows driver default value 0x0a) +* reg_20 single finger width? -* reg_21 (Windows driver default value 0x60) +* reg_21 scroll area width (small: 0x40 ... wide: 0xff) -* reg_22 (Windows driver default value 0xff) +* reg_22 - drag lock time out (short: 0x14 ... long: 0xfe; 0xff =never) + drag lock time out (short: 0x14 ... long: 0xfe; + 0xff = tap again to release) -* reg_23 (Windows driver default value 0x10) +* reg_23 tap make timeout? - Note: the Windows driver does not write this register - -* reg_24 (Windows driver default value 0x10) +* reg_24 tap release timeout? - Note: the Windows driver does not write this register - -* reg_25 (Windows driver default value 0x03) +* reg_25 smart edge cursor speed (0x02 = slow, 0x03 = medium, 0x04 = fast) -* reg_26 (Windows driver default value 0x00 ?? ) +* reg_26 smart edge activation area width? - Note: the Windows driver does not write this register - Note: the Windows driver default value of 0x00 disables smart edges - when it would get written - Note: the Windows driver sets bit 0 of the registry value to disable - tapping when typing, but never actually writes the register. - Only used as an internal driver flag? - - -Initially the Elantouch Touchpad is in emulation mode and reports 3 byte -standard PS/2 packets and hence works with a standard mouse driver. -However, it can be configured to talk its native 4 byte relative mode and 4 -byte absolute mode both for which a dedicated driver is needed. - - -Native 4 byte relative mode packet format -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +3.2 Native relative mode 4 byte packet format + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ byte 0: bit 7 6 5 4 3 2 1 0 c c p2 p1 1 M R L L, R, M = 1 when Left, Right, Middle mouse button pressed - some models have M as byte 3 odd parity - when parity checking is enabled (P = 1): - p1 = byte 1 odd parity - p2 = byte 2 odd parity + some models have M as byte 3 odd parity bit + when parity checking is enabled (reg_11, P = 1): + p1..p2 = byte 1 and 2 odd parity bit c = 1 when corner tap detected byte 1: @@ -116,48 +195,73 @@ byte 2: dy7..dy0 = y movement; positive = up, negative = down byte 3: - bit 7 6 5 4 3 2 1 0 - w h n1 n0 d3 d2 d1 d0 + parity checking enabled (reg_11, P = 1): + + bit 7 6 5 4 3 2 1 0 + w h n1 n0 ds3 ds2 ds1 ds0 - when parity checking is enabled (P = 1): normally: - d3..d0 = scroll wheel amount and direction - positive = down or left - negative = up or right + ds3..ds0 = scroll wheel amount and direction + positive = down or left + negative = up or right when corner tap detected: - d0 = 1 when top right corner tapped - d1 = 1 when bottom right corner tapped - d2 = 1 when bottom left corner tapped - d3 = 1 when top left corner tapped + ds0 = 1 when top right corner tapped + ds1 = 1 when bottom right corner tapped + ds2 = 1 when bottom left corner tapped + ds3 = 1 when top left corner tapped n1..n0 = number of fingers on touchpad - not all models report this but map one, two and three - finger taps directly to L, M and R mouse buttons - w = 1 when wide finger touch? + only models with firmware 2.x report this, models with + firmware 1.x seem to map one, two and three finger taps + directly to L, M and R mouse buttons h = 1 when horizontal scroll action - otherwise (P = 0): - all of byte 3 is vertical scroll amount and direction - negative = up - positive = down + w = 1 when wide finger touch? + + otherwise (reg_11, P = 0): + + bit 7 6 5 4 3 2 1 0 + ds7 ds6 ds5 ds4 ds3 ds2 ds1 ds0 + ds7..ds0 = vertical scroll amount and direction + negative = up + positive = down -Native 4 byte absolute mode packet format -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +3.3 Native absolute mode 4 byte packet format + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ byte 0: - bit 7 6 5 4 3 2 1 0 - D U p1 p2 1 p3 R L + firmware version 1.x: - L, R = 1 when Left, Right mouse button pressed - p1..p3 = parity bit of bytes 1..3 - D, U = 1 when rocker switch pressed Up, Down + bit 7 6 5 4 3 2 1 0 + D U p1 p2 1 p3 R L + + L, R = 1 when Left, Right mouse button pressed + p1..p3 = byte 1..3 odd parity bit + D, U = 1 when rocker switch pressed Up, Down + + firmware version 2.x: + + bit 7 6 5 4 3 2 1 0 + n1 n0 p2 p1 1 p3 R L + + L, R = 1 when Left, Right mouse button pressed + p1..p3 = byte 1..3 odd parity bit + n1..n0 = number of fingers on touchpad byte 1: - bit 7 6 5 4 3 2 1 0 - f 0 th tw x9 x8 y9 y8 + firmware version 1.x: + + bit 7 6 5 4 3 2 1 0 + f 0 th tw x9 x8 y9 y8 + + tw = 1 when two finger touch + th = 1 when three finger touch + f = 1 when finger touch - tw = 1 when two finger touch - th = 1 when three finger touch - f = 1 when finger touch + firmware version 2.x: + + bit 7 6 5 4 3 2 1 0 + . . . . x9 x8 y9 y8 byte 2: bit 7 6 5 4 3 2 1 0 @@ -170,3 +274,132 @@ byte 3: y7 y6 y5 y4 y3 y2 y1 y0 y9..y0 = absolute y value (vertical) + + +///////////////////////////////////////////////////////////////////////////// + + +4. Hardware version 2 + ================== + + +4.1 Registers + ~~~~~~~~~ + +By echoing a hexadecimal value to a register it contents can be altered. + +For example: + + echo -n 0x56 > reg_10 + +* reg_10 + + bit 7 6 5 4 3 2 1 0 + 0 1 0 1 0 1 D 0 + + D: 1 = enable drag and drop + +* reg_11 + + bit 7 6 5 4 3 2 1 0 + 1 0 0 0 S 0 1 0 + + S: 1 = enable vertical scroll + +* reg_21 + + unknown (0x00) + +* reg_22 + + drag and drop release time out (short: 0x70 ... long 0x7e; + 0x7f = never i.e. tap again to release) + + +4.2 Native absolute mode 6 byte packet format + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +4.2.1 One finger touch + ~~~~~~~~~~~~~~~~ + +byte 0: + + bit 7 6 5 4 3 2 1 0 + n1 n0 . . . . R L + + L, R = 1 when Left, Right mouse button pressed + n1..n0 = numbers of fingers on touchpad + +byte 1: + + bit 7 6 5 4 3 2 1 0 + x15 x14 x13 x12 x11 x10 x9 x8 + +byte 2: + + bit 7 6 5 4 3 2 1 0 + x7 x6 x5 x4 x4 x2 x1 x0 + + x15..x0 = absolute x value (horizontal) + +byte 3: + + bit 7 6 5 4 3 2 1 0 + . . . . . . . . + +byte 4: + + bit 7 6 5 4 3 2 1 0 + y15 y14 y13 y12 y11 y10 y8 y8 + +byte 5: + + bit 7 6 5 4 3 2 1 0 + y7 y6 y5 y4 y3 y2 y1 y0 + + y15..y0 = absolute y value (vertical) + + +4.2.2 Two finger touch + ~~~~~~~~~~~~~~~~ + +byte 0: + + bit 7 6 5 4 3 2 1 0 + n1 n0 ay8 ax8 . . R L + + L, R = 1 when Left, Right mouse button pressed + n1..n0 = numbers of fingers on touchpad + +byte 1: + + bit 7 6 5 4 3 2 1 0 + ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 + + ax8..ax0 = first finger absolute x value + +byte 2: + + bit 7 6 5 4 3 2 1 0 + ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 + + ay8..ay0 = first finger absolute y value + +byte 3: + + bit 7 6 5 4 3 2 1 0 + . . by8 bx8 . . . . + +byte 4: + + bit 7 6 5 4 3 2 1 0 + bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 + + bx8..bx0 = second finger absolute x value + +byte 5: + + bit 7 6 5 4 3 2 1 0 + by7 by8 by5 by4 by3 by2 by1 by0 + + by8..by0 = second finger absolute y value diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index ea3701b..9bf887c 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1,7 +1,7 @@ /* - * Elantech Touchpad driver + * Elantech Touchpad driver (v5) * - * Copyright (C) 2007 Arjan Opmeer <arjan@xxxxxxxxxx> + * Copyright (C) 2007-2008 Arjan Opmeer <arjan@xxxxxxxxxx> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -10,53 +10,151 @@ * Trademarks are the property of their respective owners. */ +#include <linux/delay.h> #include <linux/module.h> #include <linux/input.h> #include <linux/serio.h> #include <linux/libps2.h> #include "psmouse.h" -#include "synaptics.h" #include "elantech.h" +/* Quickest way to access etd->debug in the macro below */ +static struct elantech_data *etd; + +#define elantech_debug(format, arg...) \ + do { \ + if (etd->debug) \ + printk(KERN_DEBUG format, ##arg); \ + } while (0) + /* - * Native absolute mode reporting has odd parity check on the last 3 bytes. - * Native relative mode can have odd parity checking on second and third byte, - * or last 3 bytes depending on model. + * Send a Synaptics style sliced query command */ -static unsigned char parity[256]; +static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, + unsigned char *param) +{ + if (psmouse_sliced_command(psmouse, c) || + ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { + pr_err("elantech.c: synaptics_send_cmd query 0x%02x failed.\n", c); + return -1; + } + + return 0; +} /* - * Send a synaptics style special commands + * A retrying version of ps2_command */ -static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) +static int elantech_ps2_command(struct ps2dev *ps2dev, unsigned char *param, + int command) { - if (psmouse_sliced_command(psmouse, c)) + int rc; + int tries = ETP_PS2_COMMAND_TRIES; + + do { + rc = ps2_command(ps2dev, param, command); + if (rc == 0) + break; + tries--; + elantech_debug("elantech.c: retrying ps2 command 0x%02x (%d).\n", + command, tries); + msleep(ETP_PS2_COMMAND_DELAY); + } while (tries > 0); + + if (rc) + pr_err("elantech.c: ps2 command 0x%02x failed.\n", command); + + return rc; +} + +/* + * Send an Elantech style special command to read a value from a register + */ +static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, + unsigned char *val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + int rc = 0; + + if ((reg < 0x10) || (reg > 0x26)) return -1; - if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) + if ((reg > 0x11) && (reg < 0x20)) return -1; - return 0; + + switch (etd->hw_version) { + case 1: + if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || + psmouse_sliced_command(psmouse, reg) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + rc = -1; + } + break; + case 2: + if (elantech_ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(ps2dev, NULL, ETP_REGISTER_READ) || + elantech_ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(ps2dev, NULL, reg) || + elantech_ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + rc = -1; + } + break; + } + + if (rc) + pr_err("elantech.c: failed to read register 0x%02x.\n", reg); + else + *val = param[0]; + + return rc; } /* * Send an Elantech style special command to write a register with a value */ -static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, unsigned char val) +static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, + unsigned char val) { + struct ps2dev *ps2dev = &psmouse->ps2dev; + int rc = 0; + if ((reg < 0x10) || (reg > 0x26)) return -1; if ((reg > 0x11) && (reg < 0x20)) return -1; - if (psmouse_sliced_command(psmouse, ELANTECH_COMMAND_START) || - psmouse_sliced_command(psmouse, reg) || - psmouse_sliced_command(psmouse, val) || - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { - return -1; + switch (etd->hw_version) { + case 1: + if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || + psmouse_sliced_command(psmouse, reg) || + psmouse_sliced_command(psmouse, val) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { + rc = -1; + } + break; + case 2: + if (elantech_ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(ps2dev, NULL, ETP_REGISTER_WRITE) || + elantech_ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(ps2dev, NULL, reg) || + elantech_ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(ps2dev, NULL, val) || + elantech_ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { + rc = -1; + } + break; } - return 0; + if (rc) + pr_err("elantech.c: failed to write register 0x%02x with value 0x%02x.\n", + reg, val); + + return rc; } +/* + * Dump a complete mouse movement packet to the syslog + */ static void elantech_packet_dump(unsigned char *packet, int size) { int i; @@ -68,25 +166,33 @@ static void elantech_packet_dump(unsigned char *packet, int size) } /* - * Report absolute mode input events + * Interpret complete data packets and report absolute mode input events for + * hardware version 1. (4 byte packets) */ -static void elantech_report_absolute(struct psmouse *psmouse) +static void elantech_report_absolute_v1(struct psmouse *psmouse) { - struct elantech_data *etd = psmouse->private; struct input_dev *dev = psmouse->dev; unsigned char *packet = psmouse->packet; int fingers; - /* byte 0: D U p1 p2 1 p3 R L - * byte 1: f 0 th tw x9 x8 y9 y8 - * byte 2: x7 x6 x5 x4 x3 x2 x1 x0 - * byte 3: y7 y6 y5 y4 y3 y2 y1 y0 */ - fingers = ((packet[1] & 0x80) >> 7) + ((packet[1] & 0x30) >> 4); + if (etd->fw_version_maj == 0x01) { + /* byte 0: D U p1 p2 1 p3 R L + byte 1: f 0 th tw x9 x8 y9 y8 */ + fingers = ((packet[1] & 0x80) >> 7) + + ((packet[1] & 0x30) >> 4); + } else { + /* byte 0: n1 n0 p2 p1 1 p3 R L + byte 1: 0 0 0 0 x9 x8 y9 y8 */ + fingers = (packet[0] & 0xc0) >> 6; + } + input_report_key(dev, BTN_TOUCH, fingers != 0); - if (fingers == 1) { + /* byte 2: x7 x6 x5 x4 x3 x2 x1 x0 + byte 3: y7 y6 y5 y4 y3 y2 y1 y0 */ + if (fingers) { input_report_abs(dev, ABS_X, ((packet[1] & 0x0c) << 6) | packet[2]); - input_report_abs(dev, ABS_Y, ETP_YMAX - + input_report_abs(dev, ABS_Y, ETP_YMAX_V1 - (((packet[1] & 0x03) << 8) | packet[3])); } input_report_abs(dev, ABS_PRESSURE, (fingers) ? ETP_DEF_PRESSURE : 0); @@ -95,115 +201,118 @@ static void elantech_report_absolute(struct psmouse *psmouse) input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_LEFT, packet[0] & 0x01); input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); - if (etd->capabilities & ETP_CAP_HAS_ROCKER) { - input_report_key(dev, BTN_FORWARD, packet[0] & 0x40); /* rocker up */ - input_report_key(dev, BTN_BACK, packet[0] & 0x80); /* rocker down */ + if ((etd->fw_version_maj == 0x01) && + (etd->capabilities & ETP_CAP_HAS_ROCKER)) { + /* rocker up */ + input_report_key(dev, BTN_FORWARD, packet[0] & 0x40); + /* rocker down */ + input_report_key(dev, BTN_BACK, packet[0] & 0x80); } } /* - * Report relative mode input events + * Interpret complete data packets and report absolute mode input events for + * hardware version 2. (6 byte packets) */ -static void elantech_report_relative(struct psmouse *psmouse) +static void elantech_report_absolute_v2(struct psmouse *psmouse) { - struct elantech_data *etd = psmouse->private; struct input_dev *dev = psmouse->dev; unsigned char *packet = psmouse->packet; - int fingers, cornertap; + int fingers, x1, y1, x2, y2; - /* byte 0: c c p2 p1 1 M R L - * byte 1: dx7 dx6 dx5 dx4 dx3 dx2 dx1 dx0 - * byte 2: dy7 dy6 dy5 dy4 dy3 dy2 dy1 dy0 - * byte 3: w h n1 n0 d3 d2 d1 d0 */ + /* byte 0: n1 n0 . . . . R L */ + fingers = (packet[0] & 0xc0) >> 6; + input_report_key(dev, BTN_TOUCH, fingers != 0); + switch (fingers) { + case 1: + /* byte 1: x15 x14 x13 x12 x11 x10 x9 x8 + byte 2: x7 x6 x5 x4 x4 x2 x1 x0 */ + input_report_abs(dev, ABS_X, (packet[1] << 8) | packet[2]); + /* byte 4: y15 y14 y13 y12 y11 y10 y8 y8 + byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */ + input_report_abs(dev, ABS_Y, ETP_YMAX_V2 - + ((packet[4] << 8) | packet[5])); + break; + case 2: + /* The coordinate of each finger is reported separately with + a lower resolution for two finger touches */ + /* byte 0: . . ay8 ax8 . . . . + byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 */ + x1 = ((packet[0] & 0x10) << 4) | packet[1]; + /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */ + y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]); + /* byte 3: . . by8 bx8 . . . . + byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 */ + x2 = ((packet[3] & 0x10) << 4) | packet[4]; + /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */ + y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]); + /* For compatibility with the X Synaptics driver scale up one + coordinate and report as ordinary mouse movent */ + input_report_abs(dev, ABS_X, x1 << 2); + input_report_abs(dev, ABS_Y, y1 << 2); + /* For compatibility with the proprietary X Elantech driver + report both coordinates as hat coordinates */ + input_report_abs(dev, ABS_HAT0X, x1); + input_report_abs(dev, ABS_HAT0Y, y1); + input_report_abs(dev, ABS_HAT1X, x2); + input_report_abs(dev, ABS_HAT1Y, y2); + break; + } + input_report_abs(dev, ABS_PRESSURE, (fingers) ? ETP_DEF_PRESSURE : 0); + input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_LEFT, packet[0] & 0x01); input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); - - if (etd->capabilities & ETP_CAP_REPORTS_MIDDLE_BUTTON) - input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04); - - if (etd->capabilities & ETP_CAP_ALTERNATE_TAP_BITS) { - fingers = (packet[3] & 0x30) >> 4; - input_report_key(dev, BTN_LEFT, fingers == 1); - input_report_key(dev, BTN_MIDDLE, fingers == 2); - input_report_key(dev, BTN_RIGHT, fingers == 3); - } - - cornertap = (((packet[0] & 0xc0) == 0xc0) && ((packet[1] & 0xf0) == 0xf0)); - if (!cornertap) { - input_report_rel(dev, REL_X, (int) (packet[1] & 0x7f) - - (int) (packet[1] & 0x80)); - input_report_rel(dev, REL_Y, (int) (packet[2] & 0x80) - - (int) (packet[2] & 0x7f)); - } - - /* No more information in 3 bytes */ - if (!(etd->reg_11 & ETP_R11_4_BYTE_MODE)) - return; - - if (cornertap) { - input_report_key(dev, BTN_0, packet[3] & 0x01); /* top right */ - input_report_key(dev, BTN_1, packet[3] & 0x02); /* bottom right */ - input_report_key(dev, BTN_2, packet[3] & 0x04); /* bottom left */ - input_report_key(dev, BTN_3, packet[3] & 0x08); /* top left */ - } - - if (etd->reg_11 & ETP_R11_PARITY_CHECKING) { - if (packet[3] & 0x0f) { - if (packet[3] & 0x40) { - input_report_rel(dev, REL_HWHEEL, - (int) (packet[3] & 0x08) - - (int) (packet[3] & 0x07)); - } else { - input_report_rel(dev, REL_WHEEL, - (int) (packet[3] & 0x08) - - (int) (packet[3] & 0x07)); - } - } - } else { - if (packet[3]) - input_report_rel(dev, REL_WHEEL, - (int) (packet[3] & 0x80) - - (int) (packet[3] & 0x7f)); - } } /* - * Process byte stream from mouse and interpret complete data packets + * Process byte stream from mouse and handle complete packets */ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) { - struct elantech_data *etd = psmouse->private; struct input_dev *dev = psmouse->dev; unsigned char *packet = psmouse->packet; + unsigned char p1, p2, p3; if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA; - if (etd->debug) + if (etd->debug > 1) elantech_packet_dump(packet, psmouse->pktsize); - if (etd->reg_10 & ETP_R10_ABSOLUTE_MODE) { - /* byte 0: D U p1 p2 1 p3 R L */ - if ((parity[packet[1]] != ((packet[0] & 0x20) >> 5)) || - (parity[packet[2]] != ((packet[0] & 0x10) >> 4)) || - (parity[packet[3]] != ((packet[0] & 0x04) >> 2))) - return PSMOUSE_BAD_DATA; - } else if (etd->reg_11 & ETP_R11_PARITY_CHECKING) { - /* byte 0: c c p2 p1 1 M R L */ - if ((parity[packet[1]] != ((packet[0] & 0x10) >> 4)) || - (parity[packet[2]] != ((packet[0] & 0x20) >> 5))) - return PSMOUSE_BAD_DATA; - /* Parity bit has not been sacrificed as middle mouse button bit */ - if (!(etd->capabilities & ETP_CAP_REPORTS_MIDDLE_BUTTON)) { - if (parity[packet[3]] != ((packet[0] & 0x04) >> 2)) + if (etd->paritycheck) { + switch (etd->hw_version) { + case 1: + /* Parity bits are placed differently */ + if (etd->fw_version_maj == 0x01) { + /* byte 0: D U p1 p2 1 p3 R L */ + p1 = (packet[0] & 0x20) >> 5; + p2 = (packet[0] & 0x10) >> 4; + } else { + /* byte 0: n1 n0 p2 p1 1 p3 R L */ + p1 = (packet[0] & 0x10) >> 4; + p2 = (packet[0] & 0x20) >> 5; + } + p3 = (packet[0] & 0x04) >> 2; + if ((etd->parity[packet[1]] != p1) || + (etd->parity[packet[2]] != p2) || + (etd->parity[packet[3]] != p3)) return PSMOUSE_BAD_DATA; + break; + case 2: + /* Does version 2 hardware have parity bits? */ + break; } } - if (etd->reg_10 & ETP_R10_ABSOLUTE_MODE) { - elantech_report_absolute(psmouse); - } else { - elantech_report_relative(psmouse); + switch (etd->hw_version) { + case 1: + elantech_report_absolute_v1(psmouse); + break; + case 2: + elantech_report_absolute_v2(psmouse); + break; } input_sync(dev); @@ -212,54 +321,69 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) } /* - * Initialise the touchpad to a default state. Because we don't know (yet) - * how to read registers we need to write some default values so we can - * report their contents when asked to. + * Put the touchpad into absolute mode */ -static void elantech_set_defaults(struct psmouse *psmouse) +static int elantech_set_absolute_mode(struct psmouse *psmouse) { - struct elantech_data *etd = psmouse->private; - struct input_dev *dev = psmouse->dev; + int rc = 0; + unsigned char val; + int tries = ETP_READ_BACK_TRIES; + + switch (etd->hw_version) { + case 1: + etd->reg_10 = 0x16; + etd->reg_11 = 0x8f; + if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || + elantech_write_reg(psmouse, 0x11, etd->reg_11)) { + rc = -1; + } + break; + case 2: + /* Windows driver values */ + etd->reg_10 = 0x54; + etd->reg_11 = 0x88; /* 0x8a */ + etd->reg_21 = 0x60; /* 0x00 */ + if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || + elantech_write_reg(psmouse, 0x11, etd->reg_11) || + elantech_write_reg(psmouse, 0x21, etd->reg_21)) { + rc = -1; + break; + } + /* + * Read back reg 0x10. The touchpad is probably initalising + * and not ready until we read back the value we just wrote. + */ + do { + rc = elantech_read_reg(psmouse, 0x10, &val); + if (rc == 0) + break; + tries--; + elantech_debug("elantech.c: retrying read (%d).\n", + tries); + msleep(ETP_READ_BACK_DELAY); + } while (tries > 0); + if (rc) + pr_err("elantech.c: failed to read back register 0x10.\n"); + break; + } - /* - * For now, use the Elantech Windows driver default values - */ - etd->reg_10 = 0x12; - elantech_write_reg(psmouse, 0x10, etd->reg_10); - etd->reg_11 = 0x8f; - elantech_write_reg(psmouse, 0x11, etd->reg_11); - etd->reg_20 = 0x0a; - elantech_write_reg(psmouse, 0x20, etd->reg_20); - etd->reg_21 = 0x60; - elantech_write_reg(psmouse, 0x21, etd->reg_21); - etd->reg_22 = 0xff; - elantech_write_reg(psmouse, 0x22, etd->reg_22); - /* - * However, the Windows driver mentions registers 23, 24 and 26 - * but seems to never actually write them - */ - etd->reg_23 = 0x10; - /* - * elantech_write_reg(psmouse, 0x23, etd->reg_23); - */ - etd->reg_24 = 0x10; - /* - * elantech_write_reg(psmouse, 0x24, etd->reg_24); - */ - etd->reg_25 = 0x03; - elantech_write_reg(psmouse, 0x25, etd->reg_25); - /* - * The Windows driver default value of 0x00 seems wrong as it - * disables smart edge cursor movement - */ - etd->reg_26 = 0x00; - /* - * elantech_write_reg(psmouse, 0x26, etd->reg_26); - */ + if (rc) + pr_err("elantech.c: failed to initialise registers.\n"); + + return rc; +} + +/* + * Set the appropriate event bits for the input subsystem + */ +static void elantech_set_input_params(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; set_bit(EV_KEY, dev->evbit); + set_bit(EV_ABS, dev->evbit); + set_bit(BTN_LEFT, dev->keybit); - set_bit(BTN_MIDDLE, dev->keybit); set_bit(BTN_RIGHT, dev->keybit); set_bit(BTN_TOUCH, dev->keybit); @@ -267,27 +391,26 @@ static void elantech_set_defaults(struct psmouse *psmouse) set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); - /* Rocker button */ - if (etd->capabilities & ETP_CAP_HAS_ROCKER) { - set_bit(BTN_FORWARD, dev->keybit); - set_bit(BTN_BACK, dev->keybit); + switch (etd->hw_version) { + case 1: + /* Rocker button */ + if ((etd->fw_version_maj == 0x01) && + (etd->capabilities & ETP_CAP_HAS_ROCKER)) { + set_bit(BTN_FORWARD, dev->keybit); + set_bit(BTN_BACK, dev->keybit); + } + input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, ETP_XMAX_V1, 0, 0); + input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, ETP_YMAX_V1, 0, 0); + break; + case 2: + input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0); + input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0); + input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0); + input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0); } - /* Corner taps */ - set_bit(BTN_0, dev->keybit); - set_bit(BTN_1, dev->keybit); - set_bit(BTN_2, dev->keybit); - set_bit(BTN_3, dev->keybit); - - set_bit(EV_REL, dev->evbit); - set_bit(REL_X, dev->relbit); - set_bit(REL_Y, dev->relbit); - set_bit(REL_WHEEL, dev->relbit); - set_bit(REL_HWHEEL, dev->relbit); - - set_bit(EV_ABS, dev->evbit); - input_set_abs_params(dev, ABS_X, ETP_XMIN, ETP_XMAX, 0, 0); - input_set_abs_params(dev, ABS_Y, ETP_YMIN, ETP_YMAX, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0); } @@ -299,14 +422,19 @@ struct elantech_attr_data { /* * Display a register value by reading a sysfs entry */ -static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data, char *buf) +static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data, + char *buf) { struct elantech_data *etd = psmouse->private; struct elantech_attr_data *attr = data; unsigned char *reg = (unsigned char *) etd + attr->field_offset; + int rc = 0; + + if (attr->reg) + rc = elantech_read_reg(psmouse, attr->reg, reg); - return sprintf(buf, "0x%02x\n", *reg); + return sprintf(buf, "0x%02x\n", (attr->reg && rc) ? -1 : *reg); } /* @@ -315,40 +443,34 @@ static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data, char static ssize_t elantech_set_int_attr(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - struct elantech_data *etd = psmouse->private; - struct elantech_attr_data *attr = data; + struct elantech_attr_data *attr = data; unsigned char *reg = (unsigned char *) etd + attr->field_offset; unsigned long value; - char *rest; + int err; - value = simple_strtoul(buf, &rest, 16); - if (*rest || value > 255) + err = strict_strtoul(buf, 16, &value); + if (err) + return err; + if (value > 0xff) return -EINVAL; - if (attr->reg == 0x10) { - /* Force on 4 byte mode when absolute mode gets selected */ - if ((value & ETP_R10_ABSOLUTE_MODE) && - !(etd->reg_11 & ETP_R11_4_BYTE_MODE)) { - etd->reg_11 |= ETP_R11_4_BYTE_MODE; - elantech_write_reg(psmouse, 0x11, etd->reg_11); - psmouse->pktsize = 4; - } - } else if (attr->reg == 0x11) { - if (value & ETP_R11_4_BYTE_MODE) - psmouse->pktsize = 4; - else { - /* Force off absolute mode when 4 byte mode is no longer selected */ - if (etd->reg_10 & ETP_R10_ABSOLUTE_MODE) { - etd->reg_10 ^= ETP_R10_ABSOLUTE_MODE; - elantech_write_reg(psmouse, 0x10, etd->reg_10); - } - psmouse->pktsize = 3; - } + /* Do we need to preserve some bits for version 2 hardware too? */ + if (etd->hw_version == 1) { + if (attr->reg == 0x10) + /* Force absolute mode always on */ + value |= ETP_R10_ABSOLUTE_MODE; + else if (attr->reg == 0x11) + /* Force 4 byte mode always on */ + value |= ETP_R11_4_BYTE_MODE; } - *reg = value; - elantech_write_reg(psmouse, attr->reg, value); + if (attr->reg) { + if (elantech_write_reg(psmouse, attr->reg, value) == 0) + *reg = value; + } else { + *reg = value; + } return count; } @@ -373,6 +495,7 @@ ELANTECH_INT_ATTR(reg_24, 0x24); ELANTECH_INT_ATTR(reg_25, 0x25); ELANTECH_INT_ATTR(reg_26, 0x26); ELANTECH_INT_ATTR(debug, 0); +ELANTECH_INT_ATTR(paritycheck, 0); static struct attribute *elantech_attrs[] = { &psmouse_attr_reg_10.dattr.attr, @@ -385,6 +508,7 @@ static struct attribute *elantech_attrs[] = { &psmouse_attr_reg_25.dattr.attr, &psmouse_attr_reg_26.dattr.attr, &psmouse_attr_debug.dattr.attr, + &psmouse_attr_paritycheck.dattr.attr, NULL }; @@ -393,17 +517,6 @@ static struct attribute_group elantech_attr_group = { }; /* - * Clean up sysfs entries when disconnecting - */ -static void elantech_disconnect(struct psmouse *psmouse) -{ - sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, - &elantech_attr_group); - kfree(psmouse->private); - psmouse->private = NULL; -} - -/* * Use magic knock to detect Elantech touchpad */ int elantech_detect(struct psmouse *psmouse, int set_properties) @@ -411,12 +524,19 @@ int elantech_detect(struct psmouse *psmouse, int set_properties) struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[3]; - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + pr_err("elantech.c: sending Elantech magic knock failed.\n"); + return -1; + } + /* + * Report this in case there are Elantech models that use a different + * set of magic numbers + */ if ((param[0] != 0x3c) || (param[1] != 0x03) || (param[2] != 0xc8)) { pr_info("elantech.c: unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", param[0], param[1], param[2]); @@ -432,54 +552,111 @@ int elantech_detect(struct psmouse *psmouse, int set_properties) } /* + * Clean up sysfs entries when disconnecting + */ +static void elantech_disconnect(struct psmouse *psmouse) +{ + sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, + &elantech_attr_group); + kfree(psmouse->private); + psmouse->private = NULL; +} + +/* + * Put the touchpad back into absolute mode when reconnecting + */ +static int elantech_reconnect(struct psmouse *psmouse) +{ + if (elantech_detect(psmouse, 0)) + return -1; + + if (elantech_set_absolute_mode(psmouse)) { + pr_err("elantech.c: failed to put touchpad back into absolute mode.\n"); + return -1; + } + + return 0; +} + +/* * Initialize the touchpad and create sysfs entries */ int elantech_init(struct psmouse *psmouse) { - struct elantech_data *etd; - int i, error; - unsigned char param[3]; + int i, error; + unsigned char param[3]; etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); psmouse->private = etd; if (!etd) return -1; - parity[0] = 1; + etd->parity[0] = 1; for (i = 1; i < 256; i++) - parity[i] = (parity[i & (i - 1)] ^ 1); + etd->parity[i] = (etd->parity[i & (i - 1)] ^ 1); /* - * Why does the Elantech Windows driver try this? - * For now just report it and see if it makes sense - * when more people use this driver + * Find out what version hardware this is */ - if (!synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, param)) - pr_info("elantech.c: Synaptics identify query result 0x%02x, 0x%02x, 0x%02x.\n", - param[0], param[1], param[2]); - if (!synaptics_send_cmd(psmouse, SYN_QUE_MODES, param)) - pr_info("elantech.c: Synaptics modes query result 0x%02x, 0x%02x, 0x%02x.\n", - param[0], param[1], param[2]); - if (!synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, param)) { - pr_info("elantech.c: Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", - param[0], param[1], param[2]); - etd->capabilities = param[0]; + if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { + pr_err("elantech.c: failed to query firmware version.\n"); + goto init_fail; } + pr_info("elantech.c: Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", + param[0], param[1], param[2]); + etd->fw_version_maj = param[0]; + etd->fw_version_min = param[2]; - elantech_set_defaults(psmouse); + /* + * Assume every version greater than this is new EeePC style + * hardware with 6 byte packets + */ + if ((etd->fw_version_maj >= 0x02) && (etd->fw_version_min >= 0x30)) { + etd->hw_version = 2; + /* For now show extra debug information */ + etd->debug = 1; + /* Don't know how to do parity checking for version 2 */ + etd->paritycheck = 0; + } else { + etd->hw_version = 1; + etd->paritycheck = 1; + } + pr_info("elantech.c: assuming hardware version %d, firmware version %d.%d\n", + etd->hw_version, etd->fw_version_maj, etd->fw_version_min); - psmouse->protocol_handler = elantech_process_byte; - psmouse->disconnect = elantech_disconnect; - psmouse->pktsize = 4; + if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) { + pr_err("elantech.c: failed to query capabilities.\n"); + goto init_fail; + } + pr_info("elantech.c: Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", + param[0], param[1], param[2]); + etd->capabilities = param[0]; + + if (elantech_set_absolute_mode(psmouse)) { + pr_err("elantech.c: failed to put touchpad into absolute mode.\n"); + goto init_fail; + } + elantech_set_input_params(psmouse); error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, &elantech_attr_group); if (error) { - printk(KERN_ERR "elantech.c: failed to create sysfs attributes, error: %d\n", + pr_err("elantech.c: failed to create sysfs attributes, error: %d.\n", error); - kfree(etd); - return -1; + goto init_fail; } + psmouse->protocol_handler = elantech_process_byte; + psmouse->disconnect = elantech_disconnect; + psmouse->reconnect = elantech_reconnect; + if (etd->hw_version == 2) + psmouse->pktsize = 6; + else + psmouse->pktsize = 4; + return 0; + +init_fail: + kfree(etd); + return -1; } diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index e5a6157..3fb996e 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -1,7 +1,7 @@ /* - * Elantech Touchpad driver + * Elantech Touchpad driver (v5) * - * Copyright (C) 2007 Arjan Opmeer <arjan@xxxxxxxxxx> + * Copyright (C) 2007-2008 Arjan Opmeer <arjan@xxxxxxxxxx> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -14,42 +14,86 @@ #define _ELANTECH_H /* - * Commands start with this value + * Command values for Synaptics style queries */ -#define ELANTECH_COMMAND_START 0x11 +#define ETP_FW_VERSION_QUERY 0x01 +#define ETP_CAPABILITIES_QUERY 0x02 /* - * Register bitmasks + * Command values for register reading or writing + */ +#define ETP_REGISTER_READ 0x10 +#define ETP_REGISTER_WRITE 0x11 + +/* + * Hardware version 2 custom PS/2 command value + */ +#define ETP_PS2_CUSTOM_COMMAND 0xf8 + +/* + * Times to retry a ps2_command and millisecond delay between tries + */ +#define ETP_PS2_COMMAND_TRIES 3 +#define ETP_PS2_COMMAND_DELAY 500 + +/* + * Times to try to read back a register and millisecond delay between tries + */ +#define ETP_READ_BACK_TRIES 5 +#define ETP_READ_BACK_DELAY 2000 + +/* + * Register bitmasks for hardware version 1 */ #define ETP_R10_ABSOLUTE_MODE 0x04 -#define ETP_R11_PARITY_CHECKING 0x01 #define ETP_R11_4_BYTE_MODE 0x02 /* * Capability bitmasks */ -#define ETP_CAP_REPORTS_MIDDLE_BUTTON 0x02 #define ETP_CAP_HAS_ROCKER 0x04 -#define ETP_CAP_ALTERNATE_TAP_BITS 0x10 /* * One hard to find application note states that X axis range is 0 to 576 - * and Y axis range is 0 to 384. - * Edge fuzz might be necessary because of bezel around the touchpad. + * and Y axis range is 0 to 384 for harware version 1. + * Edge fuzz might be necessary because of bezel around the touchpad + */ +#define ETP_EDGE_FUZZ_V1 32 + +#define ETP_XMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) +#define ETP_XMAX_V1 (576 - ETP_EDGE_FUZZ_V1) +#define ETP_YMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) +#define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1) + +/* + * It seems the resolution for hardware version 2 doubled. + * Hence the X and Y ranges are doubled too. + * The bezel around the pad also appears to be smaller + */ +#define ETP_EDGE_FUZZ_V2 8 + +#define ETP_XMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2) +#define ETP_XMAX_V2 (1152 - ETP_EDGE_FUZZ_V2) +#define ETP_YMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2) +#define ETP_YMAX_V2 ( 768 - ETP_EDGE_FUZZ_V2) + +/* + * For two finger touches the coordinate of each finger gets reported + * separately but with reduced resolution. */ -#define ETP_EDGE_FUZZ 32 +#define ETP_2FT_FUZZ 4 -#define ETP_XMIN ( 0 + ETP_EDGE_FUZZ) -#define ETP_XMAX (576 - ETP_EDGE_FUZZ) -#define ETP_YMIN ( 0 + ETP_EDGE_FUZZ) -#define ETP_YMAX (384 - ETP_EDGE_FUZZ) +#define ETP_2FT_XMIN ( 0 + ETP_2FT_FUZZ) +#define ETP_2FT_XMAX (288 - ETP_2FT_FUZZ) +#define ETP_2FT_YMIN ( 0 + ETP_2FT_FUZZ) +#define ETP_2FT_YMAX (192 - ETP_2FT_FUZZ) /* * It seems the touchpad does not report pressure. * Just choose some values for compatibility with X Synaptics driver */ -#define ETP_MAX_PRESSURE 127 -#define ETP_DEF_PRESSURE 64 +#define ETP_MAX_PRESSURE 255 +#define ETP_DEF_PRESSURE 128 struct elantech_data { unsigned char reg_10; @@ -63,6 +107,11 @@ struct elantech_data { unsigned char reg_26; unsigned char debug; unsigned char capabilities; + unsigned char fw_version_maj; + unsigned char fw_version_min; + unsigned char hw_version; + unsigned char paritycheck; + unsigned char parity[256]; }; #ifdef CONFIG_MOUSE_PS2_ELANTECH diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index d42ff6f..300d960 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -770,6 +770,12 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = touchkit_ps2_detect, }, #endif + { + .type = PSMOUSE_CORTRON, + .name = "CortronPS/2", + .alias = "cortps", + .detect = cortron_detect, + }, #ifdef CONFIG_MOUSE_PS2_ELANTECH { .type = PSMOUSE_ELANTECH, @@ -780,12 +786,6 @@ static const struct psmouse_protocol psmouse_protocols[] = { }, #endif { - .type = PSMOUSE_CORTRON, - .name = "CortronPS/2", - .alias = "cortps", - .detect = cortron_detect, - }, - { .type = PSMOUSE_AUTO, .name = "auto", .alias = "any", -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html