Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S261487AbTISKdr (ORCPT ); Fri, 19 Sep 2003 06:33:47 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S261507AbTISKcf (ORCPT ); Fri, 19 Sep 2003 06:32:35 -0400 Received: from twilight.ucw.cz ([81.30.235.3]:21390 "EHLO twilight.ucw.cz") by vger.kernel.org with ESMTP id S261487AbTISK1E convert rfc822-to-8bit (ORCPT ); Fri, 19 Sep 2003 06:27:04 -0400 Subject: [PATCH 4/11] input: Big Synaptics pad update In-Reply-To: <10639672012246@twilight.ucw.cz> X-Mailer: gregkh_patchbomb_levon_offspring Date: Fri, 19 Sep 2003 12:26:41 +0200 Message-Id: <1063967201965@twilight.ucw.cz> Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII To: torvalds@transmeta.com, linux-kernel@vger.kernel.org Content-Transfer-Encoding: 7BIT From: Vojtech Pavlik Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 30090 Lines: 990 You can pull this changeset from: bk://kernel.bkbits.net/vojtech/input =================================================================== ChangeSet@1.1342, 2003-09-19 01:17:37-07:00, petero2@telia.com Input: Big Synaptics update: Restore synaptics pad mode on module unload. Support Synaptics touchpads with multiple buttons. Make Synaptics touchpad support optional. Add passthrough support for Synaptics touchpads. [Dmitry] Add support for old Synaptics protocol. Set mode byte correctly for old Synaptics pads. Fix multibutton support of Synaptics pads. Documentation/kernel-parameters.txt | 4 drivers/input/mouse/Kconfig | 20 + drivers/input/mouse/psmouse-base.c | 69 +++++- drivers/input/mouse/psmouse.h | 22 +- drivers/input/mouse/synaptics.c | 364 ++++++++++++++++++++++++++++-------- drivers/input/mouse/synaptics.h | 27 ++ drivers/input/serio/serio.c | 25 ++ include/linux/serio.h | 3 8 files changed, 424 insertions(+), 110 deletions(-) =================================================================== diff -Nru a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt --- a/Documentation/kernel-parameters.txt Fri Sep 19 12:16:32 2003 +++ b/Documentation/kernel-parameters.txt Fri Sep 19 12:16:32 2003 @@ -788,6 +788,10 @@ psmouse_noext [HW,MOUSE] Disable probing for PS2 mouse protocol extensions + psmouse_resetafter= + [HW,MOUSE] Try to reset Synaptics Touchpad after so many + bad packets (0 = never). + pss= [HW,OSS] Personal Sound System (ECHO ESC614) Format: ,,,,, diff -Nru a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig --- a/drivers/input/mouse/Kconfig Fri Sep 19 12:16:32 2003 +++ b/drivers/input/mouse/Kconfig Fri Sep 19 12:16:32 2003 @@ -19,9 +19,7 @@ Say Y here if you have a PS/2 mouse connected to your system. This includes the standard 2 or 3-button PS/2 mouse, as well as PS/2 mice with wheels and extra buttons, Microsoft, Logitech or Genius - compatible. Support for Synaptics TouchPads is also included. - For Synaptics TouchPad support in XFree86 you'll need this XFree86 - driver: http://w1.894.telia.com/~u89404340/touchpad/index.html + compatible. If unsure, say Y. @@ -29,6 +27,22 @@ inserted in and removed from the running kernel whenever you want). The module will be called psmouse. If you want to compile it as a module, say M here and read . + +config MOUSE_PS2_SYNAPTICS + bool "Synaptics TouchPad" + default n + depends on INPUT && INPUT_MOUSE && SERIO && MOUSE_PS2 + ---help--- + Say Y here if you have a Synaptics TouchPad connected to your system. + This touchpad is found on many modern laptop computers. + + Note that you also need a user space driver to interpret the data + generated by the kernel. A compatible driver for XFree86 is available + from http://w1.894.telia.com/~u89404340/touchpad/index.html + + The gpm program is not yet able to interpret the data from this + driver, so if you need to use the touchpad in the console, you have to + say N for now. config MOUSE_SERIAL tristate "Serial mouse" diff -Nru a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c --- a/drivers/input/mouse/psmouse-base.c Fri Sep 19 12:16:32 2003 +++ b/drivers/input/mouse/psmouse-base.c Fri Sep 19 12:16:32 2003 @@ -29,6 +29,8 @@ MODULE_PARM_DESC(psmouse_resolution, "Resolution, in dpi."); MODULE_PARM(psmouse_smartscroll, "i"); MODULE_PARM_DESC(psmouse_smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); +MODULE_PARM(psmouse_resetafter, "i"); +MODULE_PARM_DESC(psmouse_resetafter, "Reset Synaptics Touchpad after so many bad packets (0 = never)."); MODULE_LICENSE("GPL"); #define PSMOUSE_LOGITECH_SMARTSCROLL 1 @@ -36,11 +38,12 @@ static int psmouse_noext; int psmouse_resolution; int psmouse_smartscroll = PSMOUSE_LOGITECH_SMARTSCROLL; +unsigned int psmouse_resetafter; static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2", "Synaptics"}; /* - * psmouse_process_packet() anlyzes the PS/2 mouse packet contents and + * psmouse_process_packet() analyzes the PS/2 mouse packet contents and * reports relevant events to the input module. */ @@ -108,6 +111,9 @@ { struct psmouse *psmouse = serio->private; + if (psmouse->state == PSMOUSE_IGNORE) + goto out; + if (psmouse->acking) { switch (data) { case PSMOUSE_RET_ACK: @@ -132,31 +138,46 @@ } if (psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { - printk(KERN_WARNING "psmouse.c: Lost synchronization, throwing %d bytes away.\n", psmouse->pktcnt); + printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", + psmouse->name, psmouse->phys, psmouse->pktcnt); psmouse->pktcnt = 0; } psmouse->last = jiffies; psmouse->packet[psmouse->pktcnt++] = data; - if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) { - psmouse_process_packet(psmouse, regs); - psmouse->pktcnt = 0; - goto out; + if (psmouse->packet[0] == PSMOUSE_RET_BAT) { + if (psmouse->pktcnt == 1) + goto out; + + if (psmouse->pktcnt == 2) { + if (psmouse->packet[1] == PSMOUSE_RET_ID) { + psmouse->state = PSMOUSE_IGNORE; + serio_rescan(serio); + goto out; + } + if (psmouse->type == PSMOUSE_SYNAPTICS) { + /* neither 0xAA nor 0x00 are valid first bytes + * for a packet in absolute mode + */ + psmouse->pktcnt = 0; + goto out; + } + } } - if (psmouse->pktcnt == 1 && psmouse->type == PSMOUSE_SYNAPTICS) { + if (psmouse->type == PSMOUSE_SYNAPTICS) { /* * The synaptics driver has its own resync logic, * so it needs to receive all bytes one at a time. */ synaptics_process_byte(psmouse, regs); - psmouse->pktcnt = 0; goto out; } - if (psmouse->pktcnt == 1 && psmouse->packet[0] == PSMOUSE_RET_BAT) { - serio_rescan(serio); + if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) { + psmouse_process_packet(psmouse, regs); + psmouse->pktcnt = 0; goto out; } out: @@ -227,7 +248,7 @@ for (i = 0; i < receive; i++) param[i] = psmouse->cmdbuf[(receive - 1) - i]; - if (psmouse->cmdcnt) + if (psmouse->cmdcnt) return (psmouse->cmdcnt = 0) - 1; return 0; @@ -450,14 +471,18 @@ */ psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM); +} /* - * Last, we enable the mouse so that we get reports from it. + * psmouse_activate() enables the mouse so that we get motion reports from it. */ +static void psmouse_activate(struct psmouse *psmouse) +{ if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); + psmouse->state = PSMOUSE_ACTIVATED; } /* @@ -478,8 +503,9 @@ static void psmouse_disconnect(struct serio *serio) { struct psmouse *psmouse = serio->private; - if (psmouse->type == PSMOUSE_SYNAPTICS) - synaptics_disconnect(psmouse); + + psmouse->state = PSMOUSE_IGNORE; + synaptics_disconnect(psmouse); input_unregister_device(&psmouse->dev); serio_close(serio); kfree(psmouse); @@ -494,7 +520,8 @@ { struct psmouse *psmouse; - if ((serio->type & SERIO_TYPE) != SERIO_8042) + if ((serio->type & SERIO_TYPE) != SERIO_8042 && + (serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU) return; if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) @@ -507,6 +534,7 @@ psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); + psmouse->state = PSMOUSE_NEW_DEVICE; psmouse->serio = serio; psmouse->dev.private = psmouse; @@ -540,6 +568,10 @@ printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); psmouse_initialize(psmouse); + + synaptics_pt_init(psmouse); + + psmouse_activate(psmouse); } static struct serio_dev psmouse_dev = { @@ -568,9 +600,16 @@ return 1; } +static int __init psmouse_resetafter_setup(char *str) +{ + get_option(&str, &psmouse_resetafter); + return 1; +} + __setup("psmouse_noext", psmouse_noext_setup); __setup("psmouse_resolution=", psmouse_resolution_setup); __setup("psmouse_smartscroll=", psmouse_smartscroll_setup); +__setup("psmouse_resetafter=", psmouse_resetafter_setup); #endif diff -Nru a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h --- a/drivers/input/mouse/psmouse.h Fri Sep 19 12:16:32 2003 +++ b/drivers/input/mouse/psmouse.h Fri Sep 19 12:16:32 2003 @@ -13,9 +13,15 @@ #define PSMOUSE_CMD_RESET_BAT 0x02ff #define PSMOUSE_RET_BAT 0xaa +#define PSMOUSE_RET_ID 0x00 #define PSMOUSE_RET_ACK 0xfa #define PSMOUSE_RET_NAK 0xfe +/* psmouse states */ +#define PSMOUSE_NEW_DEVICE 0 +#define PSMOUSE_ACTIVATED 1 +#define PSMOUSE_IGNORE 2 + struct psmouse { void *private; struct input_dev dev; @@ -29,6 +35,7 @@ unsigned char type; unsigned char model; unsigned long last; + unsigned char state; char acking; volatile char ack; char error; @@ -36,16 +43,17 @@ char phys[32]; }; -#define PSMOUSE_PS2 1 -#define PSMOUSE_PS2PP 2 -#define PSMOUSE_PS2TPP 3 -#define PSMOUSE_GENPS 4 -#define PSMOUSE_IMPS 5 -#define PSMOUSE_IMEX 6 -#define PSMOUSE_SYNAPTICS 7 +#define PSMOUSE_PS2 1 +#define PSMOUSE_PS2PP 2 +#define PSMOUSE_PS2TPP 3 +#define PSMOUSE_GENPS 4 +#define PSMOUSE_IMPS 5 +#define PSMOUSE_IMEX 6 +#define PSMOUSE_SYNAPTICS 7 int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command); extern int psmouse_smartscroll; +extern unsigned int psmouse_resetafter; #endif /* _PSMOUSE_H */ diff -Nru a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c --- a/drivers/input/mouse/synaptics.c Fri Sep 19 12:16:32 2003 +++ b/drivers/input/mouse/synaptics.c Fri Sep 19 12:16:32 2003 @@ -1,6 +1,9 @@ /* * Synaptics TouchPad PS/2 mouse driver * + * 2003 Dmitry Torokhov + * Added support for pass-through port + * * 2003 Peter Osterlund * Ported to 2.5 input device infrastructure. * @@ -21,6 +24,7 @@ #include #include +#include #include "psmouse.h" #include "synaptics.h" @@ -71,7 +75,7 @@ if (synaptics_special_cmd(psmouse, mode)) return -1; - param[0] = 0x14; + param[0] = SYN_PS_SET_MODE2; if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE)) return -1; return 0; @@ -83,7 +87,7 @@ if (psmouse_command(psmouse, r, PSMOUSE_CMD_RESET_BAT)) return -1; - if (r[0] == 0xAA && r[1] == 0x00) + if (r[0] == PSMOUSE_RET_BAT && r[1] == PSMOUSE_RET_ID) return 0; return -1; } @@ -106,16 +110,25 @@ * Read the capability-bits from the touchpad * see also the SYN_CAP_* macros */ -static int synaptics_capability(struct psmouse *psmouse, unsigned long int *capability) +static int synaptics_capability(struct psmouse *psmouse, unsigned long int *capability, unsigned long int *ext_cap) { unsigned char cap[3]; if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) return -1; *capability = (cap[0]<<16) | (cap[1]<<8) | cap[2]; - if (SYN_CAP_VALID(*capability)) - return 0; - return -1; + *ext_cap = 0; + if (!SYN_CAP_VALID(*capability)) + return -1; + + if (SYN_EXT_CAP_REQUESTS(*capability)) { + if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { + printk(KERN_ERR "Synaptics claims to have extended capabilities," + " but I'm not able to read them."); + } else + *ext_cap = (cap[0]<<16) | (cap[1]<<8) | cap[2]; + } + return 0; } /* @@ -134,19 +147,11 @@ return -1; } -static int synaptics_enable_device(struct psmouse *psmouse) -{ - if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) - return -1; - return 0; -} - static void print_ident(struct synaptics_data *priv) { printk(KERN_INFO "Synaptics Touchpad, model: %ld\n", SYN_ID_MODEL(priv->identity)); printk(KERN_INFO " Firmware: %ld.%ld\n", SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity)); - if (SYN_MODEL_ROT180(priv->model_id)) printk(KERN_INFO " 180 degree mounted touchpad\n"); if (SYN_MODEL_PORTRAIT(priv->model_id)) @@ -159,12 +164,17 @@ if (SYN_CAP_EXTENDED(priv->capabilities)) { printk(KERN_INFO " Touchpad has extended capability bits\n"); - if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + printk(KERN_INFO " -> %d multi-buttons, i.e. besides standard buttons\n", + (int)(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))); + else if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) printk(KERN_INFO " -> four buttons\n"); if (SYN_CAP_MULTIFINGER(priv->capabilities)) printk(KERN_INFO " -> multifinger detection\n"); if (SYN_CAP_PALMDETECT(priv->capabilities)) printk(KERN_INFO " -> palm detection\n"); + if (SYN_CAP_PASS_THROUGH(priv->capabilities)) + printk(KERN_INFO " -> pass-through port\n"); } } @@ -172,6 +182,7 @@ { struct synaptics_data *priv = psmouse->private; int retries = 0; + int mode; while ((retries++ < 3) && synaptics_reset(psmouse)) printk(KERN_ERR "synaptics reset failed\n"); @@ -180,17 +191,107 @@ return -1; if (synaptics_model_id(psmouse, &priv->model_id)) return -1; - if (synaptics_capability(psmouse, &priv->capabilities)) + if (synaptics_capability(psmouse, &priv->capabilities, &priv->ext_cap)) return -1; - if (synaptics_set_mode(psmouse, (SYN_BIT_ABSOLUTE_MODE | - SYN_BIT_HIGH_RATE | - SYN_BIT_DISABLE_GESTURE | - SYN_BIT_W_MODE))) + + mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE; + if (SYN_ID_MAJOR(priv->identity) >= 4) + mode |= SYN_BIT_DISABLE_GESTURE; + if (SYN_CAP_EXTENDED(priv->capabilities)) + mode |= SYN_BIT_W_MODE; + if (synaptics_set_mode(psmouse, mode)) return -1; - synaptics_enable_device(psmouse); + return 0; +} - print_ident(priv); +/***************************************************************************** + * Synaptics pass-through PS/2 port support + ****************************************************************************/ +static int synaptics_pt_open(struct serio *port) +{ + return 0; +} + +static void synaptics_pt_close(struct serio *port) +{ +} + +static int synaptics_pt_write(struct serio *port, unsigned char c) +{ + struct psmouse *parent = port->driver; + char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ + + if (synaptics_special_cmd(parent, c)) + return -1; + if (psmouse_command(parent, &rate_param, PSMOUSE_CMD_SETRATE)) + return -1; + return 0; +} + +static inline int synaptics_is_pt_packet(unsigned char *buf) +{ + return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; +} + +static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) +{ + struct psmouse *child = ptport->private; + + if (child) { + if (child->state == PSMOUSE_ACTIVATED) { + serio_interrupt(ptport, packet[1], 0, NULL); + serio_interrupt(ptport, packet[4], 0, NULL); + serio_interrupt(ptport, packet[5], 0, NULL); + if (child->type >= PSMOUSE_GENPS) + serio_interrupt(ptport, packet[2], 0, NULL); + } else if (child->state != PSMOUSE_IGNORE) { + serio_interrupt(ptport, packet[1], 0, NULL); + } + } +} + +int synaptics_pt_init(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + struct serio *port; + struct psmouse *child; + + if (psmouse->type != PSMOUSE_SYNAPTICS) + return -1; + if (!SYN_CAP_EXTENDED(priv->capabilities)) + return -1; + if (!SYN_CAP_PASS_THROUGH(priv->capabilities)) + return -1; + + priv->ptport = port = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!port) { + printk(KERN_ERR "synaptics: not enough memory to allocate serio port\n"); + return -1; + } + + memset(port, 0, sizeof(struct serio)); + port->type = SERIO_PS_PSTHRU; + port->name = "Synaptics pass-through"; + port->phys = "synaptics-pt/serio0"; + port->write = synaptics_pt_write; + port->open = synaptics_pt_open; + port->close = synaptics_pt_close; + port->driver = psmouse; + + printk(KERN_INFO "serio: %s port at %s\n", port->name, psmouse->phys); + serio_register_slave_port(port); + + /* adjust the touchpad to child's choice of protocol */ + child = port->private; + if (child && child->type >= PSMOUSE_GENPS) { + if (synaptics_set_mode(psmouse, (SYN_BIT_ABSOLUTE_MODE | + SYN_BIT_HIGH_RATE | + SYN_BIT_DISABLE_GESTURE | + SYN_BIT_FOUR_BYTE_CLIENT | + SYN_BIT_W_MODE))) + printk(KERN_INFO "synaptics: failed to enable 4-byte guest protocol\n"); + } return 0; } @@ -213,18 +314,23 @@ { struct synaptics_data *priv; +#ifndef CONFIG_MOUSE_PS2_SYNAPTICS + return -1; +#endif psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) return -1; memset(priv, 0, sizeof(struct synaptics_data)); - priv->inSync = 1; + priv->out_of_sync = 0; if (query_hardware(psmouse)) { printk(KERN_ERR "Unable to query/initialize Synaptics hardware.\n"); goto init_fail; } + print_ident(priv); + /* * The x/y limits are taken from the Synaptics TouchPad interfacing Guide, * which says that they should be valid regardless of the actual size of @@ -243,7 +349,24 @@ set_bit(BTN_RIGHT, psmouse->dev.keybit); set_bit(BTN_FORWARD, psmouse->dev.keybit); set_bit(BTN_BACK, psmouse->dev.keybit); - + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { + default: + printk(KERN_ERR "This touchpad reports more than 8 multi-buttons, don't know how to handle.\n"); + case 8: + set_bit(BTN_7, psmouse->dev.keybit); + set_bit(BTN_6, psmouse->dev.keybit); + case 6: + set_bit(BTN_5, psmouse->dev.keybit); + set_bit(BTN_4, psmouse->dev.keybit); + case 4: + set_bit(BTN_3, psmouse->dev.keybit); + set_bit(BTN_2, psmouse->dev.keybit); + case 2: + set_bit(BTN_1, psmouse->dev.keybit); + set_bit(BTN_0, psmouse->dev.keybit); + break; + } clear_bit(EV_REL, psmouse->dev.evbit); clear_bit(REL_X, psmouse->dev.relbit); clear_bit(REL_Y, psmouse->dev.relbit); @@ -259,45 +382,85 @@ { struct synaptics_data *priv = psmouse->private; - /* Restore touchpad to power on default state */ - synaptics_set_mode(psmouse, 0); - - kfree(priv); + if (psmouse->type == PSMOUSE_SYNAPTICS && priv) { + synaptics_set_mode(psmouse, 0); + if (priv->ptport) { + serio_unregister_slave_port(priv->ptport); + kfree(priv->ptport); + } + kfree(priv); + } } /***************************************************************************** * Functions to interpret the absolute mode packets ****************************************************************************/ -static void synaptics_parse_hw_state(struct synaptics_data *priv, struct synaptics_hw_state *hw) +static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) { - unsigned char *buf = priv->proto_buf; - - hw->x = (((buf[3] & 0x10) << 8) | - ((buf[1] & 0x0f) << 8) | - buf[4]); - hw->y = (((buf[3] & 0x20) << 7) | - ((buf[1] & 0xf0) << 4) | - buf[5]); - - hw->z = buf[2]; - hw->w = (((buf[0] & 0x30) >> 2) | - ((buf[0] & 0x04) >> 1) | - ((buf[3] & 0x04) >> 2)); - - hw->left = (buf[0] & 0x01) ? 1 : 0; - hw->right = (buf[0] & 0x2) ? 1 : 0; hw->up = 0; hw->down = 0; + hw->b0 = 0; + hw->b1 = 0; + hw->b2 = 0; + hw->b3 = 0; + hw->b4 = 0; + hw->b5 = 0; + hw->b6 = 0; + hw->b7 = 0; + + if (SYN_MODEL_NEWABS(priv->model_id)) { + hw->x = (((buf[3] & 0x10) << 8) | + ((buf[1] & 0x0f) << 8) | + buf[4]); + hw->y = (((buf[3] & 0x20) << 7) | + ((buf[1] & 0xf0) << 4) | + buf[5]); + + hw->z = buf[2]; + hw->w = (((buf[0] & 0x30) >> 2) | + ((buf[0] & 0x04) >> 1) | + ((buf[3] & 0x04) >> 2)); + + hw->left = (buf[0] & 0x01) ? 1 : 0; + hw->right = (buf[0] & 0x02) ? 1 : 0; + if (SYN_CAP_EXTENDED(priv->capabilities) && + (SYN_CAP_FOUR_BUTTON(priv->capabilities))) { + hw->up = ((buf[3] & 0x01)) ? 1 : 0; + if (hw->left) + hw->up = !hw->up; + hw->down = ((buf[3] & 0x02)) ? 1 : 0; + if (hw->right) + hw->down = !hw->down; + } + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && + ((buf[3] & 2) ? !hw->right : hw->right)) { + switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { + default: + ; /* we did comment while initialising... */ + case 8: + hw->b7 = ((buf[5] & 0x08)) ? 1 : 0; + hw->b6 = ((buf[4] & 0x08)) ? 1 : 0; + case 6: + hw->b5 = ((buf[5] & 0x04)) ? 1 : 0; + hw->b4 = ((buf[4] & 0x04)) ? 1 : 0; + case 4: + hw->b3 = ((buf[5] & 0x02)) ? 1 : 0; + hw->b2 = ((buf[4] & 0x02)) ? 1 : 0; + case 2: + hw->b1 = ((buf[5] & 0x01)) ? 1 : 0; + hw->b0 = ((buf[4] & 0x01)) ? 1 : 0; + } + } + } else { + hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); + hw->y = (((buf[4] & 0x1f) << 8) | buf[5]); - if (SYN_CAP_EXTENDED(priv->capabilities) && - (SYN_CAP_FOUR_BUTTON(priv->capabilities))) { - hw->up = ((buf[3] & 0x01)) ? 1 : 0; - if (hw->left) - hw->up = !hw->up; - hw->down = ((buf[3] & 0x02)) ? 1 : 0; - if (hw->right) - hw->down = !hw->down; + hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F)); + hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1)); + + hw->left = (buf[0] & 0x01) ? 1 : 0; + hw->right = (buf[0] & 0x02) ? 1 : 0; } } @@ -310,7 +473,7 @@ struct synaptics_data *priv = psmouse->private; struct synaptics_hw_state hw; - synaptics_parse_hw_state(priv, &hw); + synaptics_parse_hw_state(psmouse->packet, priv, &hw); if (hw.z > 0) { int w_ok = 0; @@ -350,7 +513,24 @@ input_report_key(dev, BTN_RIGHT, hw.right); input_report_key(dev, BTN_FORWARD, hw.up); input_report_key(dev, BTN_BACK, hw.down); - + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + switch(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { + default: + ; /* we did comment while initialising... */ + case 8: + input_report_key(dev, BTN_7, hw.b7); + input_report_key(dev, BTN_6, hw.b6); + case 6: + input_report_key(dev, BTN_5, hw.b5); + input_report_key(dev, BTN_4, hw.b4); + case 4: + input_report_key(dev, BTN_3, hw.b3); + input_report_key(dev, BTN_2, hw.b2); + case 2: + input_report_key(dev, BTN_1, hw.b1); + input_report_key(dev, BTN_0, hw.b0); + break; + } input_sync(dev); } @@ -358,35 +538,59 @@ { struct input_dev *dev = &psmouse->dev; struct synaptics_data *priv = psmouse->private; - unsigned char *pBuf = priv->proto_buf; - unsigned char u = psmouse->packet[0]; + unsigned char data = psmouse->packet[psmouse->pktcnt - 1]; + int newabs = SYN_MODEL_NEWABS(priv->model_id); input_regs(dev, regs); - pBuf[priv->proto_buf_tail++] = u; + switch (psmouse->pktcnt) { + case 1: + if (newabs ? ((data & 0xC8) != 0x80) : ((data & 0xC0) != 0xC0)) { + printk(KERN_WARNING "Synaptics driver lost sync at 1st byte\n"); + goto bad_sync; + } + break; + case 2: + if (!newabs && ((data & 0x60) != 0x00)) { + printk(KERN_WARNING "Synaptics driver lost sync at 2nd byte\n"); + goto bad_sync; + } + break; + case 4: + if (newabs ? ((data & 0xC8) != 0xC0) : ((data & 0xC0) != 0x80)) { + printk(KERN_WARNING "Synaptics driver lost sync at 4th byte\n"); + goto bad_sync; + } + break; + case 5: + if (!newabs && ((data & 0x60) != 0x00)) { + printk(KERN_WARNING "Synaptics driver lost sync at 5th byte\n"); + goto bad_sync; + } + break; + default: + if (psmouse->pktcnt >= 6) { /* Full packet received */ + if (priv->out_of_sync) { + priv->out_of_sync = 0; + printk(KERN_NOTICE "Synaptics driver resynced.\n"); + } + + if (priv->ptport && synaptics_is_pt_packet(psmouse->packet)) + synaptics_pass_pt_packet(priv->ptport, psmouse->packet); + else + synaptics_process_packet(psmouse); - /* check first byte */ - if ((priv->proto_buf_tail == 1) && ((u & 0xC8) != 0x80)) { - priv->inSync = 0; - priv->proto_buf_tail = 0; - printk(KERN_WARNING "Synaptics driver lost sync at 1st byte\n"); - return; - } - - /* check 4th byte */ - if ((priv->proto_buf_tail == 4) && ((u & 0xc8) != 0xc0)) { - priv->inSync = 0; - priv->proto_buf_tail = 0; - printk(KERN_WARNING "Synaptics driver lost sync at 4th byte\n"); - return; - } - - if (priv->proto_buf_tail >= 6) { /* Full packet received */ - if (!priv->inSync) { - priv->inSync = 1; - printk(KERN_NOTICE "Synaptics driver resynced.\n"); + psmouse->pktcnt = 0; } - synaptics_process_packet(psmouse); - priv->proto_buf_tail = 0; + break; + } + return; + + bad_sync: + priv->out_of_sync++; + psmouse->pktcnt = 0; + if (psmouse_resetafter > 0 && priv->out_of_sync == psmouse_resetafter) { + psmouse->state = PSMOUSE_IGNORE; + serio_rescan(psmouse->serio); } } diff -Nru a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h --- a/drivers/input/mouse/synaptics.h Fri Sep 19 12:16:32 2003 +++ b/drivers/input/mouse/synaptics.h Fri Sep 19 12:16:32 2003 @@ -12,6 +12,7 @@ extern void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs); extern int synaptics_init(struct psmouse *psmouse); +extern int synaptics_pt_init(struct psmouse *psmouse); extern void synaptics_disconnect(struct psmouse *psmouse); /* synaptics queries */ @@ -22,12 +23,14 @@ #define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06 #define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07 #define SYN_QUE_RESOLUTION 0x08 +#define SYN_QUE_EXT_CAPAB 0x09 /* synatics modes */ #define SYN_BIT_ABSOLUTE_MODE (1 << 7) #define SYN_BIT_HIGH_RATE (1 << 6) #define SYN_BIT_SLEEP_MODE (1 << 3) #define SYN_BIT_DISABLE_GESTURE (1 << 2) +#define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1) #define SYN_BIT_W_MODE (1 << 0) /* synaptics model ID bits */ @@ -42,11 +45,14 @@ /* synaptics capability bits */ #define SYN_CAP_EXTENDED(c) ((c) & (1 << 23)) +#define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7)) #define SYN_CAP_SLEEP(c) ((c) & (1 << 4)) #define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3)) #define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1)) #define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0)) #define SYN_CAP_VALID(c) ((((c) & 0x00ff00) >> 8) == 0x47) +#define SYN_EXT_CAP_REQUESTS(c) ((((c) & 0x700000) >> 20) == 1) +#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) @@ -62,6 +68,10 @@ #define SYN_ID_MINOR(i) (((i) >> 16) & 0xff) #define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47) +/* synaptics special commands */ +#define SYN_PS_SET_MODE2 0x14 +#define SYN_PS_CLIENT_CMD 0x28 + /* * A structure to describe the state of the touchpad hardware (buttons and pad) */ @@ -75,21 +85,28 @@ int right; int up; int down; + int b0; + int b1; + int b2; + int b3; + int b4; + int b5; + int b6; + int b7; }; struct synaptics_data { /* Data read from the touchpad */ unsigned long int model_id; /* Model-ID */ unsigned long int capabilities; /* Capabilities */ + unsigned long int ext_cap; /* Extended Capabilities */ unsigned long int identity; /* Identification */ /* Data for normal processing */ - unsigned char proto_buf[6]; /* Buffer for Packet */ - unsigned char last_byte; /* last received byte */ - int inSync; /* Packets in sync */ - int proto_buf_tail; - + unsigned int out_of_sync; /* # of packets out of sync */ int old_w; /* Previous w value */ + + struct serio *ptport; /* pass-through port */ }; #endif /* _SYNAPTICS_H */ diff -Nru a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c --- a/drivers/input/serio/serio.c Fri Sep 19 12:16:32 2003 +++ b/drivers/input/serio/serio.c Fri Sep 19 12:16:32 2003 @@ -49,7 +49,9 @@ EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(serio_register_port); +EXPORT_SYMBOL(serio_register_slave_port); EXPORT_SYMBOL(serio_unregister_port); +EXPORT_SYMBOL(serio_unregister_slave_port); EXPORT_SYMBOL(serio_register_device); EXPORT_SYMBOL(serio_unregister_device); EXPORT_SYMBOL(serio_open); @@ -166,6 +168,17 @@ up(&serio_sem); } +/* + * Same as serio_register_port but does not try to acquire serio_sem. + * Should be used when registering a serio from other input device's + * connect() function. + */ +void serio_register_slave_port(struct serio *serio) +{ + list_add_tail(&serio->node, &serio_list); + serio_find_dev(serio); +} + void serio_unregister_port(struct serio *serio) { down(&serio_sem); @@ -173,6 +186,18 @@ if (serio->dev && serio->dev->disconnect) serio->dev->disconnect(serio); up(&serio_sem); +} + +/* + * Same as serio_unregister_port but does not try to acquire serio_sem. + * Should be used when unregistering a serio from other input device's + * disconnect() function. + */ +void serio_unregister_slave_port(struct serio *serio) +{ + list_del_init(&serio->node); + if (serio->dev && serio->dev->disconnect) + serio->dev->disconnect(serio); } void serio_register_device(struct serio_dev *dev) diff -Nru a/include/linux/serio.h b/include/linux/serio.h --- a/include/linux/serio.h Fri Sep 19 12:16:32 2003 +++ b/include/linux/serio.h Fri Sep 19 12:16:32 2003 @@ -65,7 +65,9 @@ irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs); void serio_register_port(struct serio *serio); +void serio_register_slave_port(struct serio *serio); void serio_unregister_port(struct serio *serio); +void serio_unregister_slave_port(struct serio *serio); void serio_register_device(struct serio_dev *dev); void serio_unregister_device(struct serio_dev *dev); @@ -104,6 +106,7 @@ #define SERIO_RS232 0x02000000UL #define SERIO_HIL_MLC 0x03000000UL #define SERIO_PC9800 0x04000000UL +#define SERIO_PS_PSTHRU 0x05000000UL #define SERIO_PROTO 0xFFUL #define SERIO_MSC 0x01 - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/