| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Synaptics TouchPad PS/2 mouse driver | 
 | 3 |  * | 
 | 4 |  *   2003 Dmitry Torokhov <dtor@mail.ru> | 
 | 5 |  *     Added support for pass-through port. Special thanks to Peter Berg Larsen | 
 | 6 |  *     for explaining various Synaptics quirks. | 
 | 7 |  * | 
 | 8 |  *   2003 Peter Osterlund <petero2@telia.com> | 
 | 9 |  *     Ported to 2.5 input device infrastructure. | 
 | 10 |  * | 
 | 11 |  *   Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch> | 
 | 12 |  *     start merging tpconfig and gpm code to a xfree-input module | 
 | 13 |  *     adding some changes and extensions (ex. 3rd and 4th button) | 
 | 14 |  * | 
 | 15 |  *   Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu> | 
 | 16 |  *   Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com> | 
 | 17 |  *     code for the special synaptics commands (from the tpconfig-source) | 
 | 18 |  * | 
 | 19 |  * This program is free software; you can redistribute it and/or modify it | 
 | 20 |  * under the terms of the GNU General Public License version 2 as published by | 
 | 21 |  * the Free Software Foundation. | 
 | 22 |  * | 
 | 23 |  * Trademarks are the property of their respective owners. | 
 | 24 |  */ | 
 | 25 |  | 
 | 26 | #include <linux/module.h> | 
| Dmitry Torokhov | 7705d54 | 2009-12-03 23:21:14 -0800 | [diff] [blame] | 27 | #include <linux/dmi.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 28 | #include <linux/input.h> | 
 | 29 | #include <linux/serio.h> | 
 | 30 | #include <linux/libps2.h> | 
 | 31 | #include "psmouse.h" | 
 | 32 | #include "synaptics.h" | 
 | 33 |  | 
 | 34 | /* | 
 | 35 |  * The x/y limits are taken from the Synaptics TouchPad interfacing Guide, | 
 | 36 |  * section 2.3.2, which says that they should be valid regardless of the | 
 | 37 |  * actual size of the sensor. | 
 | 38 |  */ | 
 | 39 | #define XMIN_NOMINAL 1472 | 
 | 40 | #define XMAX_NOMINAL 5472 | 
 | 41 | #define YMIN_NOMINAL 1408 | 
 | 42 | #define YMAX_NOMINAL 4448 | 
 | 43 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 |  | 
| Andres Salomon | 55e3d92 | 2007-03-10 01:39:54 -0500 | [diff] [blame] | 45 | /***************************************************************************** | 
 | 46 |  *	Stuff we need even when we do not want native Synaptics support | 
 | 47 |  ****************************************************************************/ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 |  | 
 | 49 | /* | 
 | 50 |  * Set the synaptics touchpad mode byte by special commands | 
 | 51 |  */ | 
 | 52 | static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode) | 
 | 53 | { | 
 | 54 | 	unsigned char param[1]; | 
 | 55 |  | 
 | 56 | 	if (psmouse_sliced_command(psmouse, mode)) | 
 | 57 | 		return -1; | 
 | 58 | 	param[0] = SYN_PS_SET_MODE2; | 
 | 59 | 	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE)) | 
 | 60 | 		return -1; | 
 | 61 | 	return 0; | 
 | 62 | } | 
 | 63 |  | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 64 | int synaptics_detect(struct psmouse *psmouse, bool set_properties) | 
