http://web.telia.com/~u89404340/patches/touchpad/2.6.11/ Subject: [PATCH 2/4] Enable hardware tapping for ALPS touchpads When hardware tapping is disabled on an ALPS touchpad, the touchpad generates exactly the same data for a single tap and a fast double tap. The effect is that the second tap in the double tap sequence is lost. To fix this problem, this patch enables hardware tapping and converts the resulting tap and gesture bits to standards finger pressure values (z), which is what mousedev.c and the userspace X driver expects. Signed-off-by: Peter Osterlund --- linux-petero/drivers/input/mouse/alps.c | 53 +++++++++++++++++++++++++------- linux-petero/drivers/input/mouse/alps.h | 4 ++ 2 files changed, 47 insertions(+), 10 deletions(-) diff -puN drivers/input/mouse/alps.c~alps-hwtaps drivers/input/mouse/alps.c --- linux/drivers/input/mouse/alps.c~alps-hwtaps 2005-03-06 18:26:39.000000000 +0100 +++ linux-petero/drivers/input/mouse/alps.c 2005-03-06 18:26:39.000000000 +0100 @@ -78,10 +78,12 @@ struct alps_model_info { static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) { + struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; struct input_dev *dev = &psmouse->dev; int x, y, z; int left = 0, right = 0, middle = 0; + int ges, fin; input_regs(dev, regs); @@ -123,6 +125,27 @@ static void alps_process_packet(struct p return; } + ges = packet[2] & 1; /* gesture bit */ + fin = packet[2] & 2; /* finger bit */ + + /* Convert hardware tap to a reasonable Z value */ + if (ges && !fin) + z = 40; + + /* + * A "tap and drag" operation is reported by the hardware as a transition + * from (!fin && ges) to (fin && ges). This should be translated to the + * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually. + */ + if (ges && fin && !priv->prev_fin) { + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_abs(dev, ABS_PRESSURE, 0); + input_report_key(dev, BTN_TOOL_FINGER, 0); + input_sync(dev); + } + priv->prev_fin = fin; + if (z > 30) input_report_key(dev, BTN_TOUCH, 1); if (z < 25) input_report_key(dev, BTN_TOUCH, 0); @@ -133,7 +156,6 @@ static void alps_process_packet(struct p input_report_abs(dev, ABS_PRESSURE, z); input_report_key(dev, BTN_TOOL_FINGER, z > 0); - left |= (packet[2] ) & 1; left |= (packet[3] ) & 1; right |= (packet[3] >> 1) & 1; if (packet[0] == 0xff) { @@ -335,7 +357,7 @@ static int alps_reconnect(struct psmouse return -1; if (param[0] & 0x04) - alps_tap_mode(psmouse, 0); + alps_tap_mode(psmouse, 1); if (alps_absolute_mode(psmouse)) { printk(KERN_ERR "alps.c: Failed to enable absolute mode\n"); @@ -351,40 +373,47 @@ static int alps_reconnect(struct psmouse static void alps_disconnect(struct psmouse *psmouse) { psmouse_reset(psmouse); + kfree(psmouse->private); } int alps_init(struct psmouse *psmouse) { + struct alps_data *priv; unsigned char param[4]; int model; + psmouse->private = priv = kmalloc(sizeof(struct alps_data), GFP_KERNEL); + if (!priv) + goto init_fail; + memset(priv, 0, sizeof(struct alps_data)); + if ((model = alps_get_model(psmouse)) < 0) - return -1; + goto init_fail; printk(KERN_INFO "ALPS Touchpad (%s) detected\n", model == ALPS_MODEL_GLIDEPOINT ? "Glidepoint" : "Dualpoint"); if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1)) - return -1; + goto init_fail; if (alps_get_status(psmouse, param)) { printk(KERN_ERR "alps.c: touchpad status report request failed\n"); - return -1; + goto init_fail; } if (param[0] & 0x04) { - printk(KERN_INFO " Disabling hardware tapping\n"); - if (alps_tap_mode(psmouse, 0)) - printk(KERN_WARNING "alps.c: Failed to disable hardware tapping\n"); + printk(KERN_INFO " Enabling hardware tapping\n"); + if (alps_tap_mode(psmouse, 1)) + printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n"); } if (alps_absolute_mode(psmouse)) { printk(KERN_ERR "alps.c: Failed to enable absolute mode\n"); - return -1; + goto init_fail; } if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0)) - return -1; + goto init_fail; psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL); psmouse->dev.relbit[LONG(REL_X)] |= BIT(REL_X); @@ -408,6 +437,10 @@ int alps_init(struct psmouse *psmouse) psmouse->pktsize = 6; return 0; + +init_fail: + kfree(priv); + return -1; } int alps_detect(struct psmouse *psmouse, int set_properties) diff -puN drivers/input/mouse/alps.h~alps-hwtaps drivers/input/mouse/alps.h --- linux/drivers/input/mouse/alps.h~alps-hwtaps 2005-03-06 18:26:39.000000000 +0100 +++ linux-petero/drivers/input/mouse/alps.h 2005-03-06 18:26:39.000000000 +0100 @@ -14,4 +14,8 @@ int alps_detect(struct psmouse *psmouse, int set_properties); int alps_init(struct psmouse *psmouse); +struct alps_data { + int prev_fin; /* Finger bit from previous packet */ +}; + #endif _ Only parse a "z == 127" packet as a relative Dualpoint stick packet if the touchpad actually is a Dualpoint device. The Glidepoint models don't have a stick, and can report z == 127 for a very wide finger. If such a packet is parsed as a stick packet, the mouse pointer will typically jump to one corner of the screen. Signed-off-by: Peter Osterlund --- linux-petero/drivers/input/mouse/alps.c | 20 ++++++++++---------- linux-petero/drivers/input/mouse/alps.h | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff -puN drivers/input/mouse/alps.c~alps-glidepoint-has-no-stick drivers/input/mouse/alps.c --- linux/drivers/input/mouse/alps.c~alps-glidepoint-has-no-stick 2005-03-06 18:26:40.000000000 +0100 +++ linux-petero/drivers/input/mouse/alps.c 2005-03-06 18:26:40.000000000 +0100 @@ -109,7 +109,8 @@ static void alps_process_packet(struct p y = (packet[4] & 0x7f) | ((packet[3] & 0x70)<<(7-4)); z = packet[5]; - if (z == 127) { /* DualPoint stick is relative, not absolute */ + if ((priv->model == ALPS_MODEL_DUALPOINT) && (z == 127)) { + /* DualPoint stick, relative packet */ if (x > 383) x = x - 768; if (y > 255) @@ -344,13 +345,13 @@ static int alps_tap_mode(struct psmouse static int alps_reconnect(struct psmouse *psmouse) { - int model; + struct alps_data *priv = psmouse->private; unsigned char param[4]; - if ((model = alps_get_model(psmouse)) < 0) + if ((priv->model = alps_get_model(psmouse)) < 0) return -1; - if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1)) + if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1)) return -1; if (alps_get_status(psmouse, param)) @@ -364,7 +365,7 @@ static int alps_reconnect(struct psmouse return -1; } - if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0)) + if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0)) return -1; return 0; @@ -380,20 +381,19 @@ int alps_init(struct psmouse *psmouse) { struct alps_data *priv; unsigned char param[4]; - int model; psmouse->private = priv = kmalloc(sizeof(struct alps_data), GFP_KERNEL); if (!priv) goto init_fail; memset(priv, 0, sizeof(struct alps_data)); - if ((model = alps_get_model(psmouse)) < 0) + if ((priv->model = alps_get_model(psmouse)) < 0) goto init_fail; printk(KERN_INFO "ALPS Touchpad (%s) detected\n", - model == ALPS_MODEL_GLIDEPOINT ? "Glidepoint" : "Dualpoint"); + priv->model == ALPS_MODEL_GLIDEPOINT ? "Glidepoint" : "Dualpoint"); - if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1)) + if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1)) goto init_fail; if (alps_get_status(psmouse, param)) { @@ -412,7 +412,7 @@ int alps_init(struct psmouse *psmouse) goto init_fail; } - if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0)) + if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0)) goto init_fail; psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL); diff -puN drivers/input/mouse/alps.h~alps-glidepoint-has-no-stick drivers/input/mouse/alps.h --- linux/drivers/input/mouse/alps.h~alps-glidepoint-has-no-stick 2005-03-06 18:26:40.000000000 +0100 +++ linux-petero/drivers/input/mouse/alps.h 2005-03-06 18:26:40.000000000 +0100 @@ -15,6 +15,7 @@ int alps_detect(struct psmouse *psmouse, int alps_init(struct psmouse *psmouse); struct alps_data { + int model; /* Glidepoint or Dualpoint */ int prev_fin; /* Finger bit from previous packet */ }; _ Subject: [PATCH 4/4] Add support for Synaptics touchpad scroll wheels Some Synaptics touchpads have a middle mouse button that also works as a scroll wheel. Scroll data is reported as packets with w == 2 and the scroll amount in byte 1, treated as a signed character. For some reason, the smallest possible wheel movement is reported as a scroll amount of 4 units. This amount is typically spread out over more than one packet, so the driver has to accumulate scroll delta values to correctly deal with this. Signed-off-by: Peter Osterlund --- linux-petero/drivers/input/mouse/synaptics.c | 28 +++++++++++++++++++++++++-- linux-petero/drivers/input/mouse/synaptics.h | 2 + 2 files changed, 28 insertions(+), 2 deletions(-) diff -puN drivers/input/mouse/synaptics.c~synaptics-scroll-wheel drivers/input/mouse/synaptics.c --- linux/drivers/input/mouse/synaptics.c~synaptics-scroll-wheel 2005-03-06 18:26:41.000000000 +0100 +++ linux-petero/drivers/input/mouse/synaptics.c 2005-03-06 18:26:41.000000000 +0100 @@ -322,8 +322,11 @@ static void synaptics_parse_hw_state(uns hw->left = (buf[0] & 0x01) ? 1 : 0; hw->right = (buf[0] & 0x02) ? 1 : 0; - if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) + if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; + if (hw->w == 2) + hw->scroll = (signed char)(buf[1]); + } if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; @@ -379,6 +382,26 @@ static void synaptics_process_packet(str synaptics_parse_hw_state(psmouse->packet, priv, &hw); + if (hw.scroll) { + priv->scroll += hw.scroll; + + while (priv->scroll >= 4) { + input_report_key(dev, BTN_BACK, !hw.down); + input_sync(dev); + input_report_key(dev, BTN_BACK, hw.down); + input_sync(dev); + priv->scroll -= 4; + } + while (priv->scroll <= -4) { + input_report_key(dev, BTN_FORWARD, !hw.up); + input_sync(dev); + input_report_key(dev, BTN_FORWARD, hw.up); + input_sync(dev); + priv->scroll += 4; + } + return; + } + if (hw.z > 0) { num_fingers = 1; finger_width = 5; @@ -528,7 +551,8 @@ static void set_input_params(struct inpu if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) set_bit(BTN_MIDDLE, dev->keybit); - if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { + if (SYN_CAP_FOUR_BUTTON(priv->capabilities) || + SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { set_bit(BTN_FORWARD, dev->keybit); set_bit(BTN_BACK, dev->keybit); } diff -puN drivers/input/mouse/synaptics.h~synaptics-scroll-wheel drivers/input/mouse/synaptics.h --- linux/drivers/input/mouse/synaptics.h~synaptics-scroll-wheel 2005-03-06 18:26:41.000000000 +0100 +++ linux-petero/drivers/input/mouse/synaptics.h 2005-03-06 18:26:41.000000000 +0100 @@ -92,6 +92,7 @@ struct synaptics_hw_state { unsigned int up:1; unsigned int down:1; unsigned char ext_buttons; + signed char scroll; }; struct synaptics_data { @@ -103,6 +104,7 @@ struct synaptics_data { unsigned char pkt_type; /* packet type - old, new, etc */ unsigned char mode; /* current mode byte */ + int scroll; }; #endif /* _SYNAPTICS_H */ _ This patch does the following: * Compensates for the lack of floating point arithmetic in the kernel by keeping track of remainders from the integer divisions. * Removes the xres/yres scaling so that you get the same speed in the X and Y directions even if your screen is not square. * Sets scale factors to make the speed for synaptics and alps equal to each other and equal to the synaptics speed from 2.6.10. Signed-off-by: Peter Osterlund --- linux-petero/drivers/input/mousedev.c | 28 +++++++++++++++++++--------- 1 files changed, 19 insertions(+), 9 deletions(-) diff -puN drivers/input/mousedev.c~mousedev-roundoff drivers/input/mousedev.c --- linux/drivers/input/mousedev.c~mousedev-roundoff 2005-03-06 18:26:41.000000000 +0100 +++ linux-petero/drivers/input/mousedev.c 2005-03-06 18:26:41.000000000 +0100 @@ -71,6 +71,7 @@ struct mousedev { struct mousedev_hw_data packet; unsigned int pkt_count; int old_x[4], old_y[4]; + int frac_dx, frac_dy; unsigned long touch; }; @@ -117,24 +118,31 @@ static struct mousedev mousedev_mix; static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value) { - int size; + int size, tmp; + enum { FRACTION_DENOM = 128 }; if (mousedev->touch) { + size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; + if (size == 0) size = 256 * 2; switch (code) { case ABS_X: - size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; - if (size == 0) size = xres; fx(0) = value; - if (mousedev->pkt_count >= 2) - mousedev->packet.dx = ((fx(0) - fx(1)) / 2 + (fx(1) - fx(2)) / 2) * xres / (size * 2); + if (mousedev->pkt_count >= 2) { + tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size; + tmp += mousedev->frac_dx; + mousedev->packet.dx = tmp / FRACTION_DENOM; + mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM; + } break; case ABS_Y: - size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y]; - if (size == 0) size = yres; fy(0) = value; - if (mousedev->pkt_count >= 2) - mousedev->packet.dy = -((fy(0) - fy(1)) / 2 + (fy(1) - fy(2)) / 2) * yres / (size * 2); + if (mousedev->pkt_count >= 2) { + tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size; + tmp += mousedev->frac_dy; + mousedev->packet.dy = tmp / FRACTION_DENOM; + mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM; + } break; } } @@ -268,6 +276,8 @@ static void mousedev_touchpad_touch(stru clear_bit(0, &mousedev_mix.packet.buttons); } mousedev->touch = mousedev->pkt_count = 0; + mousedev->frac_dx = 0; + mousedev->frac_dy = 0; } else if (!mousedev->touch) _ On Sun, Feb 13, 2005 at 03:43:54PM +0100, Peter Osterlund wrote: > > The "Version" number is 16-bit only, however the GETINFO can only > > transfer approximately 15 bits of information anyway, due to the > > limitations of the PS/2 protocols. > > I don't know exactly which bits to save, but the following patch handles > all models in the alps_model_info vector: I applied your patch, with a modification to really save all the possible version values: Signed-off-by: Peter Osterlund Signed-off-by: Vojtech Pavlik --- linux-petero/drivers/input/mouse/alps.c | 17 +++++++++++++---- linux-petero/drivers/input/mouse/psmouse.h | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff -puN drivers/input/mouse/alps.c~alps-version-info2 drivers/input/mouse/alps.c --- linux/drivers/input/mouse/alps.c~alps-version-info2 2005-03-06 18:26:42.000000000 +0100 +++ linux-petero/drivers/input/mouse/alps.c 2005-03-06 18:26:42.000000000 +0100 @@ -210,9 +210,10 @@ static psmouse_ret_t alps_process_byte(s return PSMOUSE_GOOD_DATA; } -int alps_get_model(struct psmouse *psmouse) +int alps_get_model(struct psmouse *psmouse, int *version)) { struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; unsigned char param[4]; int i; @@ -250,6 +251,9 @@ int alps_get_model(struct psmouse *psmou dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); + for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++); + *version = (param[0] << 8) | (i << 4) | (param[1] & 0x0f); + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) if (!memcmp(param, alps_model_data[i].signature, sizeof(alps_model_data[i].signature))) return alps_model_data[i].model; @@ -347,8 +351,9 @@ static int alps_reconnect(struct psmouse { struct alps_data *priv = psmouse->private; unsigned char param[4]; + int version; - if ((priv->model = alps_get_model(psmouse)) < 0) + if ((priv->model = alps_get_model(psmouse, &version)) < 0) return -1; if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1)) @@ -381,13 +386,14 @@ int alps_init(struct psmouse *psmouse) { struct alps_data *priv; unsigned char param[4]; + int version; psmouse->private = priv = kmalloc(sizeof(struct alps_data), GFP_KERNEL); if (!priv) goto init_fail; memset(priv, 0, sizeof(struct alps_data)); - if ((priv->model = alps_get_model(psmouse)) < 0) + if ((priv->model = alps_get_model(psmouse, &version)) < 0) goto init_fail; printk(KERN_INFO "ALPS Touchpad (%s) detected\n", @@ -445,12 +451,15 @@ init_fail: int alps_detect(struct psmouse *psmouse, int set_properties) { - if (alps_get_model(psmouse) < 0) + int version; + + if (alps_get_model(psmouse, &version) < 0) return -1; if (set_properties) { psmouse->vendor = "ALPS"; psmouse->name = "TouchPad"; + psmouse->model = version; } return 0; } diff -puN drivers/input/mouse/psmouse.h~alps-version-info2 drivers/input/mouse/psmouse.h --- linux/drivers/input/mouse/psmouse.h~alps-version-info2 2005-03-06 18:26:42.000000000 +0100 +++ linux-petero/drivers/input/mouse/psmouse.h 2005-03-06 18:26:42.000000000 +0100 @@ -44,7 +44,7 @@ struct psmouse { unsigned char pktcnt; unsigned char pktsize; unsigned char type; - unsigned char model; + unsigned int model; unsigned long last; unsigned long out_of_sync; enum psmouse_state state; _ Signed-off-by: Peter Osterlund --- linux-petero/drivers/input/mouse/alps.c | 2 +- 1 files changed, 1 insertion(+), 1 deletion(-) diff -puN drivers/input/mouse/alps.c~alps-abs-range-fix drivers/input/mouse/alps.c --- linux/drivers/input/mouse/alps.c~alps-abs-range-fix 2005-03-06 18:26:43.000000000 +0100 +++ linux-petero/drivers/input/mouse/alps.c 2005-03-06 18:26:43.000000000 +0100 @@ -429,7 +429,7 @@ int alps_init(struct psmouse *psmouse) psmouse->dev.evbit[LONG(EV_ABS)] |= BIT(EV_ABS); input_set_abs_params(&psmouse->dev, ABS_X, 0, 1023, 0, 0); - input_set_abs_params(&psmouse->dev, ABS_Y, 0, 1023, 0, 0); + input_set_abs_params(&psmouse->dev, ABS_Y, 0, 767, 0, 0); input_set_abs_params(&psmouse->dev, ABS_PRESSURE, 0, 127, 0, 0); psmouse->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH); _ ChangeSet@1.2039, 2005-02-23 23:06:26+01:00, vojtech@suse.cz input: Add support for less usual ALPS touchpads, rearrange code, separate touchpoint/passthrough into its own input device. Signed-off-by: Vojtech Pavlik --- linux-petero/drivers/input/mouse/alps.c | 264 +++++++++++++++++--------------- linux-petero/drivers/input/mouse/alps.h | 8 2 files changed, 148 insertions(+), 124 deletions(-) diff -puN drivers/input/mouse/alps.c~alps-more-protocols drivers/input/mouse/alps.c --- linux/drivers/input/mouse/alps.c~alps-more-protocols 2005-03-06 18:26:43.000000000 +0100 +++ linux-petero/drivers/input/mouse/alps.c 2005-03-06 18:26:43.000000000 +0100 @@ -4,6 +4,7 @@ * Copyright (c) 2003 Neil Brown * Copyright (c) 2003 Peter Osterlund * Copyright (c) 2004 Dmitry Torokhov + * Copyright (c) 2005 Vojtech Pavlik * * ALPS detection, tap switching and status querying info is taken from * tpconfig utility (by C. Scott Ananian and Bruce Kall). @@ -27,53 +28,52 @@ #define dbg(format, arg...) do {} while (0) #endif -#define ALPS_MODEL_GLIDEPOINT 1 -#define ALPS_MODEL_DUALPOINT 2 +#define ALPS_DUALPOINT 0x01 +#define ALPS_WHEEL 0x02 +#define ALPS_FW_BK 0x04 +#define ALPS_4BTN 0x08 +#define ALPS_OLDPROTO 0x10 struct alps_model_info { unsigned char signature[3]; - unsigned char model; + unsigned char flags; } alps_model_data[] = { -/* { { 0x33, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT }, */ - { { 0x53, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT }, - { { 0x53, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT }, - { { 0x63, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT }, - { { 0x63, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT }, - { { 0x73, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT }, - { { 0x73, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT }, - { { 0x63, 0x02, 0x28 }, ALPS_MODEL_GLIDEPOINT }, -/* { { 0x63, 0x02, 0x3c }, ALPS_MODEL_GLIDEPOINT }, */ -/* { { 0x63, 0x02, 0x50 }, ALPS_MODEL_GLIDEPOINT }, */ - { { 0x63, 0x02, 0x64 }, ALPS_MODEL_GLIDEPOINT }, - { { 0x20, 0x02, 0x0e }, ALPS_MODEL_DUALPOINT }, - { { 0x22, 0x02, 0x0a }, ALPS_MODEL_DUALPOINT }, - { { 0x22, 0x02, 0x14 }, ALPS_MODEL_DUALPOINT }, - { { 0x63, 0x03, 0xc8 }, ALPS_MODEL_DUALPOINT }, + { { 0x33, 0x02, 0x0a }, ALPS_OLDPROTO }, + { { 0x53, 0x02, 0x0a }, 0 }, + { { 0x53, 0x02, 0x14 }, 0 }, + { { 0x63, 0x02, 0x0a }, 0 }, + { { 0x63, 0x02, 0x14 }, 0 }, + { { 0x63, 0x02, 0x28 }, 0 }, + { { 0x63, 0x02, 0x3c }, ALPS_WHEEL }, + { { 0x63, 0x02, 0x50 }, ALPS_FW_BK }, + { { 0x63, 0x02, 0x64 }, 0 }, + { { 0x63, 0x03, 0xc8 }, 0 }, + { { 0x73, 0x02, 0x0a }, 0 }, + { { 0x73, 0x02, 0x14 }, 0 }, + { { 0x20, 0x02, 0x0e }, ALPS_DUALPOINT }, /* XXX */ + { { 0x22, 0x02, 0x0a }, ALPS_DUALPOINT }, + { { 0x22, 0x02, 0x14 }, ALPS_DUALPOINT }, }; /* - * ALPS abolute Mode - * byte 0: 1 1 1 1 1 mid0 rig0 lef0 + * XXX - this entry is suspicious. First byte has zero lower nibble, + * which is what a normal mouse would report. Also, the value 0x0e + * isn't valid per PS/2 spec. + */ + +/* + * ALPS abolute Mode - new format + * + * byte 0: 1 ? ? ? 1 ? ? ? * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 - * byte 2: 0 x10 x9 x8 x7 up1 fin ges - * byte 3: 0 y9 y8 y7 1 mid1 rig1 lef1 + * byte 2: 0 x10 x9 x8 x7 ? fin ges + * byte 3: 0 y9 y8 y7 1 M R L * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 * - * On a dualpoint, {mid,rig,lef}0 are the stick, 1 are the pad. - * We just 'or' them together for now. - * - * We used to send 'ges'tures as BTN_TOUCH but this made it impossible - * to disable tap events in the synaptics driver since the driver - * was unable to distinguish a gesture tap from an actual button click. - * A tap gesture now creates an emulated touch that the synaptics - * driver can interpret as a tap event, if MaxTapTime=0 and - * MaxTapMove=0 then the driver will ignore taps. - * - * The touchpad on an 'Acer Aspire' has 4 buttons: - * left,right,up,down. - * This device always sets {mid,rig,lef}0 to 1 and - * reflects left,right,down,up in lef1,rig1,mid1,up1. + * ?'s can have different meanings on different models, + * such as wheel rotation, extra buttons, stick buttons + * on a dualpoint, etc. */ static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) @@ -81,57 +81,60 @@ static void alps_process_packet(struct p struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; struct input_dev *dev = &psmouse->dev; - int x, y, z; - int left = 0, right = 0, middle = 0; - int ges, fin; + struct input_dev *dev2 = &priv->dev2; + int x, y, z, ges, fin, left, right, middle; input_regs(dev, regs); if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */ - x = packet[1]; - if (packet[0] & 0x10) - x = x - 256; - y = packet[2]; - if (packet[0] & 0x20) - y = y - 256; - left = (packet[0] ) & 1; - right = (packet[0] >> 1) & 1; - - input_report_rel(dev, REL_X, x); - input_report_rel(dev, REL_Y, -y); - input_report_key(dev, BTN_A, left); - input_report_key(dev, BTN_B, right); - input_sync(dev); + input_report_key(dev2, BTN_LEFT, packet[0] & 1); + input_report_key(dev2, BTN_RIGHT, packet[0] & 2); + input_report_key(dev2, BTN_MIDDLE, packet[0] & 4); + input_report_rel(dev2, REL_X, packet[1] ? + (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); + input_report_rel(dev2, REL_Y, packet[2] ? + (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); + input_sync(dev2); return; } - x = (packet[1] & 0x7f) | ((packet[2] & 0x78)<<(7-3)); - y = (packet[4] & 0x7f) | ((packet[3] & 0x70)<<(7-4)); - z = packet[5]; - - if ((priv->model == ALPS_MODEL_DUALPOINT) && (z == 127)) { - /* DualPoint stick, relative packet */ - if (x > 383) - x = x - 768; - if (y > 255) - y = y - 512; - left = packet[3] & 1; - right = (packet[3] >> 1) & 1; - - input_report_rel(dev, REL_X, x); - input_report_rel(dev, REL_Y, -y); - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); - input_sync(dev); - return; + if (priv->flags & ALPS_OLDPROTO) { + left = packet[2] & 0x08; + right = packet[2] & 0x10; + middle = 0; + x = (packet[1] & 0x7f) | ((packet[0] & 0x07) << 7); + y = (packet[4] & 0x7f) | ((packet[3] & 0x07) << 7); + z = packet[5]; + } else { + left = packet[3] & 1; + right = packet[3] & 2; + middle = packet[3] & 4; + x = (packet[1] & 0x7f) | ((packet[2] & 0x78) << (7 - 3)); + y = (packet[4] & 0x7f) | ((packet[3] & 0x70) << (7 - 4)); + z = packet[5]; + } + + ges = packet[2] & 1; + fin = packet[2] & 2; + + /* Dualpoint has stick buttons in byte 0 */ + if (priv->flags & ALPS_DUALPOINT) { + + input_report_key(dev2, BTN_LEFT, packet[0] & 1); + input_report_key(dev2, BTN_MIDDLE, (packet[0] >> 2) & 1); + input_report_key(dev2, BTN_RIGHT, (packet[0] >> 1) & 1); + + /* Relative movement packet */ + if (z == 127) { + input_report_rel(dev2, REL_X, (x > 383 ? x : (x - 768))); + input_report_rel(dev2, REL_Y, -(y > 255 ? y : (x - 512))); + input_sync(dev2); + return; + } } - ges = packet[2] & 1; /* gesture bit */ - fin = packet[2] & 2; /* finger bit */ - /* Convert hardware tap to a reasonable Z value */ - if (ges && !fin) - z = 40; + if (ges && !fin) z = 40; /* * A "tap and drag" operation is reported by the hardware as a transition @@ -154,32 +157,22 @@ static void alps_process_packet(struct p input_report_abs(dev, ABS_X, x); input_report_abs(dev, ABS_Y, y); } + input_report_abs(dev, ABS_PRESSURE, z); input_report_key(dev, BTN_TOOL_FINGER, z > 0); - left |= (packet[3] ) & 1; - right |= (packet[3] >> 1) & 1; - if (packet[0] == 0xff) { - int back = (packet[3] >> 2) & 1; - int forward = (packet[2] >> 2) & 1; - if (back && forward) { - middle = 1; - back = 0; - forward = 0; - } - input_report_key(dev, BTN_BACK, back); - input_report_key(dev, BTN_FORWARD, forward); - } else { - left |= (packet[0] ) & 1; - right |= (packet[0] >> 1) & 1; - middle |= (packet[0] >> 2) & 1; - middle |= (packet[3] >> 2) & 1; - } - input_report_key(dev, BTN_LEFT, left); input_report_key(dev, BTN_RIGHT, right); input_report_key(dev, BTN_MIDDLE, middle); + if (priv->flags & ALPS_WHEEL) + input_report_rel(dev, REL_WHEEL, ((packet[0] >> 4) & 0x07) | ((packet[2] >> 2) & 0x08)); + + if (priv->flags & ALPS_FW_BK) { + input_report_key(dev, BTN_FORWARD, packet[0] & 0x10); + input_report_key(dev, BTN_BACK, packet[2] & 0x04); + } + input_sync(dev); } @@ -194,7 +187,7 @@ static psmouse_ret_t alps_process_byte(s } /* ALPS absolute mode packets start with 0b11111mrl */ - if ((psmouse->packet[0] & 0xf8) != 0xf8) + if ((psmouse->packet[0] & 0x88) != 0x88) return PSMOUSE_BAD_DATA; /* Bytes 2 - 6 should have 0 in the highest bit */ @@ -210,7 +203,7 @@ static psmouse_ret_t alps_process_byte(s return PSMOUSE_GOOD_DATA; } -int alps_get_model(struct psmouse *psmouse, int *version)) +int alps_get_model(struct psmouse *psmouse, int *version) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; @@ -219,7 +212,7 @@ int alps_get_model(struct psmouse *psmou /* * First try "E6 report". - * ALPS should return 0x00,0x00,0x0a or 0x00,0x00,0x64 + * ALPS should return 0,0,10 or 0,0,100 */ param[0] = 0; if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || @@ -234,10 +227,13 @@ int alps_get_model(struct psmouse *psmou dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); - if (param[0] != 0x00 || param[1] != 0x00 || (param[2] != 0x0a && param[2] != 0x64)) + if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100)) return -1; - /* Now try "E7 report". ALPS should return 0x33 in byte 1 */ + /* + * Now try "E7 report". Allowed responses are in + * alps_model_data[].signature + */ param[0] = 0; if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || @@ -252,11 +248,11 @@ int alps_get_model(struct psmouse *psmou dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++); - *version = (param[0] << 8) | (i << 4) | (param[1] & 0x0f); + *version = (param[0] << 8) | (param[1] << 4) | i; for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) if (!memcmp(param, alps_model_data[i].signature, sizeof(alps_model_data[i].signature))) - return alps_model_data[i].model; + return alps_model_data[i].flags; return -1; } @@ -353,10 +349,10 @@ static int alps_reconnect(struct psmouse unsigned char param[4]; int version; - if ((priv->model = alps_get_model(psmouse, &version)) < 0) + if ((priv->flags = alps_get_model(psmouse, &version)) < 0) return -1; - if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1)) + if (priv->flags & ALPS_DUALPOINT && alps_passthrough_mode(psmouse, 1)) return -1; if (alps_get_status(psmouse, param)) @@ -370,7 +366,7 @@ static int alps_reconnect(struct psmouse return -1; } - if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0)) + if (priv->flags == ALPS_DUALPOINT && alps_passthrough_mode(psmouse, 0)) return -1; return 0; @@ -378,8 +374,10 @@ static int alps_reconnect(struct psmouse static void alps_disconnect(struct psmouse *psmouse) { + struct alps_data *priv = psmouse->private; psmouse_reset(psmouse); - kfree(psmouse->private); + input_unregister_device(&priv->dev2); + kfree(priv); } int alps_init(struct psmouse *psmouse) @@ -393,13 +391,13 @@ int alps_init(struct psmouse *psmouse) goto init_fail; memset(priv, 0, sizeof(struct alps_data)); - if ((priv->model = alps_get_model(psmouse, &version)) < 0) + if ((priv->flags = alps_get_model(psmouse, &version)) < 0) goto init_fail; printk(KERN_INFO "ALPS Touchpad (%s) detected\n", - priv->model == ALPS_MODEL_GLIDEPOINT ? "Glidepoint" : "Dualpoint"); + (priv->flags & ALPS_DUALPOINT) ? "DualPoint" : "GlidePoint"); - if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1)) + if ((priv->flags & ALPS_DUALPOINT) && alps_passthrough_mode(psmouse, 1)) goto init_fail; if (alps_get_status(psmouse, param)) { @@ -418,24 +416,42 @@ int alps_init(struct psmouse *psmouse) goto init_fail; } - if (priv->model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0)) + if ((priv->flags & ALPS_DUALPOINT) && alps_passthrough_mode(psmouse, 0)) goto init_fail; - psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL); - psmouse->dev.relbit[LONG(REL_X)] |= BIT(REL_X); - psmouse->dev.relbit[LONG(REL_Y)] |= BIT(REL_Y); - psmouse->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A); - psmouse->dev.keybit[LONG(BTN_B)] |= BIT(BTN_B); + psmouse->dev.evbit[LONG(EV_KEY)] |= BIT(EV_KEY); + psmouse->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH); + psmouse->dev.keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER); + psmouse->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); psmouse->dev.evbit[LONG(EV_ABS)] |= BIT(EV_ABS); input_set_abs_params(&psmouse->dev, ABS_X, 0, 1023, 0, 0); input_set_abs_params(&psmouse->dev, ABS_Y, 0, 767, 0, 0); input_set_abs_params(&psmouse->dev, ABS_PRESSURE, 0, 127, 0, 0); - psmouse->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH); - psmouse->dev.keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER); - psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD); - psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK); + if (priv->flags & ALPS_WHEEL) { + psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL); + psmouse->dev.relbit[LONG(REL_WHEEL)] |= BIT(REL_WHEEL); + } + + if (priv->flags & ALPS_FW_BK) { + psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD); + psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK); + } + + sprintf(priv->phys, "%s/input1", psmouse->ps2dev.serio->phys); + priv->dev2.phys = priv->phys; + priv->dev2.name = (priv->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; + priv->dev2.id.bustype = BUS_I8042; + priv->dev2.id.vendor = 0x0002; + priv->dev2.id.product = PSMOUSE_ALPS; + priv->dev2.id.version = 0x0000; + + priv->dev2.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + priv->dev2.relbit[LONG(REL_X)] |= BIT(REL_X) | BIT(REL_Y); + priv->dev2.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + + input_register_device(&priv->dev2); psmouse->protocol_handler = alps_process_byte; psmouse->disconnect = alps_disconnect; @@ -452,13 +468,17 @@ init_fail: int alps_detect(struct psmouse *psmouse, int set_properties) { int version; + int model; - if (alps_get_model(psmouse, &version) < 0) + if ((model = alps_get_model(psmouse, &version)) < 0) return -1; if (set_properties) { psmouse->vendor = "ALPS"; - psmouse->name = "TouchPad"; + if (model & ALPS_DUALPOINT) + psmouse->name = "DualPoint TouchPad"; + else + psmouse->name = "GlidePoint"; psmouse->model = version; } return 0; diff -puN drivers/input/mouse/alps.h~alps-more-protocols drivers/input/mouse/alps.h --- linux/drivers/input/mouse/alps.h~alps-more-protocols 2005-03-06 18:26:43.000000000 +0100 +++ linux-petero/drivers/input/mouse/alps.h 2005-03-06 18:26:43.000000000 +0100 @@ -2,6 +2,7 @@ * ALPS touchpad PS/2 mouse driver * * Copyright (c) 2003 Peter Osterlund + * Copyright (c) 2005 Vojtech Pavlik * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -15,8 +16,11 @@ int alps_detect(struct psmouse *psmouse, int alps_init(struct psmouse *psmouse); struct alps_data { - int model; /* Glidepoint or Dualpoint */ - int prev_fin; /* Finger bit from previous packet */ + struct input_dev dev2; /* Relative device */ + char name[32]; /* Name */ + char phys[32]; /* Phys */ + int flags; /* Protocol details */ + int prev_fin; /* Finger bit from previous packet */ }; #endif _