Hi Yunkang, On Fri, Nov 15, 2013 at 10:11:42PM +0800, Yunkang Tang wrote: > Hi all, > > Here is the 2nd version for supporting DualPoint device on Dell XT2 model > > Changelist: > - Bugfix for trackpoint's behavior was abnormal in v1. > [Root Cause] > Because of the special MPU controller being used in this DualPoint device, > when sending ALPS magic know command, not only touchpad but also trackpoint > received the commands. This would cause trackpoint also enter an unexpected > raw mode. Unfortunately, trackpoint's raw raw packet has the same check bit > as touchpad's but with different packet format. And this would cause > trackpoint's abnormal behavior. > > [Solution] > 1. Define a new ALPS_PROTO_V6 macro for this device. > 2. Add new initialization logic. It would help greatly if you could document what parameters the new initialization routine sets. > 3. Add new packet process logic. I am curious why we need the new packet processing logic. Apparently the touchpad can work in the original mode that we already know how to parse; we just need to make sure the trackpoint is initialized properly. Or yet another protocol flavor is a must? Thanks! > # Touchpad's dimension is 2047*1535. > > SelfTest: > 1. Move on touchpad -- OK > 2. Vertical/Horizontal wheel on touchpad -- OK > 3. Tap / Tap and drag on touchpad -- OK > 4. Click with touchpad's L/R button -- OK > 5. Move on touchpad with pressing touchpad's button -- OK > 6. Move on trackpoint -- OK > 7. Click with trackpoint's L/R button -- OK > 8. Move on trackpoint with pressing trackpoint's button -- OK > 9. Move cursor with both touchpad and trackpoint -- OK > 10. Move on touchpad with pressing trackpoint's button -- OK > 11. Move on trackpoint with pressing touchpad's button -- OK > > Thanks > > Signed-off-by: Yunkang Tang <yunkang.tang@xxxxxxxxxxx> > --- > drivers/input/mouse/alps.c | 206 ++++++++++++++++++++++++++++++++++++++++++++- > drivers/input/mouse/alps.h | 1 + > 2 files changed, 204 insertions(+), 3 deletions(-) > > diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c > index ca7a26f..5cf62e3 100644 > --- a/drivers/input/mouse/alps.c > +++ b/drivers/input/mouse/alps.c > @@ -70,6 +70,25 @@ static const struct alps_nibble_commands alps_v4_nibble_commands[] = { > { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ > }; > > +static const struct alps_nibble_commands alps_v6_nibble_commands[] = { > + { PSMOUSE_CMD_ENABLE, 0x00 }, /* 0 */ > + { PSMOUSE_CMD_SETRATE, 0x0a }, /* 1 */ > + { PSMOUSE_CMD_SETRATE, 0x14 }, /* 2 */ > + { PSMOUSE_CMD_SETRATE, 0x28 }, /* 3 */ > + { PSMOUSE_CMD_SETRATE, 0x3c }, /* 4 */ > + { PSMOUSE_CMD_SETRATE, 0x50 }, /* 5 */ > + { PSMOUSE_CMD_SETRATE, 0x64 }, /* 6 */ > + { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 7 */ > + { PSMOUSE_CMD_GETID, 0x00 }, /* 8 */ > + { PSMOUSE_CMD_GETINFO, 0x00 }, /* 9 */ > + { PSMOUSE_CMD_SETRES, 0x00 }, /* a */ > + { PSMOUSE_CMD_SETRES, 0x01 }, /* b */ > + { PSMOUSE_CMD_SETRES, 0x02 }, /* c */ > + { PSMOUSE_CMD_SETRES, 0x03 }, /* d */ > + { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* e */ > + { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ > +}; > + > > #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ > #define ALPS_PASS 0x04 /* device has a pass-through port */ > @@ -103,6 +122,7 @@ static const struct alps_model_info alps_model_data[] = { > /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ > { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, > ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, > + { { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT }, /* Dell XT2 */ > { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ > { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, > ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ > @@ -645,6 +665,76 @@ static void alps_process_packet_v3(struct psmouse *psmouse) > alps_process_touchpad_packet_v3(psmouse); > } > > +static void alps_process_packet_v6(struct psmouse *psmouse) > +{ > + struct alps_data *priv = psmouse->private; > + unsigned char *packet = psmouse->packet; > + struct input_dev *dev = psmouse->dev; > + struct input_dev *dev2 = priv->dev2; > + int x, y, z, left, right, middle; > + > + /* > + * We can use Byte5 to distinguish if the packet is from Touchpad > + * or Trackpoint. > + * Touchpad: 0 - 0x7E > + * Trackpoint: 0x7F > + */ > + if (packet[5] == 0x7F) { > + /* It should be a DualPoint when received Trackpoint packet */ > + if (!(priv->flags & ALPS_DUALPOINT)) > + return; > + > + /* Trackpoint packet */ > + x = packet[1] | ((packet[3] & 0x20) << 2); > + y = packet[2] | ((packet[3] & 0x40) << 1); > + z = packet[4]; > + left = packet[3] & 0x01; > + right = packet[3] & 0x02; > + middle = packet[3] & 0x04; > + > + /* To prevent the cursor jump when finger lifted */ > + if (x == 0x7F && y == 0x7F && z == 0x7F) > + x = y = z = 0; > + > + /* Divide 4 since trackpoint's speed is too fast */ > + input_report_rel(dev2, REL_X, (char)x / 4); > + input_report_rel(dev2, REL_Y, -((char)y / 4)); > + > + input_report_key(dev2, BTN_LEFT, left); > + input_report_key(dev2, BTN_RIGHT, right); > + input_report_key(dev2, BTN_MIDDLE, middle); > + > + input_sync(dev2); > + return; > + } > + > + /* Touchpad packet */ > + x = packet[1] | ((packet[3] & 0x78) << 4); > + y = packet[2] | ((packet[4] & 0x78) << 4); > + z = packet[5]; > + left = packet[3] & 0x01; > + right = packet[3] & 0x02; > + > + if (z > 30) > + input_report_key(dev, BTN_TOUCH, 1); > + if (z < 25) > + input_report_key(dev, BTN_TOUCH, 0); > + > + if (z > 0) { > + input_report_abs(dev, ABS_X, x); > + input_report_abs(dev, ABS_Y, y); > + } > + > + input_report_abs(dev, ABS_PRESSURE, z); > + input_report_key(dev, BTN_TOOL_FINGER, z > 0); > + > + /* v6 touchpad does not have middle button */ > + input_report_key(dev, BTN_LEFT, left); > + input_report_key(dev, BTN_RIGHT, right); > + > + input_sync(dev); > +} > + > static void alps_process_packet_v4(struct psmouse *psmouse) > { > struct alps_data *priv = psmouse->private; > @@ -897,7 +987,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) > } > > /* Bytes 2 - pktsize should have 0 in the highest bit */ > - if (priv->proto_version != ALPS_PROTO_V5 && > + if ((priv->proto_version < ALPS_PROTO_V5) && > psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && > (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { > psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", > @@ -1085,6 +1175,80 @@ static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) > return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); > } > > +static int alps_monitor_mode_send_word(struct psmouse *psmouse, u16 word) > +{ > + int i, nibble; > + > + /* > + * b0-b11 are valid bits, send sequence is inverse. > + * e.g. when word = 0x0123, nibble send sequence is 3, 2, 1 > + */ > + for (i = 0; i <= 8; i += 4) { > + nibble = (word >> i) & 0xf; > + if (alps_command_mode_send_nibble(psmouse, nibble)) > + return -1; > + } > + > + return 0; > +} > + > +static int alps_monitor_mode_write_reg(struct psmouse *psmouse, > + u16 addr, u16 value) > +{ > + struct ps2dev *ps2dev = &psmouse->ps2dev; > + > + /* 0x0A0 is the command to write the word */ > + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE) || > + alps_monitor_mode_send_word(psmouse, 0x0A0) || > + alps_monitor_mode_send_word(psmouse, addr) || > + alps_monitor_mode_send_word(psmouse, value) || > + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) > + return -1; > + > + return 0; > +} > + > +static int alps_monitor_mode(struct psmouse *psmouse, bool enable) > +{ > + struct ps2dev *ps2dev = &psmouse->ps2dev; > + > + if (enable) { > + /* EC E9 F5 F5 E7 E6 E7 E9 to enter monitor mode */ > + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || > + ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO) || > + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || > + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || > + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || > + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || > + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || > + ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO)) > + return -1; > + } else { > + /* EC to exit monitor mode */ > + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP)) > + return -1; > + } > + > + return 0; > +} > + > +static int alps_absolute_mode_v6(struct psmouse *psmouse) > +{ > + u16 reg_val = 0x181; > + int ret = -1; > + > + /* enter monitor mode, to write the register */ > + if (alps_monitor_mode(psmouse, true)) > + return -1; > + > + ret = alps_monitor_mode_write_reg(psmouse, 0x000, reg_val); > + > + if (alps_monitor_mode(psmouse, false)) > + ret = -1; > + > + return ret; > +} > + > static int alps_get_status(struct psmouse *psmouse, char *param) > { > /* Get status: 0xF5 0xF5 0xF5 0xE9 */ > @@ -1189,6 +1353,32 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse) > return 0; > } > > +static int alps_hw_init_v6(struct psmouse *psmouse) > +{ > + unsigned char param[2] = {0xC8, 0x14}; > + > + /* Enter passthrough mode to let trackpoint enter 6byte raw mode */ > + if (alps_passthrough_mode_v2(psmouse, true)) > + return -1; > + > + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || > + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || > + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || > + ps2_command(&psmouse->ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || > + ps2_command(&psmouse->ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) > + return -1; > + > + if (alps_passthrough_mode_v2(psmouse, false)) > + return -1; > + > + if (alps_absolute_mode_v6(psmouse)) { > + psmouse_err(psmouse, "Failed to enable absolute mode\n"); > + return -1; > + } > + > + return 0; > +} > + > /* > * Enable or disable passthrough mode to the trackstick. > */ > @@ -1553,6 +1743,8 @@ static void alps_set_defaults(struct alps_data *priv) > priv->hw_init = alps_hw_init_v1_v2; > priv->process_packet = alps_process_packet_v1_v2; > priv->set_abs_params = alps_set_abs_params_st; > + priv->x_max = 1023; > + priv->y_max = 767; > break; > case ALPS_PROTO_V3: > priv->hw_init = alps_hw_init_v3; > @@ -1584,6 +1776,14 @@ static void alps_set_defaults(struct alps_data *priv) > priv->x_bits = 23; > priv->y_bits = 12; > break; > + case ALPS_PROTO_V6: > + priv->hw_init = alps_hw_init_v6; > + priv->process_packet = alps_process_packet_v6; > + priv->set_abs_params = alps_set_abs_params_st; > + priv->nibble_commands = alps_v6_nibble_commands; > + priv->x_max = 2047; > + priv->y_max = 1535; > + break; > } > } > > @@ -1705,8 +1905,8 @@ static void alps_disconnect(struct psmouse *psmouse) > static void alps_set_abs_params_st(struct alps_data *priv, > struct input_dev *dev1) > { > - input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); > - input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); > + input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); > + input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); > } > > static void alps_set_abs_params_mt(struct alps_data *priv, > diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h > index eee5985..704f0f9 100644 > --- a/drivers/input/mouse/alps.h > +++ b/drivers/input/mouse/alps.h > @@ -17,6 +17,7 @@ > #define ALPS_PROTO_V3 3 > #define ALPS_PROTO_V4 4 > #define ALPS_PROTO_V5 5 > +#define ALPS_PROTO_V6 6 > > /** > * struct alps_model_info - touchpad ID table > -- > 1.8.1.2 > -- Dmitry -- 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