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