| Andres Salomon | 55e3d92 | 2007-03-10 01:39:54 -0500 | [diff] [blame] | 65 | { | 
 | 66 | 	struct ps2dev *ps2dev = &psmouse->ps2dev; | 
 | 67 | 	unsigned char param[4]; | 
 | 68 |  | 
 | 69 | 	param[0] = 0; | 
 | 70 |  | 
 | 71 | 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | 
 | 72 | 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | 
 | 73 | 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | 
 | 74 | 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | 
 | 75 | 	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); | 
 | 76 |  | 
 | 77 | 	if (param[1] != 0x47) | 
 | 78 | 		return -ENODEV; | 
 | 79 |  | 
 | 80 | 	if (set_properties) { | 
 | 81 | 		psmouse->vendor = "Synaptics"; | 
 | 82 | 		psmouse->name = "TouchPad"; | 
 | 83 | 	} | 
 | 84 |  | 
 | 85 | 	return 0; | 
 | 86 | } | 
 | 87 |  | 
 | 88 | void synaptics_reset(struct psmouse *psmouse) | 
 | 89 | { | 
 | 90 | 	/* reset touchpad back to relative mode, gestures enabled */ | 
 | 91 | 	synaptics_mode_cmd(psmouse, 0); | 
 | 92 | } | 
 | 93 |  | 
 | 94 | #ifdef CONFIG_MOUSE_PS2_SYNAPTICS | 
 | 95 |  | 
 | 96 | /***************************************************************************** | 
 | 97 |  *	Synaptics communications functions | 
 | 98 |  ****************************************************************************/ | 
 | 99 |  | 
 | 100 | /* | 
 | 101 |  * Send a command to the synpatics touchpad by special commands | 
 | 102 |  */ | 
 | 103 | static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) | 
 | 104 | { | 
 | 105 | 	if (psmouse_sliced_command(psmouse, c)) | 
 | 106 | 		return -1; | 
 | 107 | 	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) | 
 | 108 | 		return -1; | 
 | 109 | 	return 0; | 
 | 110 | } | 
 | 111 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 112 | /* | 
 | 113 |  * Read the model-id bytes from the touchpad | 
 | 114 |  * see also SYN_MODEL_* macros | 
 | 115 |  */ | 
 | 116 | static int synaptics_model_id(struct psmouse *psmouse) | 
 | 117 | { | 
 | 118 | 	struct synaptics_data *priv = psmouse->private; | 
 | 119 | 	unsigned char mi[3]; | 
 | 120 |  | 
 | 121 | 	if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi)) | 
 | 122 | 		return -1; | 
 | 123 | 	priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2]; | 
 | 124 | 	return 0; | 
 | 125 | } | 
 | 126 |  | 
 | 127 | /* | 
 | 128 |  * Read the capability-bits from the touchpad | 
 | 129 |  * see also the SYN_CAP_* macros | 
 | 130 |  */ | 
 | 131 | static int synaptics_capability(struct psmouse *psmouse) | 
 | 132 | { | 
 | 133 | 	struct synaptics_data *priv = psmouse->private; | 
 | 134 | 	unsigned char cap[3]; | 
 | 135 |  | 
 | 136 | 	if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) | 
 | 137 | 		return -1; | 
 | 138 | 	priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | 
 | 139 | 	priv->ext_cap = 0; | 
 | 140 | 	if (!SYN_CAP_VALID(priv->capabilities)) | 
 | 141 | 		return -1; | 
 | 142 |  | 
 | 143 | 	/* | 
 | 144 | 	 * Unless capExtended is set the rest of the flags should be ignored | 
 | 145 | 	 */ | 
 | 146 | 	if (!SYN_CAP_EXTENDED(priv->capabilities)) | 
 | 147 | 		priv->capabilities = 0; | 
 | 148 |  | 
 | 149 | 	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) { | 
 | 150 | 		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { | 
 | 151 | 			printk(KERN_ERR "Synaptics claims to have extended capabilities," | 
 | 152 | 			       " but I'm not able to read them."); | 
 | 153 | 		} else { | 
 | 154 | 			priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | 
 | 155 |  | 
 | 156 | 			/* | 
 | 157 | 			 * if nExtBtn is greater than 8 it should be considered | 
 | 158 | 			 * invalid and treated as 0 | 
 | 159 | 			 */ | 
 | 160 | 			if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8) | 
 | 161 | 				priv->ext_cap &= 0xff0fff; | 
 | 162 | 		} | 
 | 163 | 	} | 
 | 164 | 	return 0; | 
 | 165 | } | 
 | 166 |  | 
 | 167 | /* | 
 | 168 |  * Identify Touchpad | 
 | 169 |  * See also the SYN_ID_* macros | 
 | 170 |  */ | 
 | 171 | static int synaptics_identify(struct psmouse *psmouse) | 
 | 172 | { | 
 | 173 | 	struct synaptics_data *priv = psmouse->private; | 
 | 174 | 	unsigned char id[3]; | 
 | 175 |  | 
 | 176 | 	if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id)) | 
 | 177 | 		return -1; | 
 | 178 | 	priv->identity = (id[0]<<16) | (id[1]<<8) | id[2]; | 
 | 179 | 	if (SYN_ID_IS_SYNAPTICS(priv->identity)) | 
 | 180 | 		return 0; | 
 | 181 | 	return -1; | 
 | 182 | } | 
 | 183 |  | 
| Tero Saarni | ec20a02 | 2009-06-10 23:27:24 -0700 | [diff] [blame] | 184 | /* | 
 | 185 |  * Read touchpad resolution | 
 | 186 |  * Resolution is left zero if touchpad does not support the query | 
 | 187 |  */ | 
 | 188 | static int synaptics_resolution(struct psmouse *psmouse) | 
 | 189 | { | 
 | 190 | 	struct synaptics_data *priv = psmouse->private; | 
 | 191 | 	unsigned char res[3]; | 
 | 192 |  | 
 | 193 | 	if (SYN_ID_MAJOR(priv->identity) < 4) | 
 | 194 | 		return 0; | 
 | 195 |  | 
 | 196 | 	if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res)) | 
 | 197 | 		return 0; | 
 | 198 |  | 
 | 199 | 	if ((res[0] != 0) && (res[1] & 0x80) && (res[2] != 0)) { | 
 | 200 | 		priv->x_res = res[0]; /* x resolution in units/mm */ | 
 | 201 | 		priv->y_res = res[2]; /* y resolution in units/mm */ | 
 | 202 | 	} | 
 | 203 |  | 
 | 204 | 	return 0; | 
 | 205 | } | 
 | 206 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | static int synaptics_query_hardware(struct psmouse *psmouse) | 
 | 208 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 209 | 	if (synaptics_identify(psmouse)) | 
 | 210 | 		return -1; | 
 | 211 | 	if (synaptics_model_id(psmouse)) | 
 | 212 | 		return -1; | 
 | 213 | 	if (synaptics_capability(psmouse)) | 
 | 214 | 		return -1; | 
