Message ID | 1314291454-11157-2-git-send-email-seth.forshee@canonical.com |
---|---|
State | New |
Headers | show |
On 08/25/2011 10:57 AM, Seth Forshee wrote: > BugLink: https://bugs.launchpad.net/bugs/681904 > > Adds basic v3 hardware support for newer devices not currently > supported by the driver. > > Thanks to JJ Ding<jj_ding@emc.com.tw>, Tom Lin<tom_lin@emc.com.tw>, > and Mark A. Stratman<stratman@gmail.com>, whose work on other > patcesh revealed the workings of the v3 protocol. > > Signed-off-by: Seth Forshee<seth.forshee@canonical.com> > --- > drivers/input/mouse/elantech.c | 181 +++++++++++++++++++++++++++++++++------- > drivers/input/mouse/elantech.h | 5 + > 2 files changed, 157 insertions(+), 29 deletions(-) > > diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c > index 3250356..642cde0 100644 > --- a/drivers/input/mouse/elantech.c > +++ b/drivers/input/mouse/elantech.c > @@ -82,6 +82,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > { > struct elantech_data *etd = psmouse->private; > unsigned char param[3]; > + unsigned char command; > int rc = 0; > > if (reg< 0x10 || reg> 0x26) > @@ -90,9 +91,11 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > if (reg> 0x11&& reg< 0x20) > return -1; > > + command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_READ; > + > switch (etd->hw_version) { > case 1: > - if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || > + if (psmouse_sliced_command(psmouse, command) || > psmouse_sliced_command(psmouse, reg) || > ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { > rc = -1; > @@ -100,8 +103,9 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > break; > > case 2: > + case 3: > if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > - elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || > + elantech_ps2_command(psmouse, NULL, command) || > elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > elantech_ps2_command(psmouse, NULL, reg) || > elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { > @@ -125,6 +129,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, > unsigned char val) > { > struct elantech_data *etd = psmouse->private; > + unsigned char command; > int rc = 0; > > if (reg< 0x10 || reg> 0x26) > @@ -133,9 +138,11 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, > if (reg> 0x11&& reg< 0x20) > return -1; > > + command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_WRITE; > + > switch (etd->hw_version) { > case 1: > - if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || > + if (psmouse_sliced_command(psmouse, command) || > psmouse_sliced_command(psmouse, reg) || > psmouse_sliced_command(psmouse, val) || > ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { > @@ -144,8 +151,9 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, > break; > > case 2: > + case 3: > if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > - elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || > + elantech_ps2_command(psmouse, NULL, command) || > elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > elantech_ps2_command(psmouse, NULL, reg) || > elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > @@ -223,7 +231,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) > 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])); > + etd->y_max - (((packet[1]& 0x03)<< 8) | packet[3])); > } > > input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); > @@ -298,7 +306,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) > * byte 4: . . . . . . y9 y8 > * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 > */ > - y1 = ETP_YMAX_V2 - (((packet[4]& 0x03)<< 8) | packet[5]); > + y1 = etd->y_max - (((packet[4]& 0x03)<< 8) | packet[5]); > > input_report_abs(dev, ABS_X, x1); > input_report_abs(dev, ABS_Y, y1); > @@ -316,14 +324,14 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) > */ > 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]); > + y1 = etd->y_max_2ft - (((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]); > + y2 = etd->y_max_2ft - (((packet[3]& 0x20)<< 3) | packet[5]); > /* > * For compatibility with the X Synaptics driver scale up > * one coordinate and report as ordinary mouse movent > @@ -352,6 +360,83 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) > input_sync(dev); > } > > +static int elantech_v3_is_noise(unsigned int x, unsigned int y) > +{ > + return x == 0xfff&& y == 0xfff; > +} > + > +/* > + * Interpret complete data packets and report absolute mode input events for > + * hardware version 3. (6 byte packets) > + */ > +static void elantech_report_absolute_v3(struct psmouse *psmouse) > +{ > + struct elantech_data *etd = psmouse->private; > + struct input_dev *dev = psmouse->dev; > + unsigned char *packet1 = psmouse->packet; > + unsigned char *packet2 = NULL; > + unsigned int fingers, width = 0, pres = 0; > + unsigned int x1, y1, x2 = 0, y2 = 0; > + > + /* byte 0: n1 n0 . . . . R L */ > + fingers = (packet1[0]& 0xc0)>> 6; > + > + /* > + * Need to receive two packets for 2-touch events. If this > + * is the first packet, store it away and wait for the second. > + * We don't support parity in v3, so use that storage. > + */ > + if (fingers == 2&& (packet1[0]& 0x0c) == 0x04&& > + (packet1[3]& 0xcf) == 0x2) { > + memcpy(etd->parity, packet1, psmouse->pktsize); > + return; > + } > + > + /* > + * If this the second packet of a two-fingered touch event we > + * need to process the previous packet stashed away in > + * etd->parity > + */ > + if (fingers == 2) { > + packet1 = etd->parity; > + packet2 = psmouse->packet; > + } > + > + x1 = ((packet1[1]& 0x0f)<< 8) | packet1[2]; > + y1 = ((packet1[4]& 0x0f)<< 8) | packet1[5]; > + if (elantech_v3_is_noise(x1, y1)) > + return; > + y1 = etd->y_max - y1; > + > + if (packet2) { > + x2 = ((packet2[1]& 0x0f)<< 8) | packet2[2]; > + y2 = etd->y_max - (((packet2[4]& 0x0f)<< 8) | packet2[5]); > + } > + > + pres = (packet1[1]& 0xf0) | ((packet1[4]& 0xf0)>> 4); > + width = ((packet1[0]& 0x30)>> 2) | ((packet1[3]& 0x30)>> 4); > + > + elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); > + > + input_report_key(dev, BTN_TOUCH, fingers != 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_abs(dev, ABS_PRESSURE, pres); > + input_report_abs(dev, ABS_TOOL_WIDTH, width); > + > + if (fingers != 0) { > + input_report_abs(dev, ABS_X, x1); > + input_report_abs(dev, ABS_Y, y1); > + } > + > + input_report_key(dev, BTN_LEFT, packet1[0]& 0x01); > + input_report_key(dev, BTN_RIGHT, packet1[0]& 0x02); > + > + input_sync(dev); > + return; > +} > + > static int elantech_check_parity_v1(struct psmouse *psmouse) > { > struct elantech_data *etd = psmouse->private; > @@ -401,6 +486,10 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) > /* We don't know how to check parity in protocol v2 */ > elantech_report_absolute_v2(psmouse); > break; > + case 3: > + /* We don't know how to check parity in protocol v3 */ > + elantech_report_absolute_v3(psmouse); > + break; > } > > return PSMOUSE_FULL_PACKET; > @@ -435,8 +524,13 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) > elantech_write_reg(psmouse, 0x11, etd->reg_11) || > elantech_write_reg(psmouse, 0x21, etd->reg_21)) { > rc = -1; > - break; > } > + break; > + case 3: > + etd->reg_10 = 0x0b; > + if (elantech_write_reg(psmouse, 0x10, etd->reg_10)) > + rc = -1; > + break; > } > > if (rc == 0) { > @@ -473,10 +567,11 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) > /* > * Set the appropriate event bits for the input subsystem > */ > -static void elantech_set_input_params(struct psmouse *psmouse) > +static int elantech_set_input_params(struct psmouse *psmouse) > { > struct input_dev *dev = psmouse->dev; > struct elantech_data *etd = psmouse->private; > + unsigned char param[3]; > > __set_bit(EV_KEY, dev->evbit); > __set_bit(EV_ABS, dev->evbit); > @@ -492,20 +587,40 @@ static void elantech_set_input_params(struct psmouse *psmouse) > > switch (etd->hw_version) { > case 1: > + etd->x_max = ETP_XMAX_V1; > + etd->y_max = ETP_YMAX_V1; > + > /* 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); > + input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, etd->x_max, 0, 0); > + input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, etd->y_max, 0, 0); > 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); > + /* fall through */ > + case 3: > + if (etd->hw_version == 3) { > + if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param)) > + return -1; > + > + etd->x_max = (param[0]& 0x0f)<< 8 | param[1]; > + etd->y_max = (param[0]& 0xf0)<< 4 | param[2]; > + etd->y_max_2ft = etd->y_max; > + elantech_debug("x_max = %d, y_max = %d\n", > + etd->x_max, etd->y_max); > + } else { > + etd->x_max = ETP_XMAX_V2; > + etd->y_max = ETP_YMAX_V2; > + etd->y_max_2ft = ETP_2FT_YMAX; > + } > + > + input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, etd->x_max, 0, 0); > + input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, etd->y_max, 0, 0); > if (etd->reports_pressure) { > input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, > ETP_PMAX_V2, 0, 0); > @@ -514,10 +629,12 @@ static void elantech_set_input_params(struct psmouse *psmouse) > } > __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); > input_mt_init_slots(dev, 2); > - input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0); > - input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0); > + input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, etd->x_max, 0, 0); > + input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, etd->y_max, 0, 0); > break; > } > + > + return 0; > } > > struct elantech_attr_data { > @@ -659,7 +776,8 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) > * 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) { > + if (param[0] != 0x3c || param[1] != 0x03 || > + (param[2] != 0xc8&& param[2] != 0x00)) { > pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", > param[0], param[1], param[2]); > return -1; > @@ -749,23 +867,24 @@ int elantech_init(struct psmouse *psmouse) > > etd->fw_version = (param[0]<< 16) | (param[1]<< 8) | param[2]; > > - /* > - * Assume every version greater than this is new EeePC style > - * hardware with 6 byte packets > - */ > - if (etd->fw_version>= 0x020030) { > + if (etd->fw_version< 0x020030) { > + etd->hw_version = 1; > + etd->paritycheck = 1; > + } else if (etd->fw_version<= 0x150600) { > 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; > > if (etd->fw_version>= 0x020800) > etd->reports_pressure = true; > - > + } else if ((etd->fw_version& 0x0f0000) == 0x050000) { > + etd->hw_version = 3; > + etd->debug = 1; > + etd->reports_pressure = true; > } else { > - etd->hw_version = 1; > - etd->paritycheck = 1; > + pr_debug("unrecognized firmware version 0x%06x\n", > + etd->fw_version); > + goto init_fail; > } > > pr_info("assuming hardware version %d, firmware version %d.%d.%d\n", > @@ -794,7 +913,11 @@ int elantech_init(struct psmouse *psmouse) > goto init_fail; > } > > - elantech_set_input_params(psmouse); > + error = elantech_set_input_params(psmouse); > + if (error) { > + pr_err("failed to set input parameters\n"); > + goto init_fail; > + } > > error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, > &elantech_attr_group); > @@ -806,7 +929,7 @@ int elantech_init(struct psmouse *psmouse) > psmouse->protocol_handler = elantech_process_byte; > psmouse->disconnect = elantech_disconnect; > psmouse->reconnect = elantech_reconnect; > - psmouse->pktsize = etd->hw_version == 2 ? 6 : 4; > + psmouse->pktsize = etd->hw_version == 1 ? 4 : 6; > > return 0; > > diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h > index fabb2b9..40787d6 100644 > --- a/drivers/input/mouse/elantech.h > +++ b/drivers/input/mouse/elantech.h > @@ -16,12 +16,14 @@ > /* > * Command values for Synaptics style queries > */ > +#define ETP_FW_ID_QUERY 0x00 > #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 > > @@ -110,6 +112,9 @@ struct elantech_data { > bool reports_pressure; > unsigned char hw_version; > unsigned int fw_version; > + unsigned int x_max; > + unsigned int y_max; > + unsigned int y_max_2ft; > unsigned int single_finger_reports; > unsigned char parity[256]; > }; Looks like this isolates the v3 protocol, and shouldn't cause any v1 or v2 regressions though I had to look at the use of y_max_2ft pretty closely. Should we carry this in P until it conflicts ? rtg
On Fri, Aug 26, 2011 at 07:23:00AM -0600, Tim Gardner wrote: > On 08/25/2011 10:57 AM, Seth Forshee wrote: > >BugLink: https://bugs.launchpad.net/bugs/681904 > > > >Adds basic v3 hardware support for newer devices not currently > >supported by the driver. > > > >Thanks to JJ Ding<jj_ding@emc.com.tw>, Tom Lin<tom_lin@emc.com.tw>, > >and Mark A. Stratman<stratman@gmail.com>, whose work on other > >patcesh revealed the workings of the v3 protocol. > > > >Signed-off-by: Seth Forshee<seth.forshee@canonical.com> > >--- > > drivers/input/mouse/elantech.c | 181 +++++++++++++++++++++++++++++++++------- > > drivers/input/mouse/elantech.h | 5 + > > 2 files changed, 157 insertions(+), 29 deletions(-) > > > >diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c > >index 3250356..642cde0 100644 > >--- a/drivers/input/mouse/elantech.c > >+++ b/drivers/input/mouse/elantech.c > >@@ -82,6 +82,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > > { > > struct elantech_data *etd = psmouse->private; > > unsigned char param[3]; > >+ unsigned char command; > > int rc = 0; > > > > if (reg< 0x10 || reg> 0x26) > >@@ -90,9 +91,11 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > > if (reg> 0x11&& reg< 0x20) > > return -1; > > > >+ command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_READ; > >+ > > switch (etd->hw_version) { > > case 1: > >- if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || > >+ if (psmouse_sliced_command(psmouse, command) || > > psmouse_sliced_command(psmouse, reg) || > > ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { > > rc = -1; > >@@ -100,8 +103,9 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > > break; > > > > case 2: > >+ case 3: > > if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > >- elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || > >+ elantech_ps2_command(psmouse, NULL, command) || > > elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > > elantech_ps2_command(psmouse, NULL, reg) || > > elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { > >@@ -125,6 +129,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, > > unsigned char val) > > { > > struct elantech_data *etd = psmouse->private; > >+ unsigned char command; > > int rc = 0; > > > > if (reg< 0x10 || reg> 0x26) > >@@ -133,9 +138,11 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, > > if (reg> 0x11&& reg< 0x20) > > return -1; > > > >+ command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_WRITE; > >+ > > switch (etd->hw_version) { > > case 1: > >- if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || > >+ if (psmouse_sliced_command(psmouse, command) || > > psmouse_sliced_command(psmouse, reg) || > > psmouse_sliced_command(psmouse, val) || > > ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { > >@@ -144,8 +151,9 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, > > break; > > > > case 2: > >+ case 3: > > if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > >- elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || > >+ elantech_ps2_command(psmouse, NULL, command) || > > elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > > elantech_ps2_command(psmouse, NULL, reg) || > > elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > >@@ -223,7 +231,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) > > 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])); > >+ etd->y_max - (((packet[1]& 0x03)<< 8) | packet[3])); > > } > > > > input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); > >@@ -298,7 +306,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) > > * byte 4: . . . . . . y9 y8 > > * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 > > */ > >- y1 = ETP_YMAX_V2 - (((packet[4]& 0x03)<< 8) | packet[5]); > >+ y1 = etd->y_max - (((packet[4]& 0x03)<< 8) | packet[5]); > > > > input_report_abs(dev, ABS_X, x1); > > input_report_abs(dev, ABS_Y, y1); > >@@ -316,14 +324,14 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) > > */ > > 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]); > >+ y1 = etd->y_max_2ft - (((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]); > >+ y2 = etd->y_max_2ft - (((packet[3]& 0x20)<< 3) | packet[5]); > > /* > > * For compatibility with the X Synaptics driver scale up > > * one coordinate and report as ordinary mouse movent > >@@ -352,6 +360,83 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) > > input_sync(dev); > > } > > > >+static int elantech_v3_is_noise(unsigned int x, unsigned int y) > >+{ > >+ return x == 0xfff&& y == 0xfff; > >+} > >+ > >+/* > >+ * Interpret complete data packets and report absolute mode input events for > >+ * hardware version 3. (6 byte packets) > >+ */ > >+static void elantech_report_absolute_v3(struct psmouse *psmouse) > >+{ > >+ struct elantech_data *etd = psmouse->private; > >+ struct input_dev *dev = psmouse->dev; > >+ unsigned char *packet1 = psmouse->packet; > >+ unsigned char *packet2 = NULL; > >+ unsigned int fingers, width = 0, pres = 0; > >+ unsigned int x1, y1, x2 = 0, y2 = 0; > >+ > >+ /* byte 0: n1 n0 . . . . R L */ > >+ fingers = (packet1[0]& 0xc0)>> 6; > >+ > >+ /* > >+ * Need to receive two packets for 2-touch events. If this > >+ * is the first packet, store it away and wait for the second. > >+ * We don't support parity in v3, so use that storage. > >+ */ > >+ if (fingers == 2&& (packet1[0]& 0x0c) == 0x04&& > >+ (packet1[3]& 0xcf) == 0x2) { > >+ memcpy(etd->parity, packet1, psmouse->pktsize); > >+ return; > >+ } > >+ > >+ /* > >+ * If this the second packet of a two-fingered touch event we > >+ * need to process the previous packet stashed away in > >+ * etd->parity > >+ */ > >+ if (fingers == 2) { > >+ packet1 = etd->parity; > >+ packet2 = psmouse->packet; > >+ } > >+ > >+ x1 = ((packet1[1]& 0x0f)<< 8) | packet1[2]; > >+ y1 = ((packet1[4]& 0x0f)<< 8) | packet1[5]; > >+ if (elantech_v3_is_noise(x1, y1)) > >+ return; > >+ y1 = etd->y_max - y1; > >+ > >+ if (packet2) { > >+ x2 = ((packet2[1]& 0x0f)<< 8) | packet2[2]; > >+ y2 = etd->y_max - (((packet2[4]& 0x0f)<< 8) | packet2[5]); > >+ } > >+ > >+ pres = (packet1[1]& 0xf0) | ((packet1[4]& 0xf0)>> 4); > >+ width = ((packet1[0]& 0x30)>> 2) | ((packet1[3]& 0x30)>> 4); > >+ > >+ elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); > >+ > >+ input_report_key(dev, BTN_TOUCH, fingers != 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_abs(dev, ABS_PRESSURE, pres); > >+ input_report_abs(dev, ABS_TOOL_WIDTH, width); > >+ > >+ if (fingers != 0) { > >+ input_report_abs(dev, ABS_X, x1); > >+ input_report_abs(dev, ABS_Y, y1); > >+ } > >+ > >+ input_report_key(dev, BTN_LEFT, packet1[0]& 0x01); > >+ input_report_key(dev, BTN_RIGHT, packet1[0]& 0x02); > >+ > >+ input_sync(dev); > >+ return; > >+} > >+ > > static int elantech_check_parity_v1(struct psmouse *psmouse) > > { > > struct elantech_data *etd = psmouse->private; > >@@ -401,6 +486,10 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) > > /* We don't know how to check parity in protocol v2 */ > > elantech_report_absolute_v2(psmouse); > > break; > >+ case 3: > >+ /* We don't know how to check parity in protocol v3 */ > >+ elantech_report_absolute_v3(psmouse); > >+ break; > > } > > > > return PSMOUSE_FULL_PACKET; > >@@ -435,8 +524,13 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) > > elantech_write_reg(psmouse, 0x11, etd->reg_11) || > > elantech_write_reg(psmouse, 0x21, etd->reg_21)) { > > rc = -1; > >- break; > > } > >+ break; > >+ case 3: > >+ etd->reg_10 = 0x0b; > >+ if (elantech_write_reg(psmouse, 0x10, etd->reg_10)) > >+ rc = -1; > >+ break; > > } > > > > if (rc == 0) { > >@@ -473,10 +567,11 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) > > /* > > * Set the appropriate event bits for the input subsystem > > */ > >-static void elantech_set_input_params(struct psmouse *psmouse) > >+static int elantech_set_input_params(struct psmouse *psmouse) > > { > > struct input_dev *dev = psmouse->dev; > > struct elantech_data *etd = psmouse->private; > >+ unsigned char param[3]; > > > > __set_bit(EV_KEY, dev->evbit); > > __set_bit(EV_ABS, dev->evbit); > >@@ -492,20 +587,40 @@ static void elantech_set_input_params(struct psmouse *psmouse) > > > > switch (etd->hw_version) { > > case 1: > >+ etd->x_max = ETP_XMAX_V1; > >+ etd->y_max = ETP_YMAX_V1; > >+ > > /* 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); > >+ input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, etd->x_max, 0, 0); > >+ input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, etd->y_max, 0, 0); > > 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); > >+ /* fall through */ > >+ case 3: > >+ if (etd->hw_version == 3) { > >+ if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param)) > >+ return -1; > >+ > >+ etd->x_max = (param[0]& 0x0f)<< 8 | param[1]; > >+ etd->y_max = (param[0]& 0xf0)<< 4 | param[2]; > >+ etd->y_max_2ft = etd->y_max; > >+ elantech_debug("x_max = %d, y_max = %d\n", > >+ etd->x_max, etd->y_max); > >+ } else { > >+ etd->x_max = ETP_XMAX_V2; > >+ etd->y_max = ETP_YMAX_V2; > >+ etd->y_max_2ft = ETP_2FT_YMAX; > >+ } > >+ > >+ input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, etd->x_max, 0, 0); > >+ input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, etd->y_max, 0, 0); > > if (etd->reports_pressure) { > > input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, > > ETP_PMAX_V2, 0, 0); > >@@ -514,10 +629,12 @@ static void elantech_set_input_params(struct psmouse *psmouse) > > } > > __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); > > input_mt_init_slots(dev, 2); > >- input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0); > >- input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0); > >+ input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, etd->x_max, 0, 0); > >+ input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, etd->y_max, 0, 0); > > break; > > } > >+ > >+ return 0; > > } > > > > struct elantech_attr_data { > >@@ -659,7 +776,8 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) > > * 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) { > >+ if (param[0] != 0x3c || param[1] != 0x03 || > >+ (param[2] != 0xc8&& param[2] != 0x00)) { > > pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", > > param[0], param[1], param[2]); > > return -1; > >@@ -749,23 +867,24 @@ int elantech_init(struct psmouse *psmouse) > > > > etd->fw_version = (param[0]<< 16) | (param[1]<< 8) | param[2]; > > > >- /* > >- * Assume every version greater than this is new EeePC style > >- * hardware with 6 byte packets > >- */ > >- if (etd->fw_version>= 0x020030) { > >+ if (etd->fw_version< 0x020030) { > >+ etd->hw_version = 1; > >+ etd->paritycheck = 1; > >+ } else if (etd->fw_version<= 0x150600) { > > 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; > > > > if (etd->fw_version>= 0x020800) > > etd->reports_pressure = true; > >- > >+ } else if ((etd->fw_version& 0x0f0000) == 0x050000) { > >+ etd->hw_version = 3; > >+ etd->debug = 1; > >+ etd->reports_pressure = true; > > } else { > >- etd->hw_version = 1; > >- etd->paritycheck = 1; > >+ pr_debug("unrecognized firmware version 0x%06x\n", > >+ etd->fw_version); > >+ goto init_fail; > > } > > > > pr_info("assuming hardware version %d, firmware version %d.%d.%d\n", > >@@ -794,7 +913,11 @@ int elantech_init(struct psmouse *psmouse) > > goto init_fail; > > } > > > >- elantech_set_input_params(psmouse); > >+ error = elantech_set_input_params(psmouse); > >+ if (error) { > >+ pr_err("failed to set input parameters\n"); > >+ goto init_fail; > >+ } > > > > error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, > > &elantech_attr_group); > >@@ -806,7 +929,7 @@ int elantech_init(struct psmouse *psmouse) > > psmouse->protocol_handler = elantech_process_byte; > > psmouse->disconnect = elantech_disconnect; > > psmouse->reconnect = elantech_reconnect; > >- psmouse->pktsize = etd->hw_version == 2 ? 6 : 4; > >+ psmouse->pktsize = etd->hw_version == 1 ? 4 : 6; > > > > return 0; > > > >diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h > >index fabb2b9..40787d6 100644 > >--- a/drivers/input/mouse/elantech.h > >+++ b/drivers/input/mouse/elantech.h > >@@ -16,12 +16,14 @@ > > /* > > * Command values for Synaptics style queries > > */ > >+#define ETP_FW_ID_QUERY 0x00 > > #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 > > > >@@ -110,6 +112,9 @@ struct elantech_data { > > bool reports_pressure; > > unsigned char hw_version; > > unsigned int fw_version; > >+ unsigned int x_max; > >+ unsigned int y_max; > >+ unsigned int y_max_2ft; > > unsigned int single_finger_reports; > > unsigned char parity[256]; > > }; > > Looks like this isolates the v3 protocol, and shouldn't cause any v1 > or v2 regressions though I had to look at the use of y_max_2ft > pretty closely. > > Should we carry this in P until it conflicts ? Yeah, I think so. Elantech sent out new patches today that look good upon review, but I still need to test. So I think we should see them land in 3.2, but since there aren't any assurances I think carrying it in P for now is the best plan.
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 3250356..642cde0 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -82,6 +82,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, { struct elantech_data *etd = psmouse->private; unsigned char param[3]; + unsigned char command; int rc = 0; if (reg < 0x10 || reg > 0x26) @@ -90,9 +91,11 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, if (reg > 0x11 && reg < 0x20) return -1; + command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_READ; + switch (etd->hw_version) { case 1: - if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || + if (psmouse_sliced_command(psmouse, command) || psmouse_sliced_command(psmouse, reg) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { rc = -1; @@ -100,8 +103,9 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, break; case 2: + case 3: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || + elantech_ps2_command(psmouse, NULL, command) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { @@ -125,6 +129,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, unsigned char val) { struct elantech_data *etd = psmouse->private; + unsigned char command; int rc = 0; if (reg < 0x10 || reg > 0x26) @@ -133,9 +138,11 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, if (reg > 0x11 && reg < 0x20) return -1; + command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_WRITE; + switch (etd->hw_version) { case 1: - if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || + if (psmouse_sliced_command(psmouse, command) || psmouse_sliced_command(psmouse, reg) || psmouse_sliced_command(psmouse, val) || ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { @@ -144,8 +151,9 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, break; case 2: + case 3: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || + elantech_ps2_command(psmouse, NULL, command) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || @@ -223,7 +231,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) 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])); + etd->y_max - (((packet[1] & 0x03) << 8) | packet[3])); } input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); @@ -298,7 +306,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) * byte 4: . . . . . . y9 y8 * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */ - y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]); + y1 = etd->y_max - (((packet[4] & 0x03) << 8) | packet[5]); input_report_abs(dev, ABS_X, x1); input_report_abs(dev, ABS_Y, y1); @@ -316,14 +324,14 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) */ 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]); + y1 = etd->y_max_2ft - (((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]); + y2 = etd->y_max_2ft - (((packet[3] & 0x20) << 3) | packet[5]); /* * For compatibility with the X Synaptics driver scale up * one coordinate and report as ordinary mouse movent @@ -352,6 +360,83 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) input_sync(dev); } +static int elantech_v3_is_noise(unsigned int x, unsigned int y) +{ + return x == 0xfff && y == 0xfff; +} + +/* + * Interpret complete data packets and report absolute mode input events for + * hardware version 3. (6 byte packets) + */ +static void elantech_report_absolute_v3(struct psmouse *psmouse) +{ + struct elantech_data *etd = psmouse->private; + struct input_dev *dev = psmouse->dev; + unsigned char *packet1 = psmouse->packet; + unsigned char *packet2 = NULL; + unsigned int fingers, width = 0, pres = 0; + unsigned int x1, y1, x2 = 0, y2 = 0; + + /* byte 0: n1 n0 . . . . R L */ + fingers = (packet1[0] & 0xc0) >> 6; + + /* + * Need to receive two packets for 2-touch events. If this + * is the first packet, store it away and wait for the second. + * We don't support parity in v3, so use that storage. + */ + if (fingers == 2 && (packet1[0] & 0x0c) == 0x04 && + (packet1[3] & 0xcf) == 0x2) { + memcpy(etd->parity, packet1, psmouse->pktsize); + return; + } + + /* + * If this the second packet of a two-fingered touch event we + * need to process the previous packet stashed away in + * etd->parity + */ + if (fingers == 2) { + packet1 = etd->parity; + packet2 = psmouse->packet; + } + + x1 = ((packet1[1] & 0x0f) << 8) | packet1[2]; + y1 = ((packet1[4] & 0x0f) << 8) | packet1[5]; + if (elantech_v3_is_noise(x1, y1)) + return; + y1 = etd->y_max - y1; + + if (packet2) { + x2 = ((packet2[1] & 0x0f) << 8) | packet2[2]; + y2 = etd->y_max - (((packet2[4] & 0x0f) << 8) | packet2[5]); + } + + pres = (packet1[1] & 0xf0) | ((packet1[4] & 0xf0) >> 4); + width = ((packet1[0] & 0x30) >> 2) | ((packet1[3] & 0x30) >> 4); + + elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + + input_report_key(dev, BTN_TOUCH, fingers != 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_abs(dev, ABS_PRESSURE, pres); + input_report_abs(dev, ABS_TOOL_WIDTH, width); + + if (fingers != 0) { + input_report_abs(dev, ABS_X, x1); + input_report_abs(dev, ABS_Y, y1); + } + + input_report_key(dev, BTN_LEFT, packet1[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet1[0] & 0x02); + + input_sync(dev); + return; +} + static int elantech_check_parity_v1(struct psmouse *psmouse) { struct elantech_data *etd = psmouse->private; @@ -401,6 +486,10 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) /* We don't know how to check parity in protocol v2 */ elantech_report_absolute_v2(psmouse); break; + case 3: + /* We don't know how to check parity in protocol v3 */ + elantech_report_absolute_v3(psmouse); + break; } return PSMOUSE_FULL_PACKET; @@ -435,8 +524,13 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) elantech_write_reg(psmouse, 0x11, etd->reg_11) || elantech_write_reg(psmouse, 0x21, etd->reg_21)) { rc = -1; - break; } + break; + case 3: + etd->reg_10 = 0x0b; + if (elantech_write_reg(psmouse, 0x10, etd->reg_10)) + rc = -1; + break; } if (rc == 0) { @@ -473,10 +567,11 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) /* * Set the appropriate event bits for the input subsystem */ -static void elantech_set_input_params(struct psmouse *psmouse) +static int elantech_set_input_params(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; + unsigned char param[3]; __set_bit(EV_KEY, dev->evbit); __set_bit(EV_ABS, dev->evbit); @@ -492,20 +587,40 @@ static void elantech_set_input_params(struct psmouse *psmouse) switch (etd->hw_version) { case 1: + etd->x_max = ETP_XMAX_V1; + etd->y_max = ETP_YMAX_V1; + /* 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); + input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, etd->x_max, 0, 0); + input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, etd->y_max, 0, 0); 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); + /* fall through */ + case 3: + if (etd->hw_version == 3) { + if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + return -1; + + etd->x_max = (param[0] & 0x0f) << 8 | param[1]; + etd->y_max = (param[0] & 0xf0) << 4 | param[2]; + etd->y_max_2ft = etd->y_max; + elantech_debug("x_max = %d, y_max = %d\n", + etd->x_max, etd->y_max); + } else { + etd->x_max = ETP_XMAX_V2; + etd->y_max = ETP_YMAX_V2; + etd->y_max_2ft = ETP_2FT_YMAX; + } + + input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, etd->x_max, 0, 0); + input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, etd->y_max, 0, 0); if (etd->reports_pressure) { input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, ETP_PMAX_V2, 0, 0); @@ -514,10 +629,12 @@ static void elantech_set_input_params(struct psmouse *psmouse) } __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); input_mt_init_slots(dev, 2); - input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0); - input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, etd->x_max, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, etd->y_max, 0, 0); break; } + + return 0; } struct elantech_attr_data { @@ -659,7 +776,8 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) * 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) { + if (param[0] != 0x3c || param[1] != 0x03 || + (param[2] != 0xc8 && param[2] != 0x00)) { pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", param[0], param[1], param[2]); return -1; @@ -749,23 +867,24 @@ int elantech_init(struct psmouse *psmouse) etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; - /* - * Assume every version greater than this is new EeePC style - * hardware with 6 byte packets - */ - if (etd->fw_version >= 0x020030) { + if (etd->fw_version < 0x020030) { + etd->hw_version = 1; + etd->paritycheck = 1; + } else if (etd->fw_version <= 0x150600) { 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; if (etd->fw_version >= 0x020800) etd->reports_pressure = true; - + } else if ((etd->fw_version & 0x0f0000) == 0x050000) { + etd->hw_version = 3; + etd->debug = 1; + etd->reports_pressure = true; } else { - etd->hw_version = 1; - etd->paritycheck = 1; + pr_debug("unrecognized firmware version 0x%06x\n", + etd->fw_version); + goto init_fail; } pr_info("assuming hardware version %d, firmware version %d.%d.%d\n", @@ -794,7 +913,11 @@ int elantech_init(struct psmouse *psmouse) goto init_fail; } - elantech_set_input_params(psmouse); + error = elantech_set_input_params(psmouse); + if (error) { + pr_err("failed to set input parameters\n"); + goto init_fail; + } error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, &elantech_attr_group); @@ -806,7 +929,7 @@ int elantech_init(struct psmouse *psmouse) psmouse->protocol_handler = elantech_process_byte; psmouse->disconnect = elantech_disconnect; psmouse->reconnect = elantech_reconnect; - psmouse->pktsize = etd->hw_version == 2 ? 6 : 4; + psmouse->pktsize = etd->hw_version == 1 ? 4 : 6; return 0; diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index fabb2b9..40787d6 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -16,12 +16,14 @@ /* * Command values for Synaptics style queries */ +#define ETP_FW_ID_QUERY 0x00 #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 @@ -110,6 +112,9 @@ struct elantech_data { bool reports_pressure; unsigned char hw_version; unsigned int fw_version; + unsigned int x_max; + unsigned int y_max; + unsigned int y_max_2ft; unsigned int single_finger_reports; unsigned char parity[256]; };
BugLink: https://bugs.launchpad.net/bugs/681904 Adds basic v3 hardware support for newer devices not currently supported by the driver. Thanks to JJ Ding <jj_ding@emc.com.tw>, Tom Lin <tom_lin@emc.com.tw>, and Mark A. Stratman <stratman@gmail.com>, whose work on other patcesh revealed the workings of the v3 protocol. Signed-off-by: Seth Forshee <seth.forshee@canonical.com> --- drivers/input/mouse/elantech.c | 181 +++++++++++++++++++++++++++++++++------- drivers/input/mouse/elantech.h | 5 + 2 files changed, 157 insertions(+), 29 deletions(-)