--- linux-source-2.6.38/drivers/input/mouse/elantech.c_orig 2011-05-02 10:33:35.367381638 -0600 +++ linux-source-2.6.38/drivers/input/mouse/elantech.c 2011-05-24 14:22:16.000000000 -0600 @@ -1,5 +1,9 @@ /* * Elantech Touchpad driver (v6) + * + * + * 2011 Tom Lin + * Added new ic of version * * Copyright (C) 2007-2009 Arjan Opmeer * @@ -10,7 +14,6 @@ * Trademarks are the property of their respective owners. */ -#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt #include #include @@ -18,18 +21,24 @@ #include #include #include +#include #include "psmouse.h" #include "elantech.h" -#define elantech_debug(fmt, ...) \ - do { \ - if (etd->debug) \ - printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \ +#define elantech_debug(format, arg...) \ + do { \ + if (etd->debug) \ + printk(KERN_DEBUG format, ##arg); \ } while (0) -static bool force_elantech; -module_param_named(force_elantech, force_elantech, bool, 0644); -MODULE_PARM_DESC(force_elantech, "Force the Elantech PS/2 protocol extension to be used, 1 = enabled, 0 = disabled (default)."); +static int EF_023_DEBUG; +static int Public_ETP_XMAX_V2; +static int Public_ETP_YMAX_V2; +static int Public_ETP_2FT_YMAX; + +unsigned int elantech_distance_squared(elantech_point p1, elantech_point p2) { + return (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y); +} /* * Send a Synaptics style sliced query command @@ -37,9 +46,10 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) { + struct elantech_data *etd = psmouse->private; if (psmouse_sliced_command(psmouse, c) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { - pr_err("synaptics_send_cmd query 0x%02x failed.\n", c); + elantech_debug("elantech.c: synaptics_send_cmd query 0x%02x failed.\n", c); return -1; } @@ -62,13 +72,13 @@ if (rc == 0) break; tries--; - elantech_debug("retrying ps2 command 0x%02x (%d).\n", - command, 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("ps2 command 0x%02x failed.\n", command); + elantech_debug("elantech.c: ps2 command 0x%02x failed.\n", command); return rc; } @@ -76,21 +86,65 @@ /* * Send an Elantech style special command to read a value from a register */ +static int elantech_sliced_command(struct psmouse *psmouse, unsigned char reg, + unsigned char val) +{ + struct elantech_data *etd = psmouse->private; + unsigned char param[3]; + int rc = 0; + int register_read; + int register_write; + + if (etd->hw_version == ETF5900){ + register_write = ETP_REGISTER_RW; + register_read = ETP_REGISTER_RW; + }else{ + register_write = ETP_REGISTER_WRITE; + register_read = ETP_REGISTER_READ; + } + + if(psmouse_sliced_command(psmouse, register_write)|| + psmouse_sliced_command(psmouse,reg) || + psmouse_sliced_command(psmouse,val) || + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) + rc = -1; + + if(psmouse_sliced_command(psmouse, register_read) || + psmouse_sliced_command(psmouse, reg) || + ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) + rc = -1; + + if(val != param[0]) + rc = -1; + + return rc; + +} + static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, unsigned char *val) { struct elantech_data *etd = psmouse->private; unsigned char param[3]; int rc = 0; + int register_read; if (reg < 0x10 || reg > 0x26) return -1; if (reg > 0x11 && reg < 0x20) return -1; + + + if ( etd->hw_version == ETF5900) + register_read = ETP_REGISTER_RW; + else + register_read = ETP_REGISTER_WRITE; + switch (etd->hw_version) { - case 1: + case ETF020022: + case ETF020600: if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || psmouse_sliced_command(psmouse, reg) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { @@ -98,9 +152,16 @@ } break; - case 2: + case ETF020030: + case ETF0208: + case ETF020B00: + case ETF0402: + case ETF0401: + case ETF0403: + case ETF1400: + case ETF5900: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || + elantech_ps2_command(psmouse, NULL, register_read) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { @@ -109,8 +170,15 @@ break; } + if (rc == -1){ + if(!psmouse_sliced_command(psmouse, register_read) && + !psmouse_sliced_command(psmouse, reg) && + !ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) + rc = 0; + } + if (rc) - pr_err("failed to read register 0x%02x.\n", reg); + elantech_debug("elantech.c: failed to read register 0x%02x.\n", reg); else *val = param[0]; @@ -125,15 +193,24 @@ { struct elantech_data *etd = psmouse->private; int rc = 0; + int register_write; - if (reg < 0x10 || reg > 0x26) - return -1; + if(EF_023_DEBUG == 0) { + if (reg < 0x10 || reg > 0x26) + return -1; - if (reg > 0x11 && reg < 0x20) - return -1; + if (reg > 0x11 && reg < 0x20) + return -1; + } + + if ( etd->hw_version == ETF5900) + register_write = ETP_REGISTER_RW; + else + register_write = ETP_REGISTER_WRITE; switch (etd->hw_version) { - case 1: + case ETF020022: + case ETF020600: if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || psmouse_sliced_command(psmouse, reg) || psmouse_sliced_command(psmouse, val) || @@ -142,7 +219,13 @@ } break; - case 2: + case ETF020030: + case ETF0208: + case ETF020B00: + case ETF0402: + case ETF0401: + case ETF0403: + case ETF1400: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || @@ -153,28 +236,130 @@ rc = -1; } break; + case ETF5900: + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_RW) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, reg) || + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command(psmouse, NULL, val) || + elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { + rc = -1; + } + break; + } + + if (rc == -1 && etd->hw_version > ETF020600 ){ + + if(!psmouse_sliced_command(psmouse, register_write)&& + !psmouse_sliced_command(psmouse,reg) && + !psmouse_sliced_command(psmouse,val) && + !ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) + rc = 0; } if (rc) - pr_err("failed to write register 0x%02x with value 0x%02x.\n", + elantech_debug("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) + +static int ETF0208_DEBUG_SOLUTION(int fingers,int button,int w) { - int i; + static int can_bug=0,debug_condition_1=0,count_report=0,debug_condition_3=0,debug_condition_5=0; + static int solution=0; + + if(solution==3) + return 0; + + if(fingers >=2 || (fingers == 1 && button > 0)) + can_bug=1; + + + if(can_bug && button > 0) + debug_condition_1=1; + + if(fingers <= 1 && can_bug && button==0) + can_bug=0; + + if(debug_condition_1&& fingers ==0 && button > 0){ + count_report++; + //printk(KERN_DEBUG "2 count_report=%d \n", count_report); + if(count_report==4){ + debug_condition_1=0; + EF_023_DEBUG=2; + solution=2; + return 1; + + } - printk(KERN_DEBUG pr_fmt("PS/2 packet [")); - for (i = 0; i < size; i++) - printk("%s0x%02x ", (i) ? ", " : " ", packet[i]); - printk("]\n"); + } + else if(debug_condition_1 && fingers == 1 && button > 0){ + count_report=0; + } + else if(debug_condition_1 && count_report > 0 && count_report < 4 && button == 0){ + EF_023_DEBUG=1; + solution=1; + debug_condition_1=0; + count_report=0; + return 1; + } + else if(fingers ==0 && button == 0){ + count_report=0; + debug_condition_1=0; + } + + if(debug_condition_5==1 &&fingers ==1 && button ==0){ + EF_023_DEBUG=4; + debug_condition_5=2; + return 1; + }else if(debug_condition_5==2){ + if(fingers ==0 && button > 0){ + EF_023_DEBUG=5; + debug_condition_5=3; + }else{ + EF_023_DEBUG=5; + debug_condition_5=5; + } + return 1; + } + else if(debug_condition_5==3 && fingers < 2 && button==0){ + debug_condition_5=4; + } + else if(debug_condition_5 ==4 && fingers ==1 && button==0 && w !=4){ + solution=3; + } + else if(debug_condition_5==5){ + EF_023_DEBUG=1; + debug_condition_5=0; + return 1; + }else if (debug_condition_5 !=0 ){ + if((w ==4 && fingers==1)||(fingers > 1 && debug_condition_5 >= 3)){ + EF_023_DEBUG=1; + debug_condition_5=0; + return 1; + } + debug_condition_5=0; + } + else if(debug_condition_5==0 && button ==0){ + debug_condition_5=1; + } + + if(fingers ==1 && button ==0){ + debug_condition_3=1; + } + else if(debug_condition_3==1 && fingers ==0 && button ==1){ + solution = 3; + }else { + debug_condition_3=0; + } + + return 0; } + /* * Interpret complete data packets and report absolute mode input events for * hardware version 1. (4 byte packets) @@ -186,43 +371,26 @@ unsigned char *packet = psmouse->packet; int fingers; - if (etd->fw_version < 0x020000) { - /* - * byte 0: D U p1 p2 1 p3 R L - * byte 1: f 0 th tw x9 x8 y9 y8 - */ + 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 - */ + /* 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; } - if (etd->jumpy_cursor) { - if (fingers != 1) { - etd->single_finger_reports = 0; - } else if (etd->single_finger_reports < 2) { - /* Discard first 2 reports of one finger, bogus */ - etd->single_finger_reports++; - elantech_debug("discarding packet\n"); - return; - } - } - input_report_key(dev, BTN_TOUCH, fingers != 0); - /* - * byte 2: x7 x6 x5 x4 x3 x2 x1 x0 - * byte 3: y7 y6 y5 y4 y3 y2 y1 y0 - */ + /* 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_V1 - (((packet[1] & 0x03) << 8) | packet[3])); + input_report_abs(dev, ABS_Y, ETP_YMAX_V1 - + (((packet[1] & 0x03) << 8) | packet[3])); } input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); @@ -231,13 +399,6 @@ input_report_key(dev, BTN_LEFT, packet[0] & 0x01); input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); - if (etd->fw_version < 0x020000 && - (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); - } input_sync(dev); } @@ -249,6 +410,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; + struct elantech_data *etd = psmouse->private; unsigned char *packet = psmouse->packet; int fingers, x1, y1, x2, y2; @@ -257,130 +419,449 @@ input_report_key(dev, BTN_TOUCH, fingers != 0); switch (fingers) { - case 3: - /* - * Same as one finger, except report of more than 3 fingers: - * byte 3: n4 . w1 w0 . . . . - */ - if (packet[3] & 0x80) - fingers = 4; - /* pass through... */ + case 0: + input_report_abs(dev,ABS_PRESSURE,0); + break; case 1: - /* - * byte 1: . . . . . x10 x9 x8 - * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 - */ - input_report_abs(dev, ABS_X, - ((packet[1] & 0x07) << 8) | packet[2]); - /* - * byte 4: . . . . . . y9 y8 - * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 - */ - input_report_abs(dev, ABS_Y, - ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5])); + /* 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] & 0x0f)<< 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, Public_ETP_YMAX_V2 - + (((packet[4] & 0x0f) << 8) | packet[5])); + if(etd->hw_version == ETF020030) { + input_report_abs(dev,ABS_PRESSURE,60); + } + else { + input_report_abs(dev,ABS_PRESSURE, + ((packet[1]&0xf0)|((packet[4]&0xf0)>>4))); + input_report_abs(dev,ABS_TOOL_WIDTH, + ((((packet[0]&0x30)>>2)|((packet[3]&0x30)>>4)) & 0x0f)); + } + 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 - */ + /* 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 - */ + y1 = Public_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 - */ + y2 = Public_ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]); + + /* 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); + input_report_abs(dev,ABS_PRESSURE,60); break; + case 3: + /* 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_HAT2X, ((packet[1] & 0x0f)<< 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_HAT2Y, Public_ETP_YMAX_V2 - + (((packet[4] & 0x0f) << 8) | packet[5])); + + if(etd->hw_version == ETF020030) { + input_report_abs(dev,ABS_PRESSURE,60); + } + else { + input_report_abs(dev,ABS_PRESSURE, + ((packet[1]&0xf0)|((packet[4]&0xf0)>>4))); + } + + break; } 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_TOOL_QUADTAP, fingers == 4); - input_report_key(dev, BTN_LEFT, packet[0] & 0x01); - input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + input_report_key(dev, BTN_LEFT, ((packet[0] & 0x03) == 1)); + input_report_key(dev, BTN_RIGHT,((packet[0] & 0x03) == 2)); + input_report_key(dev, BTN_MIDDLE,((packet[0] & 0x03) == 3)); + + switch (etd->hw_version) { + case ETF0208: + if(etd->fw_version_min_sp != 0x00) + break; + case ETF0403: + case ETF1400: + input_report_key(dev, BTN_1, packet[3] & 0x80); /*MKY*/ + input_report_key(dev, BTN_2, packet[3] & 0x40); /*VF*/ + break; + } input_sync(dev); } -static int elantech_check_parity_v1(struct psmouse *psmouse) +static void elantech_report_absolute_v3(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; - /* Parity bits are placed differently */ - if (etd->fw_version < 0x020000) { - /* 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; + elantech_point tmp_p; + /* The x,y values in the packet during this call. */ + elantech_point p; + /* x,y values specially used when it takes two calls of + this function to get both finger positions. */ + static elantech_point p1 = {-1,-1,0,0}; + /* The point (and p1 for two finger touches) that was reported last. */ + static elantech_point last_p={-1,-1,0,0}, last_p1={-1,-1,0,0}; + + /* byte 0: n1 n0 . . . . R L */ + p.fingers = (packet[0] & 0xc0) >> 6; + + p.button = packet[0] & 0x03; + + /* byte 1: x15 x14 x13 x12 x11 x10 x9 x8 + byte 2: x7 x6 x5 x4 x4 x2 x1 x0 */ + p.x = ((packet[1] & 0x0f) << 8) | packet[2]; + /* byte 4: y15 y14 y13 y12 y11 y10 y9 y8 + byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */ + p.y = Public_ETP_YMAX_V2 - (((packet[4] & 0x0f) << 8) | packet[5]); + + switch (p.fingers) { + case 0: + input_report_abs(dev,ABS_PRESSURE,0); + break; + case 1: + input_report_abs(dev, ABS_X, p.x); + input_report_abs(dev, ABS_Y, p.y); + input_report_abs(dev,ABS_PRESSURE, ((packet[1]&0xf0)|((packet[4]&0xf0)>>4))); + input_report_abs(dev,ABS_TOOL_WIDTH, ((((packet[0]&0x30)>>2)|((packet[3]&0x30)>>4)) & 0x0f)); + break; + + case 2: + if (((packet[0] & 0x0C) == 0x04) && // check Two finger Mode 1st finger report + ((packet[3] & 0x0f) == 0x02)){ + /* We don't want to set last_p or do + any further processing here. We'll wait for the next + packet with the other finger data. */ + if (p1.x == -1) { + // This is only necessary the first time. to avoid a jump + last_p1 = last_p; + } else { + last_p1 = p1; + } + p1 = p; + return; + } else if (p1.x == -1 || p1.y == -1) { + return; + } + + /* There's no reliable way to figure out which finger is which from the data. */ + /* It appears to be based on position on the trackpad. */ + /* So we keep track of the two positions present when we last reported. */ + + /* There is probably a better way to do this: */ + /* If p is now closer to last_p1 than p1 is, + and p1 is closer to last_p than p is, + there's a pretty good chance they got flipped and now correspond + to different fingers than they did last time we reported. */ + /* TODO: When this is wrong, it results in wobbly cursor movement. */ + if (elantech_distance_squared(p,last_p1) < elantech_distance_squared(p1,last_p1) + && elantech_distance_squared(p1, last_p) < elantech_distance_squared(p, last_p)) { + tmp_p = p; + p = p1; + p1 = tmp_p; + } + + input_report_abs(dev, ABS_X, p.x); + input_report_abs(dev, ABS_Y, p.y); + + input_report_abs(dev,ABS_PRESSURE, + ((packet[1]&0xf0)|((packet[4]&0xf0)>>4))); + input_report_abs(dev,ABS_TOOL_WIDTH, + ((((packet[0]&0x30)>>2)|((packet[3]&0x30)>>4)) & 0x0f)); + break; + case 3: + input_report_abs(dev, ABS_HAT2X, p.x); + input_report_abs(dev, ABS_HAT2Y, p.y); + input_report_abs(dev,ABS_PRESSURE, + ((packet[1]&0xf0)|((packet[4]&0xf0)>>4))); + input_report_abs(dev,ABS_TOOL_WIDTH, + ((((packet[0]&0x30)>>2)|((packet[3]&0x30)>>4)) & 0x0f)); + break; } - p3 = (packet[0] & 0x04) >> 2; + /* If we're not dealing with two finger touches, make the last_p1 + packet look like whatever we're doing now, to avoid + cursor jumping later if they tuoch a second finger. */ + if (p.fingers != 2) { + last_p1 = p; + } + last_p = p; + + input_report_key(dev, BTN_TOUCH, p.fingers != 0); + input_report_key(dev, BTN_TOOL_FINGER, p.fingers == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, p.fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, p.fingers == 3); + input_report_key(dev, BTN_LEFT, (p.button == 1)); + input_report_key(dev, BTN_RIGHT,(p.button == 2)); + input_report_key(dev, BTN_MIDDLE,(p.button == 3)); - return etd->parity[packet[1]] == p1 && - etd->parity[packet[2]] == p2 && - etd->parity[packet[3]] == p3; + input_sync(dev); } -/* - * 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; + unsigned char *packet = psmouse->packet; + unsigned char bit7,bit6,bit5,bit4,bit3,bit2,bit1,bit0; + unsigned char SA,B,C,D; + static char SA_O=0,B_O=0,C_O=0,D_O=0; + unsigned char SA1,A1,B1,SB1,C1,D1; + static char SA1_O=0,A1_O=0,B1_O=0,SB1_O=0,C1_O=0,D1_O=0; + unsigned int fingers,button,w1; + int C_B,C_C,C_D,Debug; if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA; + + switch (etd->hw_version) { + case ETF020022: + case ETF020600: + + SA = packet[0]; + B = packet[1]; + C = packet[2]; + D = packet[3]; + if(SA == SA_O && B == B_O && C == C_O && D == D_O) + goto the_same_data; + + if(!(packet[0] & 0x08)) //chech SA byte + goto shift_4byte_data; + + bit7 = (packet[1] & 0x80) >> 7; + bit6 = (packet[1] & 0x40) >> 6; + bit5 = (packet[1] & 0x20) >> 5; + bit4 = (packet[1] & 0x10) >> 4; + bit3 = (packet[1] & 0x08) >> 3; + bit2 = (packet[1] & 0x04) >> 2; + bit1 = (packet[1] & 0x02) >> 1; + bit0 = (packet[1] & 0x01) ; + C_B = (bit7 + bit6 + bit5 + bit4 + bit3 + bit2 + bit1 + bit0)%2; + + if((packet[0]&0x10) && (C_B == 1)) //chech B byte + goto shift_4byte_data; + else if(!(packet[0]&0x10) && (C_B == 0)) + goto shift_4byte_data; + + bit7 = (packet[2] & 0x80) >> 7; + bit6 = (packet[2] & 0x40) >> 6; + bit5 = (packet[2] & 0x20) >> 5; + bit4 = (packet[2] & 0x10) >> 4; + bit3 = (packet[2] & 0x08) >> 3; + bit2 = (packet[2] & 0x04) >> 2; + bit1 = (packet[2] & 0x02) >> 1; + bit0 = (packet[2] & 0x01) ; + C_C = (bit7 + bit6 + bit5 + bit4 + bit3 + bit2 + bit1 + bit0)%2; + + if((packet[0]&0x20) && (C_C == 1)) //chech C byte + goto shift_4byte_data; + else if(!(packet[0]&0x20) && (C_C == 0)) + goto shift_4byte_data; + + + bit7 = (packet[3] & 0x80) >> 7; + bit6 = (packet[3] & 0x40) >> 6; + bit5 = (packet[3] & 0x20) >> 5; + bit4 = (packet[3] & 0x10) >> 4; + bit3 = (packet[3] & 0x08) >> 3; + bit2 = (packet[3] & 0x04) >> 2; + bit1 = (packet[3] & 0x02) >> 1; + bit0 = (packet[3] & 0x01) ; + C_D = (bit7 + bit6 + bit5 + bit4 + bit3 + bit2 + bit1 + bit0)%2; + + if((packet[0]&0x04) && (C_D == 1)) //chech D byte + goto shift_4byte_data; + else if(!(packet[0]&0x04) && (C_D == 0)) + goto shift_4byte_data; + elantech_report_absolute_v1(psmouse); + SA_O = SA; + B_O = B; + C_O = C; + D_O = D; + break; - if (etd->debug > 1) - elantech_packet_dump(psmouse->packet, psmouse->pktsize); + case ETF020030: + SA1= packet[0]; + A1 = packet[1]; + B1 = packet[2]; + SB1= packet[3]; + C1 = packet[4]; + D1 = packet[5]; + + if(SA1 == SA1_O && A1 == A1_O && B1 == B1_O && + SB1 == SB1_O && C1 == C1_O && D1 == D1_O) { + goto the_same_data; + } + + if( (((SA1 & 0x3C) != 0x3C) && ((SA1 & 0xC0) != 0x80)) || // check Byte 1 + (((SA1 & 0x0C) != 0x0C) && ((SA1 & 0xC0) == 0x80)) || // check Byte 1 + (((SA1 & 0xC0) != 0x80) && (( A1 & 0xF0) != 0x00)) || // check Byte 2 + (((SB1 & 0x3E) != 0x38) && ((SA1 & 0xC0) != 0x80)) || // check Byte 4 + (((SB1 & 0x0E) != 0x08) && ((SA1 & 0xC0) == 0x80)) || // check Byte 4 + (((SA1 & 0xC0) != 0x80) && (( C1 & 0xF0) != 0x00))) {// check Byte5 + goto shift_6byte_data; + } + + elantech_report_absolute_v2(psmouse); + SA_O = SA1; + A1_O = A1; + B1_O = B1; + SB1_O = SB1; + C1_O = C1; + D1_O = D1; + break; + case ETF0208: + if(etd->fw_version_min_sp == 0x00 ) { + SA1= packet[0]; + A1 = packet[1]; + B1 = packet[2]; + SB1= packet[3]; + C1 = packet[4]; + D1 = packet[5]; + + if( ((SA1 & 0x0C) != 0x04) || // check Byte 1 + ((SB1 & 0x0f) != 0x02) ) { // check Byte 4 + goto shift_6byte_data; + } + + fingers = ((packet[0] & 0xC0) >> 6); + button = ((packet[3]&0x01 << 2) | (packet[0] & 0x03)); + w1 = (((packet[0]&0x30)>>2)|((packet[3]&0x30)>>4)) & 0x0f; + Debug = ETF0208_DEBUG_SOLUTION(fingers,button,w1); + if(Debug){ + psmouse->resetafter = psmouse->out_of_sync_cnt+1; + return PSMOUSE_BAD_DATA; + } + + if(SA1 == SA1_O && A1 == A1_O && B1 == B1_O && + SB1 == SB1_O && C1 == C1_O && D1 == D1_O) { + goto the_same_data; + } + elantech_report_absolute_v2(psmouse); + SA_O = SA1; + A1_O = A1; + B1_O = B1; + SB1_O = SB1; + C1_O = C1; + D1_O = D1; + break; + } + case ETF020B00: + case ETF0402: + case ETF0401: + case ETF0403: + case ETF1400: + SA1= packet[0]; + A1 = packet[1]; + B1 = packet[2]; + SB1= packet[3]; + C1 = packet[4]; + D1 = packet[5]; + + if(SA1 == SA1_O && A1 == A1_O && B1 == B1_O && + SB1 == SB1_O && C1 == C1_O && D1 == D1_O) { + goto the_same_data; + } + + if( ((SA1 & 0x0C) != 0x04) || // check Byte 1 + ((SB1 & 0x0f) != 0x02) ) { // check Byte 4 + goto shift_6byte_data; + } + + elantech_report_absolute_v2(psmouse); + SA_O = SA1; + A1_O = A1; + B1_O = B1; + SB1_O = SB1; + C1_O = C1; + D1_O = D1; + break; + case ETF5900: + SA1= packet[0]; + A1 = packet[1]; + B1 = packet[2]; + SB1= packet[3]; + C1 = packet[4]; + D1 = packet[5]; + + if(SA1 == 0xc4 && A1 == 0xff && B1 == 0xff&& + SB1 == 0x02 && C1 == 0xff && D1 == 0xff) { + goto the_same_data; + } - switch (etd->hw_version) { - case 1: - if (etd->paritycheck && !elantech_check_parity_v1(psmouse)) - return PSMOUSE_BAD_DATA; + if(SA1 == SA1_O && A1 == A1_O && B1 == B1_O && + SB1 == SB1_O && C1 == C1_O && D1 == D1_O) { + goto the_same_data; + } - elantech_report_absolute_v1(psmouse); + //printk("SA1=0x%02x A1=0x%02x B1=0x%02x SB1=0x%02x C1=0x%02x D1=0x%02x\n",SA1,A1,B1,SB1,C1,D1); + if(((SA1 & 0xc0) != 0x80)&& // check finger 4 + (((SA1 & 0x0C) != 0x04) || // check Byte 1 + ((SB1 & 0x0f) != 0x02))){ // check Byte 4 + goto shift_6byte_data; + }else if((SA1 & 0xc0) == 0x80){ + if(!(((SA1 & 0x0C) == 0x04) && // check Byte 1 + ((SB1 & 0x0f) == 0x02)) && + !(((SA1 & 0x0C) == 0x0c) && // check Byte 1 + ((SB1 & 0x0e) == 0x0c))) { // check Byte 4 + goto shift_6byte_data; + } + } + elantech_report_absolute_v3(psmouse); + SA1_O = SA1; + A1_O = A1; + B1_O = B1; + SB1_O = SB1; + C1_O = C1; + D1_O = D1; break; - case 2: - /* We don't know how to check parity in protocol v2 */ - elantech_report_absolute_v2(psmouse); - break; } +the_same_data: return PSMOUSE_FULL_PACKET; + +shift_4byte_data: + packet[0] = B; + packet[1] = C; + packet[2] = D; + psmouse->pktcnt = psmouse->pktcnt-1; + return PSMOUSE_GOOD_DATA; + +shift_6byte_data: + packet[0] = A1; + packet[1] = B1; + packet[2] = SB1; + packet[3] = C1; + packet[4] = D1; + psmouse->pktcnt = psmouse->pktcnt - 1; + return PSMOUSE_GOOD_DATA; + } + + /* * Put the touchpad into absolute mode */ + static int elantech_set_absolute_mode(struct psmouse *psmouse) { struct elantech_data *etd = psmouse->private; @@ -389,55 +870,89 @@ int rc = 0; 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; + case ETF020022: + case ETF020600: + etd->reg_10 = 0x14; + etd->reg_11 = 0x8b; + if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || + elantech_write_reg(psmouse, 0x11, etd->reg_11)) { + rc = -1; + } + break; + case ETF020030: + case ETF0208: + case ETF020B00: + case ETF0402: + case ETF0401: + case ETF0403: + case ETF1400: + /* Windows driver values */ + if(etd->hw_version == ETF020030){ + etd->reg_10 = 0x54; + etd->reg_11 = 0x8a; + } + else { + etd->reg_10 = 0xc4; + etd->reg_11 = 0x8a; + } + + + if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || + elantech_write_reg(psmouse, 0x11, etd->reg_11) ) { + 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(val != etd->reg_10) + rc = elantech_sliced_command(psmouse,0x10,etd->reg_10); + + if (rc == 0) + break; + tries--; + elantech_debug("elantech.c: retrying read (%d).\n", + tries); + msleep(ETP_READ_BACK_DELAY); + } while (tries > 0); + + if (rc) + elantech_debug("elantech.c: failed to read back register 0x10.\n"); break; + case ETF5900: + etd->reg_10 = 0x03; + if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ) { + rc = -1; } - } - if (rc == 0) { /* - * Read back reg 0x10. For hardware version 1 we must make - * sure the absolute mode bit is set. For hardware version 2 - * the touchpad is probably initalising and not ready until - * we read back the value we just wrote. + * 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); + printk("val = 0x%02x \n",val); + + if(val != etd->reg_10) + rc = elantech_sliced_command(psmouse,0x10,etd->reg_10); if (rc == 0) break; tries--; - elantech_debug("retrying read (%d).\n", tries); + elantech_debug("elantech.c: retrying read (%d).\n", tries); msleep(ETP_READ_BACK_DELAY); } while (tries > 0); - if (rc) { - pr_err("failed to read back register 0x10.\n"); - } else if (etd->hw_version == 1 && - !(val & ETP_R10_ABSOLUTE_MODE)) { - pr_err("touchpad refuses to switch to absolute mode.\n"); - rc = -1; - } + if (rc) + elantech_debug("elantech.c: failed to read back register 0x10.\n"); + } if (rc) - pr_err("failed to initialise registers.\n"); + elantech_debug("elantech.c: failed to initialise registers.\n"); return rc; } @@ -447,15 +962,21 @@ */ static void elantech_set_input_params(struct psmouse *psmouse) { + struct ps2dev *ps2dev = &psmouse->ps2dev; struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; + int i; + unsigned int x_max,y_max; + unsigned int twoft_xmax,twoft_ymax; + unsigned char param[3]; + __set_bit(EV_KEY, dev->evbit); __set_bit(EV_ABS, dev->evbit); - __clear_bit(EV_REL, dev->evbit); __set_bit(BTN_LEFT, dev->keybit); __set_bit(BTN_RIGHT, dev->keybit); + __set_bit(BTN_MIDDLE, dev->keybit); __set_bit(BTN_TOUCH, dev->keybit); __set_bit(BTN_TOOL_FINGER, dev->keybit); @@ -463,29 +984,110 @@ __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); switch (etd->hw_version) { - case 1: - /* Rocker button */ - if (etd->fw_version < 0x020000 && - (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 ETF020022: + case ETF020600: + 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 ETF0208: + if(etd->fw_version_min_sp != 0x00){ + i=1; + goto count_edge; + } + case ETF020B00: + case ETF020030: + fix_edge: + Public_ETP_YMAX_V2 = ETP_YMAX_V2; + Public_ETP_2FT_YMAX = ETP_2FT_YMAX; + 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); + input_set_abs_params(dev, ABS_HAT2X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_HAT2Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(dev, ABS_TOOL_WIDTH,0,16,0,0); + __set_bit(BTN_0, dev->keybit); + __set_bit(BTN_1, dev->keybit); + break; + case ETF0402: + case ETF0401: + case ETF0403: + case ETF1400: + i=2; + count_edge: + if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) { + elantech_debug("elantech.c: failed to query capabiliities.\n"); + goto fix_edge; + } + + x_max=param[1]; + x_max=(x_max-i)*64; + y_max=param[2]; + y_max=(y_max-i)*64; + + + twoft_xmax=(x_max-i)*64/4; + twoft_ymax=(y_max-i)*64/4; + Public_ETP_YMAX_V2 = y_max; + Public_ETP_2FT_YMAX = twoft_ymax; + + input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, x_max, 0, 0); + input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, y_max, 0, 0); + input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, twoft_xmax, 0, 0); + input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, twoft_ymax, 0, 0); + input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, twoft_xmax, 0, 0); + input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, twoft_ymax, 0, 0); + input_set_abs_params(dev, ABS_HAT2X, ETP_XMIN_V2, x_max, 0, 0); + input_set_abs_params(dev, ABS_HAT2Y, ETP_YMIN_V2, y_max, 0, 0); + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(dev, ABS_TOOL_WIDTH,0,16,0,0); + __set_bit(BTN_0, dev->keybit); + __set_bit(BTN_1, dev->keybit); + break; + case ETF5900: + if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + ps2_command(ps2dev, NULL, 0x00) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + elantech_debug("elantech_chrome.c: sending elantech_set_input_params fail.\n"); + return; + } + + Public_ETP_XMAX_V2 = (0x0F & param[0]) << 8 | param[1]; + Public_ETP_YMAX_V2 = (0xF0 & param[0]) << 4 | param[2]; + elantech_debug("xmax=%d ymax=%d\n", Public_ETP_XMAX_V2, Public_ETP_YMAX_V2); + + Public_ETP_2FT_YMAX = Public_ETP_YMAX_V2; + input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, Public_ETP_XMAX_V2, 0, 0); + elantech_debug("ABS_X %d %d\n", ETP_XMIN_V2, Public_ETP_XMAX_V2); + input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, Public_ETP_YMAX_V2, 0, 0); + elantech_debug("ABS_Y %d %d\n", ETP_YMIN_V2, Public_ETP_YMAX_V2); + input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, Public_ETP_XMAX_V2, 0, 0); + elantech_debug("ABS_HAT0X %d %d\n", ETP_2FT_XMIN, Public_ETP_XMAX_V2); + input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, Public_ETP_YMAX_V2, 0, 0); + elantech_debug("ABS_HAT0Y %d %d\n", ETP_2FT_YMIN, Public_ETP_YMAX_V2); + input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, Public_ETP_XMAX_V2, 0, 0); + elantech_debug("ABS_HAT1X %d %d\n", ETP_2FT_XMIN, Public_ETP_XMAX_V2); + input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, Public_ETP_YMAX_V2, 0, 0); + elantech_debug("ABS_HAT1Y %d %d\n", ETP_2FT_YMIN, Public_ETP_YMAX_V2); + input_set_abs_params(dev, ABS_HAT2X, ETP_XMIN_V2, Public_ETP_XMAX_V2, 0, 0); + elantech_debug("ABS_HAT2X %d %d\n", ETP_XMIN_V2, Public_ETP_XMAX_V2); + input_set_abs_params(dev, ABS_HAT2Y, ETP_YMIN_V2, Public_ETP_YMAX_V2, 0, 0); + elantech_debug("ABS_HAT2Y %d %d\n", ETP_YMIN_V2, Public_ETP_YMAX_V2); + elantech_debug("ABS_PRESSURE 0 255\n"); + elantech_debug("ABS_TOOL_WIDTH 0 16\n"); + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(dev, ABS_TOOL_WIDTH,0,16,0,0); + __set_bit(BTN_0, dev->keybit); + __set_bit(BTN_1, dev->keybit); + break; - case 2: - __set_bit(BTN_TOOL_QUADTAP, dev->keybit); - 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); - break; } } + struct elantech_attr_data { size_t field_offset; unsigned char reg; @@ -528,7 +1130,7 @@ return -EINVAL; /* Do we need to preserve some bits for version 2 hardware too? */ - if (etd->hw_version == 1) { + if (etd->hw_version < ETF020030) { if (attr->reg == 0x10) /* Force absolute mode always on */ value |= ETP_R10_ABSOLUTE_MODE; @@ -584,40 +1186,33 @@ .attrs = elantech_attrs, }; -static bool elantech_is_signature_valid(const unsigned char *param) -{ - static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 }; - int i; - - if (param[0] == 0) - return false; - - if (param[1] == 0) - return true; - - for (i = 0; i < ARRAY_SIZE(rates); i++) - if (param[2] == rates[i]) - return false; - - return true; -} /* * Use magic knock to detect Elantech touchpad */ -int elantech_detect(struct psmouse *psmouse, bool set_properties) + +int elantech_detect(struct psmouse *psmouse, int set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; + struct elantech_data *etd = psmouse->private; unsigned char param[3]; - - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - + printk(KERN_INFO "2.6.2X-Elan-touchpad-2011-04-12\n"); + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE); + + param[0] = param[1] = param[2] = 0; + + if(!ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT)) { + printk(KERN_INFO "elantech.c: PSMOUSE_CMD_RESET_BAT param[0]=%x param[1]=%x param[2]=%x\n" + ,param[0],param[1],param[2]); + } + + 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_debug("sending Elantech magic knock failed.\n"); + elantech_debug("elantech.c: sending Elantech magic knock failed.\n"); return -1; } @@ -625,37 +1220,15 @@ * 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_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", - param[0], param[1], param[2]); + if (!(param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) && !(param[0] != 0x3c || param[1] != 0x03 || param[2] != 0x00)) { + pr_info("elantech.c: unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", + param[0], param[1], param[2]); return -1; } - /* - * Query touchpad's firmware version and see if it reports known - * value to avoid mis-detection. Logitech mice are known to respond - * to Elantech magic knock and there might be more. - */ - if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { - pr_debug("failed to query firmware version.\n"); - return -1; - } - - pr_debug("Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", - param[0], param[1], param[2]); - - if (!elantech_is_signature_valid(param)) { - if (!force_elantech) { - pr_debug("Probably not a real Elantech touchpad. Aborting.\n"); - return -1; - } - - pr_debug("Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n"); - } - if (set_properties) { psmouse->vendor = "Elantech"; - psmouse->name = "Touchpad"; + } return 0; @@ -677,27 +1250,110 @@ */ static int elantech_reconnect(struct psmouse *psmouse) { + struct elantech_data *etd = psmouse->private; if (elantech_detect(psmouse, 0)) return -1; if (elantech_set_absolute_mode(psmouse)) { - pr_err("failed to put touchpad back into absolute mode.\n"); + elantech_debug("elantech.c: failed to put touchpad back into absolute mode.\n"); return -1; } - return 0; } +static int elantech_reconnect_ETF0208(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int i; + int err,debug_err; + err=i8042_command(NULL,I8042_CMD_KBD_DISABLE);// tom + + + if(!err) + printk(KERN_INFO"i8042_command I8042_CMD_KBD_DISABLE OK\n"); + + switch(EF_023_DEBUG){ + + case 1: + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + debug_err=elantech_write_reg(psmouse, 0x12, 0x04); + debug_err=elantech_write_reg(psmouse, 0x60, 0x00); + debug_err=elantech_write_reg(psmouse, 0x12, 0x00); + EF_023_DEBUG=0; + + break; + + case 2: + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + debug_err=elantech_write_reg(psmouse, 0x12, 0x04); + debug_err=elantech_write_reg(psmouse, 0x1F, 0x00); + debug_err=elantech_write_reg(psmouse, 0x12, 0x00); + EF_023_DEBUG=0; + break; + case 3: + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + debug_err=elantech_write_reg(psmouse, 0x12, 0x04); + debug_err=elantech_write_reg(psmouse, 0x13, 0x08); + debug_err=elantech_write_reg(psmouse, 0x12, 0x00); + EF_023_DEBUG=0; + break; + case 4: + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + debug_err=elantech_write_reg(psmouse, 0x12, 0x04); + debug_err=elantech_write_reg(psmouse, 0x06, 0x07); + debug_err=elantech_write_reg(psmouse, 0x12, 0x00); + EF_023_DEBUG=0; + break; + case 5: + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + debug_err=elantech_write_reg(psmouse, 0x12, 0x04); + debug_err=elantech_write_reg(psmouse, 0x06, 0x17); + debug_err=elantech_write_reg(psmouse, 0x12, 0x00); + EF_023_DEBUG=0; + break; + + + default: + + for(i=0;i<3;i++){ + printk(KERN_INFO"elantech_reconnect\n"); + if (!elantech_detect(psmouse, 0)){ + if (!elantech_init(psmouse)) + break; + } + else + continue; + } + + if(i>=3){ + printk(KERN_INFO"elantech_reconnect fail\n"); + if(!err) + err=i8042_command(NULL,I8042_CMD_KBD_ENABLE);// tom + + if(!err) + printk(KERN_INFO"i8042_command I8042_CMD_KBD_ENABLE OK\n"); + return -1; + } + break; + + } + if(!err) + err=i8042_command(NULL,I8042_CMD_KBD_ENABLE);// tom + + if(!err) + printk(KERN_INFO"i8042_command I8042_CMD_KBD_ENABLE OK\n"); + return 0; +} /* * Initialize the touchpad and create sysfs entries */ int elantech_init(struct psmouse *psmouse) { + struct ps2dev *ps2dev = &psmouse->ps2dev; struct elantech_data *etd; int i, error; unsigned char param[3]; + unsigned char check_touchpad_D4[3]; - psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); + etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); + psmouse->private = etd; if (!etd) return -ENOMEM; @@ -707,52 +1363,97 @@ /* * Do the version query again so we can store the result + * Find out what version hardware this is */ if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { - pr_err("failed to query firmware version.\n"); + elantech_debug("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[1]; + etd->fw_version_min_sp = param [2]; - etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; /* - * Assume every version greater than this is new EeePC style + * Assume every version greater than this is Elan Touch Pad style * hardware with 6 byte packets */ - if (etd->fw_version >= 0x020030) { - 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("assuming hardware version %d, firmware version %d.%d.%d\n", - etd->hw_version, param[0], param[1], param[2]); - - if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) { - pr_err("failed to query capabilities.\n"); - goto init_fail; - } - pr_info("Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", - param[0], param[1], param[2]); - etd->capabilities = param[0]; + + if (etd->fw_version_maj == 0x02 && etd->fw_version_min == 0x00 && + etd->fw_version_min_sp == 0x22) { + etd->hw_version = ETF020022; + psmouse->name = "ETF020022"; + } + else if (etd->fw_version_maj == 0x02 && etd->fw_version_min == 0x06 && + etd->fw_version_min_sp == 0x00) { + etd->hw_version = ETF020600; + psmouse->name = "ETF020600"; + } + else if (etd->fw_version_maj == 0x02 && etd->fw_version_min == 0x00 && + etd->fw_version_min_sp == 0x30) { + etd->hw_version = ETF020030; + psmouse->name = "ETF020030"; + } + else if (etd->fw_version_maj == 0x02 && etd->fw_version_min == 0x08 ) { + etd->hw_version = ETF0208; + psmouse->name = "ETF0208"; + } + else if (etd->fw_version_maj == 0x02 && etd->fw_version_min == 0x0B && + etd->fw_version_min_sp == 0x00) { + etd->hw_version = ETF020B00; + psmouse->name = "ETF020B00"; + } + else if (etd->fw_version_maj == 0x04 && etd->fw_version_min == 0x02) { + etd->hw_version = ETF0402; + psmouse->name = "ETF0402"; + } + else if (etd->fw_version_maj == 0x04 && etd->fw_version_min == 0x01) { + etd->hw_version = ETF0401; + psmouse->name = "ETF0401"; + } + else if (etd->fw_version_maj == 0x04 && etd->fw_version_min == 0x03) { + etd->hw_version = ETF0403; + psmouse->name = "ETF0403"; + } + else if (etd->fw_version_maj == 0x14 && etd->fw_version_min == 0x00) { + etd->hw_version = ETF1400; + psmouse->name = "ETF1400"; + } + else if (etd->fw_version_maj == 0x14) { + etd->hw_version = ETF1400; + psmouse->name = "ETF1400"; + } + else { + if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + ps2_command(ps2dev, NULL, 0x01) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + elantech_debug("elantech_chrome.c: sending Elantech F/W Version.\n"); + return -1; + } + if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + ps2_command(ps2dev, NULL, 0x04) || + ps2_command(ps2dev, check_touchpad_D4, PSMOUSE_CMD_GETINFO)) { + elantech_debug("elantech_chrome.c: sending Elantech Touchpad Module.\n"); + return -1; + } + printk("param[0] = %x param[1] = %x param[2] = %x \n",param[0],param[1] , param[2]); + if (((param[0] &0x0f) >= 5) && ((param[1] &0x0f) >= 0x06)){ + etd->hw_version = ETF5900; + psmouse->name = "ETF1059 Click-Pad"; + } + else { + printk(KERN_DEBUG "elantech -- don't find firmware version\n"); + goto init_fail; + } - /* - * This firmware suffers from misreporting coordinates when - * a touch action starts causing the mouse cursor or scrolled page - * to jump. Enable a workaround. - */ - if (etd->fw_version == 0x020022 || etd->fw_version == 0x020600) { - pr_info("firmware version 2.0.34/2.6.0 detected, enabling jumpy cursor workaround\n"); - etd->jumpy_cursor = true; } - + pr_info("elantech.c: assuming hardware version %d, firmware version 0x%02x.0x%02x.0x%02x\n", + etd->hw_version, etd->fw_version_maj, etd->fw_version_min,etd->fw_version_min_sp); + if (elantech_set_absolute_mode(psmouse)) { - pr_err("failed to put touchpad into absolute mode.\n"); + elantech_debug("elantech.c: failed to put touchpad into absolute mode.\n"); goto init_fail; } @@ -761,14 +1462,21 @@ error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, &elantech_attr_group); if (error) { - pr_err("failed to create sysfs attributes, error: %d.\n", error); + elantech_debug("elantech.c: failed to create sysfs attributes, error: %d.\n", + error); goto init_fail; } psmouse->protocol_handler = elantech_process_byte; psmouse->disconnect = elantech_disconnect; - psmouse->reconnect = elantech_reconnect; - psmouse->pktsize = etd->hw_version == 2 ? 6 : 4; + if( etd->hw_version == ETF0208 && + etd->fw_version_min_sp == 0x00){ + EF_023_DEBUG = 0; + psmouse->reconnect =elantech_reconnect_ETF0208; + }else { + psmouse->reconnect = elantech_reconnect; + } + psmouse->pktsize = etd->hw_version > ETF020600 ? 6 : 4; return 0; @@ -776,3 +1484,4 @@ kfree(etd); return -1; } + --- linux-source-2.6.38/drivers/input/mouse/elantech.h_orig 2011-05-02 10:33:41.547381641 -0600 +++ linux-source-2.6.38/drivers/input/mouse/elantech.h 2011-05-19 14:58:49.000000000 -0600 @@ -1,14 +1,3 @@ -/* - * Elantech Touchpad driver (v6) - * - * Copyright (C) 2007-2009 Arjan Opmeer - * - * 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 - * by the Free Software Foundation. - * - * Trademarks are the property of their respective owners. - */ #ifndef _ELANTECH_H #define _ELANTECH_H @@ -19,9 +8,11 @@ #define ETP_FW_VERSION_QUERY 0x01 #define ETP_CAPABILITIES_QUERY 0x02 + /* * Command values for register reading or writing */ +#define ETP_REGISTER_RW 0x00 #define ETP_REGISTER_READ 0x10 #define ETP_REGISTER_WRITE 0x11 @@ -70,7 +61,7 @@ * 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_EDGE_FUZZ_V2 0 #define ETP_XMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2) #define ETP_XMAX_V2 (1152 - ETP_EDGE_FUZZ_V2) @@ -81,7 +72,7 @@ * For two finger touches the coordinate of each finger gets reported * separately but with reduced resolution. */ -#define ETP_2FT_FUZZ 4 +#define ETP_2FT_FUZZ 0 #define ETP_2FT_XMIN ( 0 + ETP_2FT_FUZZ) #define ETP_2FT_XMAX (288 - ETP_2FT_FUZZ) @@ -100,19 +91,41 @@ unsigned char reg_26; unsigned char debug; unsigned char capabilities; - bool paritycheck; + unsigned char fw_version_maj; + unsigned char fw_version_min; + unsigned char fw_version_min_sp; bool jumpy_cursor; unsigned char hw_version; - unsigned int fw_version; + unsigned char paritycheck; unsigned int single_finger_reports; unsigned char parity[256]; }; +typedef struct { + int x; + int y; + unsigned char button; + unsigned char fingers; +} elantech_point; + +typedef enum{ + ETF020022 = 0, + ETF020600,/*4 byte mode fw_version*/ + ETF020030, + ETF020B00, + ETF0208, + ETF0402, + ETF0401, + ETF0403, + ETF1400, + ETF5900 +} ElanFW; + #ifdef CONFIG_MOUSE_PS2_ELANTECH -int elantech_detect(struct psmouse *psmouse, bool set_properties); +int elantech_detect(struct psmouse *psmouse, int set_properties); int elantech_init(struct psmouse *psmouse); #else -static inline int elantech_detect(struct psmouse *psmouse, bool set_properties) +static inline int elantech_detect(struct psmouse *psmouse, int set_properties) { return -ENOSYS; }