| Tero Saarni | ec20a02 | 2009-06-10 23:27:24 -0700 | [diff] [blame] | 215 | 	if (synaptics_resolution(psmouse)) | 
 | 216 | 		return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 217 |  | 
 | 218 | 	return 0; | 
 | 219 | } | 
 | 220 |  | 
 | 221 | static int synaptics_set_absolute_mode(struct psmouse *psmouse) | 
 | 222 | { | 
 | 223 | 	struct synaptics_data *priv = psmouse->private; | 
 | 224 |  | 
 | 225 | 	priv->mode = SYN_BIT_ABSOLUTE_MODE; | 
 | 226 | 	if (SYN_ID_MAJOR(priv->identity) >= 4) | 
 | 227 | 		priv->mode |= SYN_BIT_DISABLE_GESTURE; | 
 | 228 | 	if (SYN_CAP_EXTENDED(priv->capabilities)) | 
 | 229 | 		priv->mode |= SYN_BIT_W_MODE; | 
 | 230 |  | 
 | 231 | 	if (synaptics_mode_cmd(psmouse, priv->mode)) | 
 | 232 | 		return -1; | 
 | 233 |  | 
 | 234 | 	return 0; | 
 | 235 | } | 
 | 236 |  | 
 | 237 | static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate) | 
 | 238 | { | 
 | 239 | 	struct synaptics_data *priv = psmouse->private; | 
 | 240 |  | 
 | 241 | 	if (rate >= 80) { | 
 | 242 | 		priv->mode |= SYN_BIT_HIGH_RATE; | 
 | 243 | 		psmouse->rate = 80; | 
 | 244 | 	} else { | 
 | 245 | 		priv->mode &= ~SYN_BIT_HIGH_RATE; | 
 | 246 | 		psmouse->rate = 40; | 
 | 247 | 	} | 
 | 248 |  | 
 | 249 | 	synaptics_mode_cmd(psmouse, priv->mode); | 
 | 250 | } | 
 | 251 |  | 
 | 252 | /***************************************************************************** | 
 | 253 |  *	Synaptics pass-through PS/2 port support | 
 | 254 |  ****************************************************************************/ | 
 | 255 | static int synaptics_pt_write(struct serio *serio, unsigned char c) | 
 | 256 | { | 
 | 257 | 	struct psmouse *parent = serio_get_drvdata(serio->parent); | 
 | 258 | 	char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ | 
 | 259 |  | 
 | 260 | 	if (psmouse_sliced_command(parent, c)) | 
 | 261 | 		return -1; | 
 | 262 | 	if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE)) | 
 | 263 | 		return -1; | 
 | 264 | 	return 0; | 
 | 265 | } | 
 | 266 |  | 
 | 267 | static inline int synaptics_is_pt_packet(unsigned char *buf) | 
 | 268 | { | 
 | 269 | 	return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; | 
 | 270 | } | 
 | 271 |  | 
 | 272 | static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) | 
 | 273 | { | 
 | 274 | 	struct psmouse *child = serio_get_drvdata(ptport); | 
 | 275 |  | 
 | 276 | 	if (child && child->state == PSMOUSE_ACTIVATED) { | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 277 | 		serio_interrupt(ptport, packet[1], 0); | 
 | 278 | 		serio_interrupt(ptport, packet[4], 0); | 
 | 279 | 		serio_interrupt(ptport, packet[5], 0); | 
| Sergey Vlasov | 33fdfa9 | 2005-07-24 00:53:32 -0500 | [diff] [blame] | 280 | 		if (child->pktsize == 4) | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 281 | 			serio_interrupt(ptport, packet[2], 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 | 	} else | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 283 | 		serio_interrupt(ptport, packet[1], 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 284 | } | 
 | 285 |  | 
 | 286 | static void synaptics_pt_activate(struct psmouse *psmouse) | 
 | 287 | { | 
 | 288 | 	struct serio *ptport = psmouse->ps2dev.serio->child; | 
 | 289 | 	struct psmouse *child = serio_get_drvdata(ptport); | 
 | 290 | 	struct synaptics_data *priv = psmouse->private; | 
 | 291 |  | 
 | 292 | 	/* adjust the touchpad to child's choice of protocol */ | 
 | 293 | 	if (child) { | 
| Sergey Vlasov | 33fdfa9 | 2005-07-24 00:53:32 -0500 | [diff] [blame] | 294 | 		if (child->pktsize == 4) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 295 | 			priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT; | 
 | 296 | 		else | 
 | 297 | 			priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT; | 
 | 298 |  | 
 | 299 | 		if (synaptics_mode_cmd(psmouse, priv->mode)) | 
 | 300 | 			printk(KERN_INFO "synaptics: failed to switch guest protocol\n"); | 
 | 301 | 	} | 
 | 302 | } | 
 | 303 |  | 
 | 304 | static void synaptics_pt_create(struct psmouse *psmouse) | 
 | 305 | { | 
 | 306 | 	struct serio *serio; | 
 | 307 |  | 
| Eric Sesterhenn | b39787a | 2006-03-14 00:09:16 -0500 | [diff] [blame] | 308 | 	serio = kzalloc(sizeof(struct serio), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 309 | 	if (!serio) { | 
 | 310 | 		printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n"); | 
 | 311 | 		return; | 
 | 312 | 	} | 
 | 313 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 314 | 	serio->id.type = SERIO_PS_PSTHRU; | 
 | 315 | 	strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); | 
 | 316 | 	strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); | 
 | 317 | 	serio->write = synaptics_pt_write; | 
 | 318 | 	serio->parent = psmouse->ps2dev.serio; | 
 | 319 |  | 
 | 320 | 	psmouse->pt_activate = synaptics_pt_activate; | 
 | 321 |  | 
 | 322 | 	printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys); | 
 | 323 | 	serio_register_port(serio); | 
 | 324 | } | 
 | 325 |  | 
 | 326 | /***************************************************************************** | 
 | 327 |  *	Functions to interpret the absolute mode packets | 
 | 328 |  ****************************************************************************/ | 
 | 329 |  | 
 | 330 | static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) | 
 | 331 | { | 
 | 332 | 	memset(hw, 0, sizeof(struct synaptics_hw_state)); | 
 | 333 |  | 
 | 334 | 	if (SYN_MODEL_NEWABS(priv->model_id)) { | 
 | 335 | 		hw->x = (((buf[3] & 0x10) << 8) | | 
 | 336 | 			 ((buf[1] & 0x0f) << 8) | | 
 | 337 | 			 buf[4]); | 
 | 338 | 		hw->y = (((buf[3] & 0x20) << 7) | | 
 | 339 | 			 ((buf[1] & 0xf0) << 4) | | 
 | 340 | 			 buf[5]); | 
 | 341 |  | 
 | 342 | 		hw->z = buf[2]; | 
 | 343 | 		hw->w = (((buf[0] & 0x30) >> 2) | | 
 | 344 | 			 ((buf[0] & 0x04) >> 1) | | 
 | 345 | 			 ((buf[3] & 0x04) >> 2)); | 
 | 346 |  | 
 | 347 | 		hw->left  = (buf[0] & 0x01) ? 1 : 0; | 
 | 348 | 		hw->right = (buf[0] & 0x02) ? 1 : 0; | 
 | 349 |  | 
 | 350 | 		if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { | 
 | 351 | 			hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | 
 | 352 | 			if (hw->w == 2) | 
 | 353 | 				hw->scroll = (signed char)(buf[1]); | 
 | 354 | 		} | 
 | 355 |  | 
 | 356 | 		if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { | 
 | 357 | 			hw->up   = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | 
 | 358 | 			hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; | 
 | 359 | 		} | 
 | 360 |  | 
 | 361 | 		if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && | 
 | 362 | 		    ((buf[0] ^ buf[3]) & 0x02)) { | 
 | 363 | 			switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { | 
 | 364 | 			default: | 
 | 365 | 				/* | 
 | 366 | 				 * if nExtBtn is greater than 8 it should be | 
 | 367 | 				 * considered invalid and treated as 0 | 
 | 368 | 				 */ | 
 | 369 | 				break; | 
 | 370 | 			case 8: | 
 | 371 | 				hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0; | 
 | 372 | 				hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0; | 
 | 373 | 			case 6: | 
 | 374 | 				hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0; | 
 | 375 | 				hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0; | 
 | 376 | 			case 4: | 
 | 377 | 				hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0; | 
 | 378 | 				hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0; | 
 | 379 | 			case 2: | 
 | 380 | 				hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0; | 
 | 381 | 				hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0; | 
 | 382 | 			} | 
 | 383 | 		} | 
 | 384 | 	} else { | 
 | 385 | 		hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); | 
 | 386 | 		hw->y = (((buf[4] & 0x1f) << 8) | buf[5]); | 
 | 387 |  | 
 | 388 | 		hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F)); | 
 | 389 | 		hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1)); | 
 | 390 |  | 
 | 391 | 		hw->left  = (buf[0] & 0x01) ? 1 : 0; | 
 | 392 | 		hw->right = (buf[0] & 0x02) ? 1 : 0; | 
 | 393 | 	} | 
 | 394 | } | 
 | 395 |  | 
 | 396 | /* | 
 | 397 |  *  called for each full received packet from the touchpad | 
 | 398 |  */ | 
 | 399 | static void synaptics_process_packet(struct psmouse *psmouse) | 
 | 400 | { | 
| Dmitry Torokhov | 2e5b636 | 2005-09-15 02:01:44 -0500 | [diff] [blame] | 401 | 	struct input_dev *dev = psmouse->dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 402 | 	struct synaptics_data *priv = psmouse->private; | 
 | 403 | 	struct synaptics_hw_state hw; | 
 | 404 | 	int num_fingers; | 
 | 405 | 	int finger_width; | 
 | 406 | 	int i; | 
 | 407 |  | 
 | 408 | 	synaptics_parse_hw_state(psmouse->packet, priv, &hw); | 
 | 409 |  | 
 | 410 | 	if (hw.scroll) { | 
 | 411 | 		priv->scroll += hw.scroll; | 
 | 412 |  | 
 | 413 | 		while (priv->scroll >= 4) { | 
 | 414 | 			input_report_key(dev, BTN_BACK, !hw.down); | 
 | 415 | 			input_sync(dev); | 
 | 416 | 			input_report_key(dev, BTN_BACK, hw.down); | 
 | 417 | 			input_sync(dev); | 
 | 418 | 			priv->scroll -= 4; | 
 | 419 | 		} | 
 | 420 | 		while (priv->scroll <= -4) { | 
 | 421 | 			input_report_key(dev, BTN_FORWARD, !hw.up); | 
 | 422 | 			input_sync(dev); | 
 | 423 | 			input_report_key(dev, BTN_FORWARD, hw.up); | 
 | 424 | 			input_sync(dev); | 
 | 425 | 			priv->scroll += 4; | 
 | 426 | 		} | 
 | 427 | 		return; | 
 | 428 | 	} | 
 | 429 |  | 
 | 430 | 	if (hw.z > 0) { | 
 | 431 | 		num_fingers = 1; | 
 | 432 | 		finger_width = 5; | 
 | 433 | 		if (SYN_CAP_EXTENDED(priv->capabilities)) { | 
 | 434 | 			switch (hw.w) { | 
 | 435 | 			case 0 ... 1: | 
 | 436 | 				if (SYN_CAP_MULTIFINGER(priv->capabilities)) | 
 | 437 | 					num_fingers = hw.w + 2; | 
 | 438 | 				break; | 
 | 439 | 			case 2: | 
 | 440 | 				if (SYN_MODEL_PEN(priv->model_id)) | 
 | 441 | 					;   /* Nothing, treat a pen as a single finger */ | 
 | 442 | 				break; | 
 | 443 | 			case 4 ... 15: | 
 | 444 | 				if (SYN_CAP_PALMDETECT(priv->capabilities)) | 
 | 445 | 					finger_width = hw.w; | 
 | 446 | 				break; | 
 | 447 | 			} | 
 | 448 | 		} | 
 | 449 | 	} else { | 
 | 450 | 		num_fingers = 0; | 
 | 451 | 		finger_width = 0; | 
 | 452 | 	} | 
 | 453 |  | 
 | 454 | 	/* Post events | 
 | 455 | 	 * BTN_TOUCH has to be first as mousedev relies on it when doing | 
 | 456 | 	 * absolute -> relative conversion | 
 | 457 | 	 */ | 
 | 458 | 	if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1); | 
 | 459 | 	if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0); | 
 | 460 |  | 
 | 461 | 	if (hw.z > 0) { | 
 | 462 | 		input_report_abs(dev, ABS_X, hw.x); | 
 | 463 | 		input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y); | 
 | 464 | 	} | 
 | 465 | 	input_report_abs(dev, ABS_PRESSURE, hw.z); | 
 | 466 |  | 
 | 467 | 	input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); | 
 | 468 | 	input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 469 | 	input_report_key(dev, BTN_LEFT, hw.left); | 
 | 470 | 	input_report_key(dev, BTN_RIGHT, hw.right); | 
 | 471 |  | 
