Hi Hans, On Wed, Jul 09, 2014 at 05:24:19PM +0200, Hans de Goede wrote: > From: Yunkang Tang <yunkang.tang@xxxxxxxxxxx> > > Such as found on the new Toshiba Portégé Z30-A and Z40-A. > > Signed-off-by: Yunkang Tang <yunkang.tang@xxxxxxxxxxx> > [hdegoede@xxxxxxxxxx: Remove softbutton handling, this is done in userspace] > [hdegoede@xxxxxxxxxx: Report INPUT_PROP_BUTTONPAD] > [hdegoede@xxxxxxxxxx: Do not report fake PRESSURE, reporting BTN_TOUCH is > enough] > [hdegoede@xxxxxxxxxx: Various cleanups / refactoring] > Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> > --- > drivers/input/mouse/alps.c | 257 ++++++++++++++++++++++++++++++++++++++++++++- > drivers/input/mouse/alps.h | 18 ++++ > 2 files changed, 272 insertions(+), 3 deletions(-) > > diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c > index ad3a708..8b9b4b0 100644 > --- a/drivers/input/mouse/alps.c > +++ b/drivers/input/mouse/alps.c > @@ -100,6 +100,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = { > #define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with > 6-byte ALPS packet */ > #define ALPS_IS_RUSHMORE 0x100 /* device is a rushmore */ > +#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ > > static const struct alps_model_info alps_model_data[] = { > { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ > @@ -845,6 +846,177 @@ static void alps_process_packet_v4(struct psmouse *psmouse) > alps_report_semi_mt_data(psmouse, f->fingers); > } > > +static bool alps_is_valid_package_v7(struct psmouse *psmouse) > +{ > + if ((psmouse->pktcnt == 3) && ((psmouse->packet[2] & 0x40) != 0x40)) > + return false; > + if ((psmouse->pktcnt == 4) && ((psmouse->packet[3] & 0x48) != 0x48)) > + return false; > + if ((psmouse->pktcnt == 6) && ((psmouse->packet[5] & 0x40) != 0x0)) > + return false; > + return true; Maybe: switch (psmouse->pktcnt) { case 3: return (psmouse->packet[2] & 0x40) == 0x40; case 4: return (psmouse->packet[3] & 0x48) == 0x48; case 6: return (psmouse->packet[5] & 0x40) == 0x00; } return true; ? > +} > + > +static unsigned char alps_get_packet_id_v7(char *byte) > +{ > + unsigned char packet_id; > + > + if (byte[4] & 0x40) > + packet_id = V7_PACKET_ID_TWO; > + else if (byte[4] & 0x01) > + packet_id = V7_PACKET_ID_MULTI; > + else if ((byte[0] & 0x10) && !(byte[4] & 0x43)) > + packet_id = V7_PACKET_ID_NEW; > + else if (byte[1] == 0x00 && byte[4] == 0x00) > + packet_id = V7_PACKET_ID_IDLE; > + else > + packet_id = V7_PACKET_ID_UNKNOWN; > + > + return packet_id; > +} > + > +static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, > + unsigned char *pkt, > + unsigned char pkt_id) > +{ > + mt[0].x = ((pkt[2] & 0x80) << 4); > + mt[0].x |= ((pkt[2] & 0x3F) << 5); > + mt[0].x |= ((pkt[3] & 0x30) >> 1); > + mt[0].x |= (pkt[3] & 0x07); > + mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07); > + > + mt[1].x = ((pkt[3] & 0x80) << 4); > + mt[1].x |= ((pkt[4] & 0x80) << 3); > + mt[1].x |= ((pkt[4] & 0x3F) << 4); > + mt[1].y = ((pkt[5] & 0x80) << 3); > + mt[1].y |= ((pkt[5] & 0x3F) << 4); > + > + if (pkt_id == V7_PACKET_ID_TWO) { > + mt[1].x &= ~0x000F; > + mt[1].y |= 0x000F; > + } else if (pkt_id == V7_PACKET_ID_MULTI) { > + mt[1].x &= ~0x003F; > + mt[1].y &= ~0x0020; > + mt[1].y |= ((pkt[4] & 0x02) << 4); > + mt[1].y |= 0x001F; > + } else if (pkt_id == V7_PACKET_ID_NEW) { > + mt[1].x &= ~0x003F; > + mt[1].x |= (pkt[0] & 0x20); > + mt[1].y |= 0x000F; > + } > + > + mt[0].y = 0x7FF - mt[0].y; > + mt[1].y = 0x7FF - mt[1].y; > +} > + > +static int alps_get_mt_count(struct input_mt_pos *mt) > +{ > + int i; > + > + for (i = 0; i < MAX_TOUCHES && mt[i].x != 0 && mt[i].y != 0; i++) > + ; /* empty */; just to make sure... > + > + return i; > +} > + > +static int alps_decode_packet_v7(struct alps_fields *f, > + unsigned char *p, > + struct psmouse *psmouse) > +{ > + unsigned char pkt_id; > + > + pkt_id = alps_get_packet_id_v7(p); > + if (pkt_id == V7_PACKET_ID_IDLE) > + return 0; > + if (pkt_id == V7_PACKET_ID_UNKNOWN) > + return -1; > + > + alps_get_finger_coordinate_v7(f->mt, p, pkt_id); > + > + if (pkt_id == V7_PACKET_ID_TWO || pkt_id == V7_PACKET_ID_MULTI) { > + f->left = (p[0] & 0x80) >> 7; > + f->right = (p[0] & 0x20) >> 5; > + f->middle = (p[0] & 0x10) >> 4; > + } > + > + if (pkt_id == V7_PACKET_ID_TWO) > + f->fingers = alps_get_mt_count(f->mt); > + else if (pkt_id == V7_PACKET_ID_MULTI) > + f->fingers = 3 + (p[5] & 0x03); > + > + return 0; > +} > + > +static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) > +{ > + struct alps_data *priv = psmouse->private; > + unsigned char *packet = psmouse->packet; > + struct input_dev *dev2 = priv->dev2; > + int x, y, z, left, right, middle; > + > + /* > + * b7 b6 b5 b4 b3 b2 b1 b0 > + * Byte0 0 1 0 0 1 0 0 0 > + * Byte1 1 1 * * 1 M R L > + * Byte2 X7 1 X5 X4 X3 X2 X1 X0 > + * Byte3 Z6 1 Y6 X6 1 Y2 Y1 Y0 > + * Byte4 Y7 0 Y5 Y4 Y3 1 1 0 > + * Byte5 T&P 0 Z5 Z4 Z3 Z2 Z1 Z0 > + * M / R / L: Middle / Right / Left button > + */ > + > + x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2); > + y = (packet[3] & 0x07) | (packet[4] & 0xb8) | > + ((packet[3] & 0x20) << 1); > + z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); > + > + left = (packet[1] & 0x01); > + right = (packet[1] & 0x02) >> 1; > + middle = (packet[1] & 0x04) >> 2; > + > + /* Divide 2 since trackpoint's speed is too fast */ > + input_report_rel(dev2, REL_X, (char)x / 2); > + input_report_rel(dev2, REL_Y, -((char)y / 2)); > + > + input_report_key(dev2, BTN_LEFT, left); > + input_report_key(dev2, BTN_RIGHT, right); > + input_report_key(dev2, BTN_MIDDLE, middle); > + > + input_sync(dev2); > +} > + > +static void alps_process_touchpad_packet_v7(struct psmouse *psmouse) > +{ > + struct alps_data *priv = psmouse->private; > + struct input_dev *dev = psmouse->dev; > + struct alps_fields *f = &priv->f; > + > + memset(f, 0, sizeof(*f)); > + > + if (priv->decode_fields(f, psmouse->packet, psmouse)) > + return; > + > + alps_report_mt_data(psmouse, alps_get_mt_count(f->mt)); > + > + input_mt_report_finger_count(dev, f->fingers); > + > + input_report_key(dev, BTN_LEFT, f->left); > + input_report_key(dev, BTN_RIGHT, f->right); > + input_report_key(dev, BTN_MIDDLE, f->middle); > + > + input_sync(dev); > +} > + > +static void alps_process_packet_v7(struct psmouse *psmouse) > +{ > + unsigned char *packet = psmouse->packet; > + > + if ((packet[0] == 0x48) && ((packet[4] & 0x47) == 0x06)) > + alps_process_trackstick_packet_v7(psmouse); > + else > + alps_process_touchpad_packet_v7(psmouse); > +} > + > static void alps_report_bare_ps2_packet(struct psmouse *psmouse, > unsigned char packet[], > bool report_buttons) > @@ -1009,6 +1181,14 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) > return PSMOUSE_BAD_DATA; > } > > + if (priv->proto_version == ALPS_PROTO_V7 && > + !alps_is_valid_package_v7(psmouse)) { > + psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", > + psmouse->pktcnt - 1, > + psmouse->packet[psmouse->pktcnt - 1]); > + return PSMOUSE_BAD_DATA; > + } > + > if (psmouse->pktcnt == psmouse->pktsize) { > priv->process_packet(psmouse); > return PSMOUSE_FULL_PACKET; > @@ -1121,6 +1301,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, > return 0; > } > > +static int alps_check_valid_firmware_id(unsigned char id[]) bool > +{ > + int valid = 1; bool; true > + > + if (id[0] == 0x73) > + valid = 1; true > + else if (id[0] == 0x88) { > + if ((id[1] == 0x07) || > + (id[1] == 0x08) || > + ((id[1] & 0xf0) == 0xB0)) > + valid = 1; true > + } > + > + return valid; Hmmm, does not make sense - it is never false... > +} > + > static int alps_enter_command_mode(struct psmouse *psmouse) > { > unsigned char param[4]; > @@ -1130,8 +1326,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse) > return -1; > } > > - if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) && > - param[0] != 0x73) { > + if (!alps_check_valid_firmware_id(param)) { > psmouse_dbg(psmouse, > "unknown response while entering command mode\n"); > return -1; > @@ -1785,6 +1980,32 @@ static int alps_hw_init_dolphin_v1(struct psmouse *psmouse) > return 0; > } > > +static int alps_hw_init_v7(struct psmouse *psmouse) > +{ > + struct ps2dev *ps2dev = &psmouse->ps2dev; > + int reg_val, ret = -1; > + > + if (alps_enter_command_mode(psmouse) || > + alps_command_mode_read_reg(psmouse, 0xc2d9) == -1) > + goto error; > + > + if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64)) > + goto error; > + > + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4); > + if (reg_val == -1) > + goto error; > + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02)) > + goto error; > + > + alps_exit_command_mode(psmouse); > + return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); > + > +error: > + alps_exit_command_mode(psmouse); > + return ret; > +} > + > static void alps_set_defaults(struct alps_data *priv) > { > priv->byte0 = 0x8f; > @@ -1843,6 +2064,21 @@ static void alps_set_defaults(struct alps_data *priv) > priv->x_max = 2047; > priv->y_max = 1535; > break; > + case ALPS_PROTO_V7: > + priv->hw_init = alps_hw_init_v7; > + priv->process_packet = alps_process_packet_v7; > + priv->decode_fields = alps_decode_packet_v7; > + priv->set_abs_params = alps_set_abs_params_mt; > + priv->nibble_commands = alps_v3_nibble_commands; > + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; > + priv->x_max = 0xfff; > + priv->y_max = 0x7ff; > + priv->byte0 = 0x48; > + priv->mask0 = 0x48; > + > + if (priv->fw_ver[1] != 0xba) > + priv->flags |= ALPS_BUTTONPAD; > + break; > } > } > > @@ -1914,6 +2150,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) > return -EIO; > else > return 0; > + } else if (ec[0] == 0x88 && > + ((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) { > + priv->proto_version = ALPS_PROTO_V7; > + alps_set_defaults(priv); > + > + return 0; > } else if (ec[0] == 0x88 && ec[1] == 0x08) { > priv->proto_version = ALPS_PROTO_V3; > alps_set_defaults(priv); > @@ -1985,6 +2227,10 @@ static void alps_set_abs_params_mt(struct alps_data *priv, > > set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); > set_bit(BTN_TOOL_QUADTAP, dev1->keybit); > + > + /* V7 is real multi-touch */ > + if (priv->proto_version == ALPS_PROTO_V7) > + clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit); > } > > int alps_init(struct psmouse *psmouse) > @@ -2030,7 +2276,9 @@ int alps_init(struct psmouse *psmouse) > dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); > > priv->set_abs_params(priv, dev1); > - input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); > + /* No pressure on V7 */ > + if (priv->proto_version != ALPS_PROTO_V7) > + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); > > if (priv->flags & ALPS_WHEEL) { > dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); > @@ -2047,6 +2295,9 @@ int alps_init(struct psmouse *psmouse) > dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); > dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); > dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); > + } else if (priv->flags & ALPS_BUTTONPAD) { > + set_bit(INPUT_PROP_BUTTONPAD, dev1->propbit); > + clear_bit(BTN_RIGHT, dev1->keybit); > } else { > dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); > } > diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h > index e3d0f09..a98ac9b 100644 > --- a/drivers/input/mouse/alps.h > +++ b/drivers/input/mouse/alps.h > @@ -20,6 +20,7 @@ > #define ALPS_PROTO_V4 4 > #define ALPS_PROTO_V5 5 > #define ALPS_PROTO_V6 6 > +#define ALPS_PROTO_V7 7 /* t3btl t4s */ > > #define MAX_TOUCHES 2 > > @@ -27,6 +28,23 @@ > #define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */ > #define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */ > > +/* > + * enum V7_PACKET_ID - defines the packet type for V7 > + * V7_PACKET_ID_IDLE: There's no finger and no button activity. > + * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad > + * or there's button activities. > + * V7_PACKET_ID_MULTI: There are at least three non-resting fingers. > + * V7_PACKET_ID_NEW: The finger position in slot is not continues from > + * previous packet. > +*/ > +enum V7_PACKET_ID { > + V7_PACKET_ID_IDLE, > + V7_PACKET_ID_TWO, > + V7_PACKET_ID_MULTI, > + V7_PACKET_ID_NEW, > + V7_PACKET_ID_UNKNOWN, > +}; > + > /** > * struct alps_model_info - touchpad ID table > * @signature: E7 response string to match. > -- > 2.0.0 > Thanks. -- 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