vserver 2.0 rc7
[linux-2.6.git] / drivers / input / mouse / synaptics.c
index a182ce7..36c7212 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/module.h>
 #include <linux/input.h>
 #include <linux/serio.h>
+#include <linux/libps2.h>
 #include "psmouse.h"
 #include "synaptics.h"
 
  *     Synaptics communications functions
  ****************************************************************************/
 
-/*
- * Use the Synaptics extended ps/2 syntax to write a special command byte.
- * special command: 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
- *                  is the command. A 0xF3 or 0xE9 must follow (see synaptics_send_cmd
- *                  and synaptics_mode_cmd)
- */
-static int synaptics_special_cmd(struct psmouse *psmouse, unsigned char command)
-{
-       int i;
-
-       if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11))
-               return -1;
-
-       for (i = 6; i >= 0; i -= 2) {
-               unsigned char d = (command >> i) & 3;
-               if (psmouse_command(psmouse, &d, PSMOUSE_CMD_SETRES))
-                       return -1;
-       }
-
-       return 0;
-}
-
 /*
  * Send a command to the synpatics touchpad by special commands
  */
 static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
 {
-       if (synaptics_special_cmd(psmouse, c))
+       if (psmouse_sliced_command(psmouse, c))
                return -1;
-       if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO))
+       if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
                return -1;
        return 0;
 }
@@ -84,10 +63,10 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
 {
        unsigned char param[1];
 
-       if (synaptics_special_cmd(psmouse, mode))
+       if (psmouse_sliced_command(psmouse, mode))
                return -1;
        param[0] = SYN_PS_SET_MODE2;
-       if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE))
+       if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
                return -1;
        return 0;
 }
@@ -118,17 +97,31 @@ static int synaptics_capability(struct psmouse *psmouse)
 
        if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
                return -1;
-       priv->capabilities = (cap[0]<<16) | (cap[1]<<8) | cap[2];
+       priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
        priv->ext_cap = 0;
        if (!SYN_CAP_VALID(priv->capabilities))
                return -1;
 
-       if (SYN_EXT_CAP_REQUESTS(priv->capabilities)) {
+       /*
+        * Unless capExtended is set the rest of the flags should be ignored
+        */
+       if (!SYN_CAP_EXTENDED(priv->capabilities))
+               priv->capabilities = 0;
+
+       if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
                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
-                       priv->ext_cap = (cap[0]<<16) | (cap[1]<<8) | cap[2];
+               } else {
+                       priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+
+                       /*
+                        * if nExtBtn is greater than 8 it should be considered
+                        * invalid and treated as 0
+                        */
+                       if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
+                               priv->ext_cap &= 0xff0fff;
+               }
        }
        return 0;
 }
@@ -150,38 +143,6 @@ static int synaptics_identify(struct psmouse *psmouse)
        return -1;
 }
 
-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))
-               printk(KERN_INFO " portrait touchpad\n");
-       printk(KERN_INFO " Sensor: %ld\n", SYN_MODEL_SENSOR(priv->model_id));
-       if (SYN_MODEL_NEWABS(priv->model_id))
-               printk(KERN_INFO " new absolute packet format\n");
-       if (SYN_MODEL_PEN(priv->model_id))
-               printk(KERN_INFO " pen detection\n");
-
-       if (SYN_CAP_EXTENDED(priv->capabilities)) {
-               printk(KERN_INFO " Touchpad has extended capability bits\n");
-               if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
-                   SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) <= 8)
-                       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");
-       }
-}
-
 static int synaptics_query_hardware(struct psmouse *psmouse)
 {
        int retries = 0;
@@ -199,43 +160,48 @@ static int synaptics_query_hardware(struct psmouse *psmouse)
        return 0;
 }
 
-static int synaptics_set_mode(struct psmouse *psmouse, int mode)
+static int synaptics_set_absolute_mode(struct psmouse *psmouse)
 {
        struct synaptics_data *priv = psmouse->private;
 
-       mode |= SYN_BIT_ABSOLUTE_MODE;
-       if (psmouse_rate >= 80)
-               mode |= SYN_BIT_HIGH_RATE;
+       priv->mode = SYN_BIT_ABSOLUTE_MODE;
        if (SYN_ID_MAJOR(priv->identity) >= 4)
-               mode |= SYN_BIT_DISABLE_GESTURE;
+               priv->mode |= SYN_BIT_DISABLE_GESTURE;
        if (SYN_CAP_EXTENDED(priv->capabilities))
-               mode |= SYN_BIT_W_MODE;
-       if (synaptics_mode_cmd(psmouse, mode))
+               priv->mode |= SYN_BIT_W_MODE;
+
+       if (synaptics_mode_cmd(psmouse, priv->mode))
                return -1;
 
        return 0;
 }
 