| Peter Hutterer | e42b664 | 2008-11-20 15:24:42 -0500 | [diff] [blame] | 472 | 	if (SYN_CAP_MULTIFINGER(priv->capabilities)) { | 
 | 473 | 		input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); | 
 | 474 | 		input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); | 
 | 475 | 	} | 
 | 476 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 477 | 	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | 
 | 478 | 		input_report_key(dev, BTN_MIDDLE, hw.middle); | 
 | 479 |  | 
 | 480 | 	if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { | 
 | 481 | 		input_report_key(dev, BTN_FORWARD, hw.up); | 
 | 482 | 		input_report_key(dev, BTN_BACK, hw.down); | 
 | 483 | 	} | 
 | 484 |  | 
 | 485 | 	for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) | 
 | 486 | 		input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i)); | 
 | 487 |  | 
 | 488 | 	input_sync(dev); | 
 | 489 | } | 
 | 490 |  | 
 | 491 | static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type) | 
 | 492 | { | 
| Helge Deller | e38de67 | 2006-09-10 21:54:39 -0400 | [diff] [blame] | 493 | 	static const unsigned char newabs_mask[]	= { 0xC8, 0x00, 0x00, 0xC8, 0x00 }; | 
 | 494 | 	static const unsigned char newabs_rel_mask[]	= { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; | 
 | 495 | 	static const unsigned char newabs_rslt[]	= { 0x80, 0x00, 0x00, 0xC0, 0x00 }; | 
 | 496 | 	static const unsigned char oldabs_mask[]	= { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; | 
 | 497 | 	static const unsigned char oldabs_rslt[]	= { 0xC0, 0x00, 0x00, 0x80, 0x00 }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 498 |  | 
 | 499 | 	if (idx < 0 || idx > 4) | 
 | 500 | 		return 0; | 
 | 501 |  | 
 | 502 | 	switch (pkt_type) { | 
 | 503 | 		case SYN_NEWABS: | 
 | 504 | 		case SYN_NEWABS_RELAXED: | 
 | 505 | 			return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx]; | 
 | 506 |  | 
 | 507 | 		case SYN_NEWABS_STRICT: | 
 | 508 | 			return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; | 
 | 509 |  | 
 | 510 | 		case SYN_OLDABS: | 
 | 511 | 			return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; | 
 | 512 |  | 
 | 513 | 		default: | 
 | 514 | 			printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type); | 
 | 515 | 			return 0; | 
 | 516 | 	} | 
 | 517 | } | 
 | 518 |  | 
 | 519 | static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse) | 
 | 520 | { | 
 | 521 | 	int i; | 
 | 522 |  | 
 | 523 | 	for (i = 0; i < 5; i++) | 
 | 524 | 		if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) { | 
 | 525 | 			printk(KERN_INFO "synaptics: using relaxed packet validation\n"); | 
 | 526 | 			return SYN_NEWABS_RELAXED; | 
 | 527 | 		} | 
 | 528 |  | 
 | 529 | 	return SYN_NEWABS_STRICT; | 
 | 530 | } | 
 | 531 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 532 | static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 533 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 534 | 	struct synaptics_data *priv = psmouse->private; | 
 | 535 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 536 | 	if (psmouse->pktcnt >= 6) { /* Full packet received */ | 
 | 537 | 		if (unlikely(priv->pkt_type == SYN_NEWABS)) | 
 | 538 | 			priv->pkt_type = synaptics_detect_pkt_type(psmouse); | 
 | 539 |  | 
 | 540 | 		if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { | 
 | 541 | 			if (psmouse->ps2dev.serio->child) | 
 | 542 | 				synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet); | 
 | 543 | 		} else | 
 | 544 | 			synaptics_process_packet(psmouse); | 
 | 545 |  | 
 | 546 | 		return PSMOUSE_FULL_PACKET; | 
 | 547 | 	} | 
 | 548 |  | 
 | 549 | 	return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ? | 
 | 550 | 		PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; | 
 | 551 | } | 
 | 552 |  | 
 | 553 | /***************************************************************************** | 
 | 554 |  *	Driver initialization/cleanup functions | 
 | 555 |  ****************************************************************************/ | 
 | 556 | static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) | 
 | 557 | { | 
 | 558 | 	int i; | 
 | 559 |  | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 560 | 	__set_bit(EV_ABS, dev->evbit); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 561 | 	input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); | 
 | 562 | 	input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); | 
 | 563 | 	input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 564 | 	__set_bit(ABS_TOOL_WIDTH, dev->absbit); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 565 |  | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 566 | 	__set_bit(EV_KEY, dev->evbit); | 
 | 567 | 	__set_bit(BTN_TOUCH, dev->keybit); | 
 | 568 | 	__set_bit(BTN_TOOL_FINGER, dev->keybit); | 
 | 569 | 	__set_bit(BTN_LEFT, dev->keybit); | 
 | 570 | 	__set_bit(BTN_RIGHT, dev->keybit); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 571 |  | 
