Date: Wed, 20 Jul 2011 16:19:08 GMT From: Denis <denis5594@gmail.com> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/159063: [patch]ALPS touchpad recognized as PS mouse (no scrl, nor r-click) Message-ID: <201107201619.p6KGJ8CK058443@red.freebsd.org> Resent-Message-ID: <201107201620.p6KGKCNP059496@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 159063 >Category: kern >Synopsis: [patch]ALPS touchpad recognized as PS mouse (no scrl, nor r-click) >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Wed Jul 20 16:20:11 UTC 2011 >Closed-Date: >Last-Modified: >Originator: Denis >Release: 8.2-RELEASE >Organization: -- >Environment: FreeBSD nop.dyndns.org 8.2-RELEASE FreeBSD 8.2-RELEASE #0: Fri Feb 18 02:24:46 UTC 2011 root@almeida.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC i386 >Description: Newer ALPS touchpads require special sequence to turn into "touchpad" mode, according to new protocol they using. Unfortunately, this specification in proprietary, and not well-known. However, there is a patch that can make touchpad act like ImPS/2 intellimouse, thus allowing to use horizontal scrolling and right click. Bad news - its a patch for linux kernel, not freebsd. I've tried to figure out how psmouse from linux and psm working, and it seems like this patch can be sucsessfully ported to freebsd. >How-To-Repeat: Any new ALPS Glidepoint touchpad will work that way >Fix: This patch gives at least ability to scroll or right-click Patch attached with submission follows: 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 >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201107201619.p6KGJ8CK058443>