-/*****************************************************************************
- *     Synaptics pass-through PS/2 port support
- ****************************************************************************/
-static int synaptics_pt_open(struct serio *port)
+static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
 {
-       return 0;
-}
+       struct synaptics_data *priv = psmouse->private;
 
-static void synaptics_pt_close(struct serio *port)
-{
+       if (rate >= 80) {
+               priv->mode |= SYN_BIT_HIGH_RATE;
+               psmouse->rate = 80;
+       } else {
+               priv->mode &= ~SYN_BIT_HIGH_RATE;
+               psmouse->rate = 40;
+       }
+
+       synaptics_mode_cmd(psmouse, priv->mode);
 }
 
-static int synaptics_pt_write(struct serio *port, unsigned char c)
+/*****************************************************************************
+ *     Synaptics pass-through PS/2 port support
+ ****************************************************************************/
+static int synaptics_pt_write(struct serio *serio, unsigned char c)
 {
-       struct psmouse *parent = port->driver;
+       struct psmouse *parent = serio_get_drvdata(serio->parent);
        char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
 
-       if (synaptics_special_cmd(parent, c))
+       if (psmouse_sliced_command(parent, c))
                return -1;
-       if (psmouse_command(parent, &rate_param, PSMOUSE_CMD_SETRATE))
+       if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
                return -1;
        return 0;
 }
@@ -247,205 +213,58 @@ static inline int synaptics_is_pt_packet(unsigned char *buf)
 
 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);