| Peter Hutterer | e42b664 | 2008-11-20 15:24:42 -0500 | [diff] [blame] | 572 | 	if (SYN_CAP_MULTIFINGER(priv->capabilities)) { | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 573 | 		__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); | 
 | 574 | 		__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); | 
| Peter Hutterer | e42b664 | 2008-11-20 15:24:42 -0500 | [diff] [blame] | 575 | 	} | 
 | 576 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 577 | 	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 578 | 		__set_bit(BTN_MIDDLE, dev->keybit); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 579 |  | 
 | 580 | 	if (SYN_CAP_FOUR_BUTTON(priv->capabilities) || | 
 | 581 | 	    SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 582 | 		__set_bit(BTN_FORWARD, dev->keybit); | 
 | 583 | 		__set_bit(BTN_BACK, dev->keybit); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 584 | 	} | 
 | 585 |  | 
 | 586 | 	for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 587 | 		__set_bit(BTN_0 + i, dev->keybit); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 588 |  | 
| Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 589 | 	__clear_bit(EV_REL, dev->evbit); | 
 | 590 | 	__clear_bit(REL_X, dev->relbit); | 
 | 591 | 	__clear_bit(REL_Y, dev->relbit); | 
| Tero Saarni | ec20a02 | 2009-06-10 23:27:24 -0700 | [diff] [blame] | 592 |  | 
 | 593 | 	dev->absres[ABS_X] = priv->x_res; | 
 | 594 | 	dev->absres[ABS_Y] = priv->y_res; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 595 | } | 
 | 596 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 597 | static void synaptics_disconnect(struct psmouse *psmouse) | 
 | 598 | { | 
 | 599 | 	synaptics_reset(psmouse); | 
 | 600 | 	kfree(psmouse->private); | 
 | 601 | 	psmouse->private = NULL; | 
 | 602 | } | 
 | 603 |  | 
 | 604 | static int synaptics_reconnect(struct psmouse *psmouse) | 
 | 605 | { | 
 | 606 | 	struct synaptics_data *priv = psmouse->private; | 
 | 607 | 	struct synaptics_data old_priv = *priv; | 
 | 608 |  | 
| Andy Whitcroft | 4d36845 | 2009-02-28 12:51:01 -0800 | [diff] [blame] | 609 | 	psmouse_reset(psmouse); | 
 | 610 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 611 | 	if (synaptics_detect(psmouse, 0)) | 
 | 612 | 		return -1; | 
 | 613 |  | 
 | 614 | 	if (synaptics_query_hardware(psmouse)) { | 
 | 615 | 		printk(KERN_ERR "Unable to query Synaptics hardware.\n"); | 
 | 616 | 		return -1; | 
 | 617 | 	} | 
 | 618 |  | 
 | 619 | 	if (old_priv.identity != priv->identity || | 
 | 620 | 	    old_priv.model_id != priv->model_id || | 
 | 621 | 	    old_priv.capabilities != priv->capabilities || | 
 | 622 | 	    old_priv.ext_cap != priv->ext_cap) | 
 | 623 | 		return -1; | 
 | 624 |  | 
 | 625 | 	if (synaptics_set_absolute_mode(psmouse)) { | 
 | 626 | 		printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); | 
 | 627 | 		return -1; | 
 | 628 | 	} | 
 | 629 |  | 
 | 630 | 	return 0; | 
 | 631 | } | 
 | 632 |  | 
