Quantcast

kern/159063: [patch]ALPS touchpad recognized as PS mouse (no scrl, nor r-click)

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

kern/159063: [patch]ALPS touchpad recognized as PS mouse (no scrl, nor r-click)

Denis

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