>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     [email protected]:/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, &param, 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:
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "[email protected]"

Reply via email to