| Dmitry Torokhov | 7705d54 | 2009-12-03 23:21:14 -0800 | [diff] [blame] | 633 | static bool impaired_toshiba_kbc; | 
 | 634 |  | 
 | 635 | static const struct dmi_system_id __initconst toshiba_dmi_table[] = { | 
 | 636 | #if defined(CONFIG_DMI) && defined(CONFIG_X86) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 637 | 	{ | 
| Dmitry Torokhov | 9961e25 | 2009-12-04 10:24:20 -0800 | [diff] [blame] | 638 | 		/* Toshiba Satellite */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 639 | 		.matches = { | 
 | 640 | 			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 
| Richard Thrippleton | 53a2670 | 2006-04-02 00:10:18 -0500 | [diff] [blame] | 641 | 			DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 642 | 		}, | 
 | 643 | 	}, | 
| Simon Horman | 9ba5eaa | 2005-07-11 01:07:20 -0500 | [diff] [blame] | 644 | 	{ | 
| Dmitry Torokhov | 9961e25 | 2009-12-04 10:24:20 -0800 | [diff] [blame] | 645 | 		/* Toshiba Dynabook */ | 
| Simon Horman | 9ba5eaa | 2005-07-11 01:07:20 -0500 | [diff] [blame] | 646 | 		.matches = { | 
 | 647 | 			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 
| Richard Thrippleton | 53a2670 | 2006-04-02 00:10:18 -0500 | [diff] [blame] | 648 | 			DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), | 
 | 649 | 		}, | 
 | 650 | 	}, | 
 | 651 | 	{ | 
| Dmitry Torokhov | 9961e25 | 2009-12-04 10:24:20 -0800 | [diff] [blame] | 652 | 		/* Toshiba Portege M300 */ | 
| Richard Thrippleton | 53a2670 | 2006-04-02 00:10:18 -0500 | [diff] [blame] | 653 | 		.matches = { | 
 | 654 | 			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 
 | 655 | 			DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), | 
| Simon Horman | 9ba5eaa | 2005-07-11 01:07:20 -0500 | [diff] [blame] | 656 | 		}, | 
| Dmitry Torokhov | 5f5eeff | 2009-10-12 21:35:00 -0700 | [diff] [blame] | 657 |  | 
 | 658 | 	}, | 
 | 659 | 	{ | 
| Dmitry Torokhov | 9961e25 | 2009-12-04 10:24:20 -0800 | [diff] [blame] | 660 | 		/* Toshiba Portege M300 */ | 
| Dmitry Torokhov | 5f5eeff | 2009-10-12 21:35:00 -0700 | [diff] [blame] | 661 | 		.matches = { | 
 | 662 | 			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 
 | 663 | 			DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), | 
 | 664 | 			DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"), | 
 | 665 | 		}, | 
 | 666 |  | 
