diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index a9f461e..27ec20e 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -29,15 +29,17 @@ #define dbg(format, arg...) do {} while (0) #endif -#define ALPS_DUALPOINT 0x01 -#define ALPS_WHEEL 0x02 -#define ALPS_FW_BK_1 0x04 -#define ALPS_4BTN 0x08 -#define ALPS_OLDPROTO 0x10 -#define ALPS_PASS 0x20 -#define ALPS_FW_BK_2 0x40 -#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with +#define ALPS_DUALPOINT 0x001 +#define ALPS_WHEEL 0x002 +#define ALPS_FW_BK_1 0x004 +#define ALPS_4BTN 0x008 +#define ALPS_OLDPROTO 0x010 +#define ALPS_PASS 0x020 +#define ALPS_FW_BK_2 0x040 +#define ALPS_PS2_INTERLEAVED 0x080 /* 3-byte PS/2 packet interleaved with 6-byte ALPS packet */ +#define ALPS_EC_PROTO 0x100 /* EC memory access protocol */ +#define ALPS_IMPS 0x200 /* IMPS emulation */ static const struct alps_model_info alps_model_data[] = { { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ @@ -64,6 +66,7 @@ static const struct alps_model_info alps_model_data[] = { { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 }, /* Dell Vostro 1400 */ { { 0x52, 0x01, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ + { { 0x73, 0x02, 0x64 }, 0x00, 0x00, ALPS_EC_PROTO | ALPS_IMPS }, /* Dell E2 series */ }; /* @@ -516,6 +519,89 @@ static int alps_absolute_mode(struct psmouse *psmouse) return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); } +static int alps_ec_mode(struct psmouse *psmouse, bool enable) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) + return -1; + if (enable) { + /* EC EC EC E9 */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP)) + return -1; + param[0] = param[1] = param[2] = 0xff; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + + dbg("EC report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); + + if (param[0] != 0x88 || param[1] != 0x07 || (param[2] != 0x9b && param[2] != 0x9d)) + return -1; + } + + return 0; +} + +static int alps_ec_nibble(struct psmouse *psmouse, uint8_t nibble) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param; + static const int cmds[] = { + PSMOUSE_CMD_SETPOLL, + PSMOUSE_CMD_RESET_DIS, + PSMOUSE_CMD_SETSCALE21, + PSMOUSE_CMD_SETRATE, + PSMOUSE_CMD_SETRATE, + PSMOUSE_CMD_SETRATE, + PSMOUSE_CMD_SETRATE, + PSMOUSE_CMD_SETRATE, + PSMOUSE_CMD_SETRATE, + PSMOUSE_CMD_SETRATE, + PSMOUSE_CMD_GETINFO, + PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_SETSCALE11 }; + static const unsigned char params[] = { + 0xff, 0xff, 0xff, 10, 20, 40, 60, 80, 100, 200, 0xff, 0, 1, 2, 3, 0xff }; + + nibble &= 0xf; + param = params[nibble]; + if (ps2_command(ps2dev, ¶m, cmds[nibble])) + return -1; + + return 0; +} + +static int alps_ec_write(struct psmouse *psmouse, uint16_t addr, uint8_t value) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + /* Select new address: EC addr3 addr2 addr1 addr0 */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + alps_ec_nibble(psmouse, addr >> 12) || + alps_ec_nibble(psmouse, addr >> 8) || + alps_ec_nibble(psmouse, addr >> 4) || + alps_ec_nibble(psmouse, addr)) + return -1; + + /* + * PSMOUSE_CMD_GETINFO can be used to read from the current address, + * returning { addr_high, addr_low, value } Useful when working with bit fields. + */ + + /* Write byte: value1 value0 */ + if (alps_ec_nibble(psmouse, value >> 4) || + alps_ec_nibble(psmouse, value)) + return -1; + + return 0; +} + static int alps_get_status(struct psmouse *psmouse, char *param) { struct ps2dev *ps2dev = &psmouse->ps2dev; @@ -602,30 +688,65 @@ static int alps_hw_init(struct psmouse *psmouse, int *version) if (!priv->i) return -1; - if ((priv->i->flags & ALPS_PASS) && - alps_passthrough_mode(psmouse, true)) { + if (!priv->dev2 && !(priv->i->flags & ALPS_IMPS)) { + /* Because we never initialized dev2 */ + printk(KERN_ERR "alps.c: Failed to reconnect to IMPS emulation mode\n"); return -1; } - if (alps_tap_mode(psmouse, true)) { - printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n"); - return -1; - } + if (priv->i->flags & ALPS_EC_PROTO) { + if (alps_ec_mode(psmouse, true)) { + printk(KERN_ERR "alps.c: Failed to enable EC memory access mode\n"); + return -1; + } + /* Reset touchpad memory (disabling EC mode in the process) */ + if (alps_ec_write(psmouse, 0x0003, 0x01)) + return -1; + if (alps_ec_mode(psmouse, true)) { + printk(KERN_ERR "alps.c: Failed to re-enable EC memory access mode\n"); + return -1; + } + if (priv->i->flags & ALPS_IMPS) { + /* + * Enable IntelliMouse protocol. + * + * Other known bits at address 0005 are: + * 01 - Enable IntelliMouse protocol + * 02 - Disable hardware tapping entirely + * 04 - Disable corner tap for right-click + * 80 - Upper-left corner tap (default upper-right) + */ + if (alps_ec_write(psmouse, 0x0005, 0x01)) + return -1; + } + if (alps_ec_mode(psmouse, false)) + return -1; + } else { + if ((priv->i->flags & ALPS_PASS) && + alps_passthrough_mode(psmouse, true)) { + return -1; + } - if (alps_absolute_mode(psmouse)) { - printk(KERN_ERR "alps.c: Failed to enable absolute mode\n"); - return -1; - } + if (alps_tap_mode(psmouse, true)) { + printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n"); + return -1; + } - if ((priv->i->flags & ALPS_PASS) && - alps_passthrough_mode(psmouse, false)) { - return -1; - } + if (alps_absolute_mode(psmouse)) { + printk(KERN_ERR "alps.c: Failed to enable absolute mode\n"); + return -1; + } - /* ALPS needs stream mode, otherwise it won't report any data */ - if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) { - printk(KERN_ERR "alps.c: Failed to enable stream mode\n"); - return -1; + if ((priv->i->flags & ALPS_PASS) && + alps_passthrough_mode(psmouse, false)) { + return -1; + } + + /* ALPS needs stream mode, otherwise it won't report any data */ + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) { + printk(KERN_ERR "alps.c: Failed to enable stream mode\n"); + return -1; + } } return 0; @@ -648,7 +769,8 @@ static void alps_disconnect(struct psmouse *psmouse) psmouse_reset(psmouse); del_timer_sync(&priv->timer); - input_unregister_device(priv->dev2); + if (priv->dev2) + input_unregister_device(priv->dev2); kfree(priv); } @@ -668,8 +790,23 @@ int alps_init(struct psmouse *psmouse) psmouse->private = priv; + priv->i = NULL; if (alps_hw_init(psmouse, &version)) goto init_fail; + if (priv->i->flags & ALPS_IMPS) { + input_free_device(priv->dev2); + priv->dev2 = NULL; + + __set_bit(BTN_MIDDLE, dev1->keybit); + __set_bit(REL_WHEEL, dev1->relbit); + + psmouse->disconnect = alps_disconnect; + psmouse->reconnect = alps_reconnect; + psmouse->type = PSMOUSE_IMPS; + psmouse->pktsize = 4; + + return 0; + } dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 904ed8b..ba4a7f5 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -15,7 +15,7 @@ struct alps_model_info { unsigned char signature[3]; unsigned char byte0, mask0; - unsigned char flags; + unsigned int flags; }; struct alps_data { diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 9451e28..1fb42d4 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -632,8 +632,9 @@ static int psmouse_extensions(struct psmouse *psmouse, if (max_proto > PSMOUSE_IMEX) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); if (alps_detect(psmouse, set_properties) == 0) { + psmouse->type = PSMOUSE_NONE; if (!set_properties || alps_init(psmouse) == 0) - return PSMOUSE_ALPS; + return (psmouse->type != PSMOUSE_NONE) ? psmouse->type : PSMOUSE_ALPS; /* * Init failed, try basic relative protocols */ diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index e053bdd..207ca61 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -6,6 +6,8 @@ #define PSMOUSE_CMD_SETRES 0x10e8 #define PSMOUSE_CMD_GETINFO 0x03e9 #define PSMOUSE_CMD_SETSTREAM 0x00ea +#define PSMOUSE_CMD_RESET_WRAP 0x00ec +#define PSMOUSE_CMD_SETWRAP 0x00ee #define PSMOUSE_CMD_SETPOLL 0x00f0 #define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */ #define PSMOUSE_CMD_GETID 0x02f2