-               }
-       }
+       struct psmouse *child = serio_get_drvdata(ptport);
+
+       if (child && 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
+               serio_interrupt(ptport, packet[1], 0, NULL);
 }
 
 static void synaptics_pt_activate(struct psmouse *psmouse)
 {
-       struct psmouse *child = psmouse->ptport->serio.private;
+       struct serio *ptport = psmouse->ps2dev.serio->child;
+       struct psmouse *child = serio_get_drvdata(ptport);
+       struct synaptics_data *priv = psmouse->private;
 
        /* adjust the touchpad to child's choice of protocol */
-       if (child && child->type >= PSMOUSE_GENPS) {
-               if (synaptics_set_mode(psmouse, SYN_BIT_FOUR_BYTE_CLIENT))
-                       printk(KERN_INFO "synaptics: failed to enable 4-byte guest protocol\n");
+       if (child) {
+               if (child->type >= PSMOUSE_GENPS)
+                       priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT;
+               else
+                       priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
+
+               if (synaptics_mode_cmd(psmouse, priv->mode))
+                       printk(KERN_INFO "synaptics: failed to switch guest protocol\n");
        }
 }
 
 static void synaptics_pt_create(struct psmouse *psmouse)
 {
-       struct psmouse_ptport *port;
+       struct serio *serio;
 
-       psmouse->ptport = port = kmalloc(sizeof(struct psmouse_ptport), GFP_KERNEL);
-       if (!port) {
+       serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+       if (!serio) {
                printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
                return;
        }
 
-       memset(port, 0, sizeof(struct psmouse_ptport));
+       memset(serio, 0, sizeof(struct serio));
 
-       port->serio.type = SERIO_PS_PSTHRU;
-       port->serio.name = "Synaptics pass-through";
-       port->serio.phys = "synaptics-pt/serio0";
-       port->serio.write = synaptics_pt_write;
-       port->serio.open = synaptics_pt_open;
-       port->serio.close = synaptics_pt_close;
-       port->serio.driver = psmouse;
+       serio->id.type = SERIO_PS_PSTHRU;
+       strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
+       strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
+       serio->write = synaptics_pt_write;
+       serio->parent = psmouse->ps2dev.serio;
 
-       port->activate = synaptics_pt_activate;
-}
+       psmouse->pt_activate = synaptics_pt_activate;
 
-/*****************************************************************************
- *     Driver initialization/cleanup functions
- ****************************************************************************/
-
-static inline void set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
-{
-       dev->absmin[axis] = min;
-       dev->absmax[axis] = max;
-       dev->absfuzz[axis] = fuzz;
-       dev->absflat[axis] = flat;
-
-       set_bit(axis, dev->absbit);
-}
-
-static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
-{
-       set_bit(EV_ABS, dev->evbit);
-       set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
-       set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
-       set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
-       set_bit(ABS_TOOL_WIDTH, dev->absbit);
-
-       set_bit(EV_KEY, dev->evbit);
-       set_bit(BTN_TOUCH, dev->keybit);
-       set_bit(BTN_TOOL_FINGER, dev->keybit);
-       set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
-       set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
-
-       set_bit(BTN_LEFT, dev->keybit);
-       set_bit(BTN_RIGHT, dev->keybit);
-       set_bit(BTN_FORWARD, dev->keybit);
-       set_bit(BTN_BACK, dev->keybit);
-       if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) {
-               switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
-               default:
-                       /*
-                        * if nExtBtn is greater than 8 it should be considered
-                        * invalid and treated as 0
-                        */
-                       break;
-               case 8:
-                       set_bit(BTN_7, dev->keybit);
-                       set_bit(BTN_6, dev->keybit);
-               case 6:
-                       set_bit(BTN_5, dev->keybit);
-                       set_bit(BTN_4, dev->keybit);
-               case 4:
-                       set_bit(BTN_3, dev->keybit);
-                       set_bit(BTN_2, dev->keybit);
-               case 2:
-                       set_bit(BTN_1, dev->keybit);
-                       set_bit(BTN_0, dev->keybit);
-                       break;
-               }
-       }
-
-       clear_bit(EV_REL, dev->evbit);
-       clear_bit(REL_X, dev->relbit);
-       clear_bit(REL_Y, dev->relbit);
-}
-
-void synaptics_reset(struct psmouse *psmouse)
-{
-       /* reset touchpad back to relative mode, gestures enabled */
-       synaptics_mode_cmd(psmouse, 0);
-}
-
-static void synaptics_disconnect(struct psmouse *psmouse)
-{
-       synaptics_reset(psmouse);
-       kfree(psmouse->private);
-}
-
-static int synaptics_reconnect(struct psmouse *psmouse)
-{
-       struct synaptics_data *priv = psmouse->private;
-       struct synaptics_data old_priv = *priv;
-
-       if (!synaptics_detect(psmouse))
-               return -1;
-
-       if (synaptics_query_hardware(psmouse)) {
-               printk(KERN_ERR "Unable to query Synaptics hardware.\n");
-               return -1;
-       }
-
-       if (old_priv.identity != priv->identity ||
-           old_priv.model_id != priv->model_id ||
-           old_priv.capabilities != priv->capabilities ||
-           old_priv.ext_cap != priv->ext_cap)
-               return -1;
-
-       if (synaptics_set_mode(psmouse, 0)) {
-               printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
-               return -1;
-       }
-
-       return 0;
-}
-
-int synaptics_detect(struct psmouse *psmouse)
-{
-       unsigned char param[4];
-
-       param[0] = 0;
-
-       psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
-       psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
-       psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
-       psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
-       psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
-
-       return param[1] == 0x47;
-}
-
-int synaptics_init(struct psmouse *psmouse)
-{
-       struct synaptics_data *priv;
-
-       psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL);
-       if (!priv)
-               return -1;
-       memset(priv, 0, sizeof(struct synaptics_data));
-
-       if (synaptics_query_hardware(psmouse)) {
-               printk(KERN_ERR "Unable to query Synaptics hardware.\n");
-               goto init_fail;
-       }
-
-       if (synaptics_set_mode(psmouse, 0)) {
-               printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
-               goto init_fail;
-       }
-
-       priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
-
-       if (SYN_CAP_EXTENDED(priv->capabilities) && SYN_CAP_PASS_THROUGH(priv->capabilities))
-                       synaptics_pt_create(psmouse);
-
-       print_ident(priv);
-       set_input_params(&psmouse->dev, priv);
-
-       psmouse->disconnect = synaptics_disconnect;
-       psmouse->reconnect = synaptics_reconnect;
-
-       return 0;
-
- init_fail:
-       kfree(priv);
-       return -1;
+       printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys);
+       serio_register_port(serio);
 }
 
 /*****************************************************************************
@@ -471,17 +290,20 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
 
                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_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;
+                       hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
+               }
+
                if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
-                   ((buf[3] & 2) ? !hw->right : hw->right)) {
+                   ((buf[0] ^ buf[3]) & 0x02)) {
                        switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
                        default:
                                /*
@@ -490,17 +312,17 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
                                 */
                                break;
                        case 8:
-                               hw->b7 = ((buf[5] & 0x08)) ? 1 : 0;
-                               hw->b6 = ((buf[4] & 0x08)) ? 1 : 0;
+                               hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0;
+                               hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0;
                        case 6:
-                               hw->b5 = ((buf[5] & 0x04)) ? 1 : 0;
-                               hw->b4 = ((buf[4] & 0x04)) ? 1 : 0;
+                               hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0;
+                               hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0;
                        case 4:
-                               hw->b3 = ((buf[5] & 0x02)) ? 1 : 0;
-                               hw->b2 = ((buf[4] & 0x02)) ? 1 : 0;
+                               hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0;
+                               hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0;
                        case 2:
-                               hw->b1 = ((buf[5] & 0x01)) ? 1 : 0;
-                               hw->b0 = ((buf[4] & 0x01)) ? 1 : 0;
+                               hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0;
+                               hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0;
                        }
                }
        } else {
@@ -525,9 +347,30 @@ static void synaptics_process_packet(struct psmouse *psmouse)
        struct synaptics_hw_state hw;
        int num_fingers;
        int finger_width;
+       int i;
 
        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;
@@ -570,32 +413,20 @@ static void synaptics_process_packet(struct psmouse *psmouse)
        input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
        input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
 
-       input_report_key(dev, BTN_LEFT,    hw.left);
-       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:
-                       /*
-                        * if nExtBtn is greater than 8 it should be considered
-                        * invalid and treated as 0
-                        */
-                       break;
-               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_report_key(dev, BTN_LEFT, hw.left);
+       input_report_key(dev, BTN_RIGHT, hw.right);
+
+       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+               input_report_key(dev, BTN_MIDDLE, hw.middle);
+
+       if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+               input_report_key(dev, BTN_FORWARD, hw.up);
+               input_report_key(dev, BTN_BACK, hw.down);
+       }
+
+       for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+               input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
+
        input_sync(dev);
 }
 
@@ -607,6 +438,9 @@ static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned cha
        static unsigned char oldabs_mask[]      = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
        static unsigned char oldabs_rslt[]      = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
 
+       if (idx < 0 || idx > 4)
+               return 0;
+
        switch (pkt_type) {
                case SYN_NEWABS:
                case SYN_NEWABS_RELAXED:
@@ -637,7 +471,7 @@ static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
        return SYN_NEWABS_STRICT;
 }
 
-void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
 {
        struct input_dev *dev = &psmouse->dev;
        struct synaptics_data *priv = psmouse->private;
@@ -645,28 +479,193 @@ void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
        input_regs(dev, regs);
 
        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 (unlikely(priv->pkt_type == SYN_NEWABS))
                        priv->pkt_type = synaptics_detect_pkt_type(psmouse);
 
-               if (psmouse->ptport && psmouse->ptport->serio.dev && synaptics_is_pt_packet(psmouse->packet))
-                       synaptics_pass_pt_packet(&psmouse->ptport->serio, psmouse->packet);
-               else
+               if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
+                       if (psmouse->ps2dev.serio->child)
+                               synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
+               } else
                        synaptics_process_packet(psmouse);