| Simon Horman | 9ba5eaa | 2005-07-11 01:07:20 -0500 | [diff] [blame] | 667 | 	}, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 668 | 	{ } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 669 | #endif | 
| Dmitry Torokhov | 7705d54 | 2009-12-03 23:21:14 -0800 | [diff] [blame] | 670 | }; | 
 | 671 |  | 
 | 672 | void __init synaptics_module_init(void) | 
 | 673 | { | 
 | 674 | 	impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); | 
 | 675 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 676 |  | 
 | 677 | int synaptics_init(struct psmouse *psmouse) | 
 | 678 | { | 
 | 679 | 	struct synaptics_data *priv; | 
 | 680 |  | 
| Eric Sesterhenn | b39787a | 2006-03-14 00:09:16 -0500 | [diff] [blame] | 681 | 	psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 682 | 	if (!priv) | 
 | 683 | 		return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 684 |  | 
| Andy Whitcroft | 4d36845 | 2009-02-28 12:51:01 -0800 | [diff] [blame] | 685 | 	psmouse_reset(psmouse); | 
 | 686 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 687 | 	if (synaptics_query_hardware(psmouse)) { | 
 | 688 | 		printk(KERN_ERR "Unable to query Synaptics hardware.\n"); | 
 | 689 | 		goto init_fail; | 
 | 690 | 	} | 
 | 691 |  | 
 | 692 | 	if (synaptics_set_absolute_mode(psmouse)) { | 
 | 693 | 		printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); | 
 | 694 | 		goto init_fail; | 
 | 695 | 	} | 
 | 696 |  | 
 | 697 | 	priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; | 
 | 698 |  | 
