From: Stuffed Crust One of the side-effects of the new input layer is that the old usermode tools for manipulating the touchpad configuration don't work any more. Most importantly, the ability to disable the tap-to-click "feature". And this has been long-recognized, as bug #18. :) So, here's my crack at scratching this itch. it defaults to disabling the tap-to-click, but there's a module parameter to re-enable it. I started writing this from the perspective of a full-native synaptics driver, using the absolute mode of operation, which will let us do all sorts of yummy things like corner taps and virtual scroll wheels and sensitivity and whatnot... Anyone else working on this, before I wade further in? All of the new code is wrapped in #ifdef SYNAPTICS. It should work on all models of the touchpad, old and new, but I've only tested this on my rev5.7 touchpad (Dell Inspiron 4100) drivers/input/mouse/psmouse.c | 185 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 185 insertions(+) diff -puN drivers/input/mouse/psmouse.c~synaptics-mouse-support drivers/input/mouse/psmouse.c --- 25/drivers/input/mouse/psmouse.c~synaptics-mouse-support 2003-04-21 20:28:07.000000000 -0700 +++ 25-akpm/drivers/input/mouse/psmouse.c 2003-04-21 20:31:40.000000000 -0700 @@ -10,6 +10,11 @@ * the Free Software Foundation. */ +/* + * Initial Snaptics support by Solomon Peachy (pizza@shaftnet.org) + */ +#define SYNAPTICS + #include #include #include @@ -25,6 +30,11 @@ MODULE_LICENSE("GPL"); static int psmouse_noext; +#ifdef SYNAPTICS +static int synaptics_tap = 0; +MODULE_PARM(synaptics_tap, "1i"); +#endif + #define PSMOUSE_CMD_SETSCALE11 0x00e6 #define PSMOUSE_CMD_SETRES 0x10e8 #define PSMOUSE_CMD_GETINFO 0x03e9 @@ -33,6 +43,7 @@ static int psmouse_noext; #define PSMOUSE_CMD_GETID 0x02f2 #define PSMOUSE_CMD_SETRATE 0x10f3 #define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_DISABLE 0x00f5 #define PSMOUSE_CMD_RESET_DIS 0x00f6 #define PSMOUSE_CMD_RESET_BAT 0x02ff @@ -40,6 +51,52 @@ static int psmouse_noext; #define PSMOUSE_RET_ACK 0xfa #define PSMOUSE_RET_NAK 0xfe +#ifdef SYNAPTICS + +/* Synaptics special queries */ +#define STP_QRY_IDENTIFY 0x00 +#define STP_QRY_READ_MODES 0x01 +#define STP_QRY_READ_CAPS 0x02 +#define STP_QRY_READ_MODEL_ID 0x03 +#define STP_QRY_READ_SN_PREFIX 0x06 +#define STP_QRY_READ_SN_SUFFIX 0x07 +#define STP_QRY_READ_RES 0x08 + +/* Model ID bits */ +#define STP_INFO_ROT180(id) (((id) & 0x800000) >> 23) +#define STP_INFO_PORTRAIT(id) (((id) & 0x400000) >> 22) +#define STP_INFO_SENSOR(id) (((id) & 0x3F0000) >> 16) +#define STP_INFO_HARDWARE(id) (((id) & 0x00FE00) >> 9) +#define STP_INFO_NEW_ABS(id) (((id) & 0x000080) >> 7) +#define STP_INFO_CAP_PEN(id) (((id) & 0x000040) >> 6) +#define STP_INFO_SIMPLE_CMD(id) (((id) & 0x000020) >> 5) +#define STP_INFO_GEOMETRY(id) ((id) & 0x00000F) + +#define STP_SET_MODE_ABSOLUTE(m, a) (((m) &~ 0x80) | ((a) << 7)) +#define STP_SET_MODE_RATE(m, a) (((m) &~ 0x40) | ((a) << 6)) +#define STP_SET_MODE_SLEEP(m, a) (((m) &~ 0x08) | ((a) << 3)) +#define STP_SET_MODE_GESTURE(m, a) (((m) &~ 0x04) | ((!a) << 2)) +#define STP_SET_MODE_PACKSIZE(m, a) (((m) &~ 0x02) | ((a) << 1)) +#define STP_SET_MODE_WMODE(m, a) (((m) &~ 0x01) | (a)) + +#define STP_SET_OMODE_CORNER(m, a) (((m) &~ 0x80000000) | ((a) << 31)) +#define STP_SET_OMODE_Z_THRESH(m, a) (((m) &~ 0x70000000) | ((a) << 28)) +#define STP_SET_OMODE_TAP_MODE(m, a) (((m) &~ 0x0C000000) | ((a) << 26)) +#define STP_SET_OMODE_EDGE_MOTN(m, a) (((m) &~ 0x03000000) | ((a) << 24)) +#define STP_SET_OMODE_ABSOLUTE(m, a) (((m) &~ 0x00800000) | ((a) << 23)) +#define STP_SET_OMODE_RATE(m, a) (((m) &~ 0x00400000) | ((a) << 22)) +#define STP_SET_OMODE_BAUD(m, a) (((m) &~ 0x00080000) | ((a) << 19)) +#define STP_SET_OMODE_3_BUTTON(m, a) (((m) &~ 0x00040000) | ((a) << 18)) +#define STP_SET_OMODE_MIDDLE(m, a) (((m) &~ 0x00020000) | ((a) << 17)) +#define STP_SET_OMODE_HOP(m, a) (((m) &~ 0x00010000) | ((a) << 16)) +#define STP_SET_OMODE_RIGHT_MGN(m, a) (((m) &~ 0x0000F000) | ((a) << 12)) +#define STP_SET_OMODE_LEFT_MGN(m, a) (((m) &~ 0x00000F00) | ((a) << 8)) +#define STP_SET_OMODE_TOP_MGN(m, a) (((m) &~ 0x000000F0) | ((a) << 4)) +#define STP_SET_OMODE_BOTTOM_MGN(m, a) (((m) &~ 0x0000000F) | (a)) +#define QUAD_MODE_BYTE 0x0302 + +#endif + struct psmouse { struct input_dev dev; struct serio *serio; @@ -57,6 +114,14 @@ struct psmouse { char error; char devname[64]; char phys[32]; + +#ifdef SYNAPTICS + unsigned int fw_version; + unsigned int single_mode_byte; + unsigned int model_id; + unsigned int modes; + unsigned int caps; +#endif }; #define PSMOUSE_PS2 1 @@ -287,6 +352,78 @@ static int psmouse_command(struct psmous return 0; } +#ifdef SYNAPTICS +void synaptics_command(struct psmouse *psmouse, unsigned char cmd) +{ + unsigned char param[4]; + + param[0] = (cmd & 0xC0) >> 6; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); + param[0] = (cmd & 0x30) >> 4; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); + param[0] = (cmd & 0x0C) >> 2; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); + param[0] = (cmd & 0x03); + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); +} + +void synaptics_set_mode(struct psmouse *psmouse, unsigned int mode) +{ + unsigned char param[4]; + + psmouse_command(psmouse, param, PSMOUSE_CMD_DISABLE); + + if (psmouse->single_mode_byte) { + synaptics_command(psmouse, mode & 0xff); + param[0] = 20; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); + } else { + synaptics_command(psmouse, (mode >> 24) & 0xff); + param[0] = 10; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); + synaptics_command(psmouse, (mode >> 16) & 0xff); + param[0] = 20; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); + if (psmouse->fw_version < QUAD_MODE_BYTE) { + synaptics_command(psmouse, (mode >> 8) & 0xff); + param[0] = 40; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); + synaptics_command(psmouse, mode & 0xff); + param[0] = 60; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); + } + } + + psmouse_command(psmouse, param, PSMOUSE_CMD_ENABLE); +} + +unsigned int synaptics_get_mode(struct psmouse *psmouse) +{ + unsigned char param[4]; + unsigned int retval = 0; + + synaptics_command(psmouse, STP_QRY_READ_MODES); + psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); + + param[1] = 0; + + if (psmouse->single_mode_byte) { + retval = param[2]; + } else { + retval = ((param[0] << 24) | + (param[2] << 16)); + + if (psmouse->fw_version < QUAD_MODE_BYTE) { + synaptics_command(psmouse, STP_QRY_READ_CAPS); + psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); + retval |= ((param[0] << 8) | + (param[2])); + } + } + return retval; +} +#endif + /* * psmouse_ps2pp_cmd() sends a PS2++ command, sliced into two bit * pieces through the SETRES command. This is needed to send extended @@ -348,6 +485,54 @@ static int psmouse_extensions(struct psm thing up. */ psmouse->vendor = "Synaptics"; psmouse->name = "TouchPad"; + +#ifdef SYNAPTICS + param[0] = 0; + synaptics_command(psmouse, STP_QRY_IDENTIFY); + psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); + psmouse->fw_version = ((param[2] & 0x0f) << 8) | param[0]; + + synaptics_command(psmouse, STP_QRY_READ_MODEL_ID); + psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); + + psmouse->model_id = ((param[1] & 0x1) ? 0x1 : + (((unsigned int) param[0] << 16) | + ((unsigned int) param[1] << 8) | + ((unsigned int) param[2]))); + + psmouse->single_mode_byte = STP_INFO_SIMPLE_CMD (psmouse->model_id); + /* Fetch capabilities */ + synaptics_command(psmouse, STP_QRY_READ_MODEL_ID); + psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); + psmouse->caps = (((unsigned int) param[0] << 8) | + (unsigned int) param[2]); + + printk(KERN_INFO "Found Synaptics Touchpad rev %d.%d\n", + (psmouse->fw_version >> 8) & 0x0f, + (psmouse->fw_version & 0xff)); + + /* pull the modes down */ + psmouse->modes = synaptics_get_mode(psmouse); + + /* Now, disable tap-to-click and enable high rate. */ + + if (psmouse->single_mode_byte) { + psmouse->modes = STP_SET_MODE_GESTURE(psmouse->modes, synaptics_tap); + psmouse->modes = STP_SET_MODE_RATE(psmouse->modes, 1); + + synaptics_set_mode(psmouse, psmouse->modes); + } else { + psmouse->modes = STP_SET_OMODE_TAP_MODE(psmouse->modes, synaptics_tap); + psmouse->modes = STP_SET_OMODE_RATE(psmouse->modes, 1); + + synaptics_set_mode(psmouse, psmouse->modes); + } + + psmouse->modes = synaptics_get_mode(psmouse); +#else + printk(KERN_INFO "Found Synaptics touchpad, support disabled\n"); +#endif + return PSMOUSE_PS2; } _