-               psmouse->pktcnt = 0;
-
-       } else if (psmouse->pktcnt &&
-                  !synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type)) {
-               printk(KERN_WARNING "Synaptics driver lost sync at byte %d\n", psmouse->pktcnt);
-               psmouse->pktcnt = 0;
-               if (++priv->out_of_sync == psmouse_resetafter) {
-                       psmouse->state = PSMOUSE_IGNORE;
-                       printk(KERN_NOTICE "synaptics: issuing reconnect request\n");
-                       serio_reconnect(psmouse->serio);
-               }
+
+               return PSMOUSE_FULL_PACKET;
+       }
+
+       return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ?
+               PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+}
+
+/*****************************************************************************
+ *     Driver initialization/cleanup functions
+ ****************************************************************************/
+static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+{
+       int i;
+
+       set_bit(EV_ABS, dev->evbit);
+       input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
+       input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
+       input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+       set_bit(ABS_TOOL_WIDTH, dev->absbit);
+
+       set_bit(EV_KEY, dev->evbit);
+       set_bit(BTN_TOUCH, dev->keybit);
+       set_bit(BTN_TOOL_FINGER, dev->keybit);
+       set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+       set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+
+       set_bit(BTN_LEFT, dev->keybit);
+       set_bit(BTN_RIGHT, dev->keybit);
+
+       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+               set_bit(BTN_MIDDLE, dev->keybit);
+
+       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);
+       }
+
+       for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+               set_bit(BTN_0 + i, dev->keybit);
+
+       clear_bit(EV_REL, dev->evbit);
+       clear_bit(REL_X, dev->relbit);
+       clear_bit(REL_Y, dev->relbit);
+}
+
+void synaptics_reset(struct psmouse *psmouse)
+{
+       /* reset touchpad back to relative mode, gestures enabled */
+       synaptics_mode_cmd(psmouse, 0);
+}
+
+static void synaptics_disconnect(struct psmouse *psmouse)
+{
+       synaptics_reset(psmouse);
+       kfree(psmouse->private);
+       psmouse->private = NULL;
+}
+
+static int synaptics_reconnect(struct psmouse *psmouse)
+{
+       struct synaptics_data *priv = psmouse->private;
+       struct synaptics_data old_priv = *priv;
+
+       if (synaptics_detect(psmouse, 0))
+               return -1;
+
+       if (synaptics_query_hardware(psmouse)) {
+               printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+               return -1;
+       }
+
+       if (old_priv.identity != priv->identity ||
+           old_priv.model_id != priv->model_id ||
+           old_priv.capabilities != priv->capabilities ||
+           old_priv.ext_cap != priv->ext_cap)
+               return -1;
+
+       if (synaptics_set_absolute_mode(psmouse)) {
+               printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+int synaptics_detect(struct psmouse *psmouse, int set_properties)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       unsigned char param[4];
+
+       param[0] = 0;
+
+       ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+       ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+       ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+       ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+       ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+       if (param[1] != 0x47)
+               return -1;
+
+       if (set_properties) {
+               psmouse->vendor = "Synaptics";
+               psmouse->name = "TouchPad";
+       }
+
+       return 0;
+}
+
+#if defined(__i386__)
+#include <linux/dmi.h>
+static struct dmi_system_id toshiba_dmi_table[] = {
+       {
+               .ident = "Toshiba Satellite",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+                       DMI_MATCH(DMI_PRODUCT_NAME , "Satellite"),
+               },
+       },
+       { }
+};
+#endif
+
+int synaptics_init(struct psmouse *psmouse)
+{
+       struct synaptics_data *priv;
+
+       psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL);
+       if (!priv)
+               return -1;
+       memset(priv, 0, sizeof(struct synaptics_data));
+
+       if (synaptics_query_hardware(psmouse)) {
+               printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+               goto init_fail;
+       }
+
+       if (synaptics_set_absolute_mode(psmouse)) {
+               printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+               goto init_fail;
+       }
+
+       priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
+
+       printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx\n",
+               SYN_ID_MODEL(priv->identity),
+               SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
+               priv->model_id, priv->capabilities, priv->ext_cap);
+
+       set_input_params(&psmouse->dev, priv);
+
+       psmouse->protocol_handler = synaptics_process_byte;
+       psmouse->set_rate = synaptics_set_rate;
+       psmouse->disconnect = synaptics_disconnect;
+       psmouse->reconnect = synaptics_reconnect;
+       psmouse->pktsize = 6;
+
+       if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+               synaptics_pt_create(psmouse);
+
+#if defined(__i386__)
+       /*
+        * Toshiba's KBC seems to have trouble handling data from
+        * Synaptics as full rate, switch to lower rate which is roughly
+        * thye same as rate of standard PS/2 mouse.
+        */
+       if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) {
+               printk(KERN_INFO "synaptics: Toshiba Satellite detected, limiting rate to 40pps.\n");
+               psmouse->rate = 40;
        }
+#endif
+
+       return 0;
+
+ init_fail:
+       kfree(priv);
+       return -1;
 }
+
+