| Dmitry Torokhov | 409b750 | 2005-05-28 02:12:18 -0500 | [diff] [blame] | 699 | 	printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx\n", | 
 | 700 | 		SYN_ID_MODEL(priv->identity), | 
 | 701 | 		SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), | 
 | 702 | 		priv->model_id, priv->capabilities, priv->ext_cap); | 
 | 703 |  | 
| Dmitry Torokhov | 2e5b636 | 2005-09-15 02:01:44 -0500 | [diff] [blame] | 704 | 	set_input_params(psmouse->dev, priv); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 705 |  | 
| Dmitry Torokhov | 887cc12 | 2007-04-12 01:30:41 -0400 | [diff] [blame] | 706 | 	/* | 
 | 707 | 	 * Encode touchpad model so that it can be used to set | 
 | 708 | 	 * input device->id.version and be visible to userspace. | 
 | 709 | 	 * Because version is __u16 we have to drop something. | 
 | 710 | 	 * Hardware info bits seem to be good candidates as they | 
 | 711 | 	 * are documented to be for Synaptics corp. internal use. | 
 | 712 | 	 */ | 
 | 713 | 	psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) | | 
 | 714 | 			  (priv->model_id & 0x000000ff); | 
 | 715 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 716 | 	psmouse->protocol_handler = synaptics_process_byte; | 
 | 717 | 	psmouse->set_rate = synaptics_set_rate; | 
 | 718 | 	psmouse->disconnect = synaptics_disconnect; | 
 | 719 | 	psmouse->reconnect = synaptics_reconnect; | 
| Dmitry Torokhov | a1cec06 | 2007-02-18 01:40:24 -0500 | [diff] [blame] | 720 | 	psmouse->cleanup = synaptics_reset; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 721 | 	psmouse->pktsize = 6; | 
| Dmitry Torokhov | f0d5c6f4 | 2006-01-14 00:27:37 -0500 | [diff] [blame] | 722 | 	/* Synaptics can usually stay in sync without extra help */ | 
 | 723 | 	psmouse->resync_time = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 724 |  | 
 | 725 | 	if (SYN_CAP_PASS_THROUGH(priv->capabilities)) | 
 | 726 | 		synaptics_pt_create(psmouse); | 
 | 727 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 728 | 	/* | 
 | 729 | 	 * Toshiba's KBC seems to have trouble handling data from | 
 | 730 | 	 * Synaptics as full rate, switch to lower rate which is roughly | 
 | 731 | 	 * thye same as rate of standard PS/2 mouse. | 
 | 732 | 	 */ | 
| Dmitry Torokhov | 7705d54 | 2009-12-03 23:21:14 -0800 | [diff] [blame] | 733 | 	if (psmouse->rate >= 80 && impaired_toshiba_kbc) { | 
| Simon Horman | 9ba5eaa | 2005-07-11 01:07:20 -0500 | [diff] [blame] | 734 | 		printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n", | 
 | 735 | 			dmi_get_system_info(DMI_PRODUCT_NAME)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 736 | 		psmouse->rate = 40; | 
 | 737 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 738 |  | 
 | 739 | 	return 0; | 
 | 740 |  | 
 | 741 |  init_fail: | 
 | 742 | 	kfree(priv); | 
 | 743 | 	return -1; | 
 | 744 | } | 
 | 745 |  | 
| Daniel Drake | e4e6efd | 2010-01-07 01:52:39 -0800 | [diff] [blame] | 746 | bool synaptics_supported(void) | 
 | 747 | { | 
 | 748 | 	return true; | 
 | 749 | } | 
 | 750 |  | 
| Andres Salomon | 55e3d92 | 2007-03-10 01:39:54 -0500 | [diff] [blame] | 751 | #else /* CONFIG_MOUSE_PS2_SYNAPTICS */ | 
 | 752 |  | 
| Dmitry Torokhov | 7705d54 | 2009-12-03 23:21:14 -0800 | [diff] [blame] | 753 | void __init synaptics_module_init(void) | 
 | 754 | { | 
 | 755 | } | 
 | 756 |  | 
| Andres Salomon | 55e3d92 | 2007-03-10 01:39:54 -0500 | [diff] [blame] | 757 | int synaptics_init(struct psmouse *psmouse) | 
 | 758 | { | 
 | 759 | 	return -ENOSYS; | 
 | 760 | } | 
 | 761 |  | 
| Daniel Drake | e4e6efd | 2010-01-07 01:52:39 -0800 | [diff] [blame] | 762 | bool synaptics_supported(void) | 
 | 763 | { | 
 | 764 | 	return false; | 
 | 765 | } | 
 | 766 |  | 
| Andres Salomon | 55e3d92 | 2007-03-10 01:39:54 -0500 | [diff] [blame] | 767 | #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 768 |  |