blob: 6ad728f0e287d44c3f8c0c9a0c838a334a57f804 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 Torokhov5cb6d282011-12-12 00:05:53 -080027#include <linux/delay.h>
Dmitry Torokhov7705d542009-12-03 23:21:14 -080028#include <linux/dmi.h>
Henrik Rydbergfec6e522010-12-21 18:11:25 +010029#include <linux/input/mt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/serio.h>
31#include <linux/libps2.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090032#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include "psmouse.h"
34#include "synaptics.h"
35
36/*
37 * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
38 * section 2.3.2, which says that they should be valid regardless of the
39 * actual size of the sensor.
Dmitry Torokhov83ba9ea2010-05-10 23:06:52 -070040 * Note that newer firmware allows querying device for maximum useable
41 * coordinates.
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 */
43#define XMIN_NOMINAL 1472
44#define XMAX_NOMINAL 5472
45#define YMIN_NOMINAL 1408
46#define YMAX_NOMINAL 4448
47
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Andres Salomon55e3d922007-03-10 01:39:54 -050049/*****************************************************************************
50 * Stuff we need even when we do not want native Synaptics support
51 ****************************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53/*
54 * Set the synaptics touchpad mode byte by special commands
55 */
56static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
57{
58 unsigned char param[1];
59
60 if (psmouse_sliced_command(psmouse, mode))
61 return -1;
62 param[0] = SYN_PS_SET_MODE2;
63 if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
64 return -1;
65 return 0;
66}
67
Dmitry Torokhovb7802c52009-09-09 19:13:20 -070068int synaptics_detect(struct psmouse *psmouse, bool set_properties)
Andres Salomon55e3d922007-03-10 01:39:54 -050069{
70 struct ps2dev *ps2dev = &psmouse->ps2dev;
71 unsigned char param[4];
72
73 param[0] = 0;
74
75 ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
76 ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
77 ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
78 ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
79 ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
80
81 if (param[1] != 0x47)
82 return -ENODEV;
83
84 if (set_properties) {
85 psmouse->vendor = "Synaptics";
86 psmouse->name = "TouchPad";
87 }
88
89 return 0;
90}
91
92void synaptics_reset(struct psmouse *psmouse)
93{
94 /* reset touchpad back to relative mode, gestures enabled */
95 synaptics_mode_cmd(psmouse, 0);
96}
97
98#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
99
100/*****************************************************************************
101 * Synaptics communications functions
102 ****************************************************************************/
103
104/*
105 * Send a command to the synpatics touchpad by special commands
106 */
107static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
108{
109 if (psmouse_sliced_command(psmouse, c))
110 return -1;
111 if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
112 return -1;
113 return 0;
114}
115
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116/*
117 * Read the model-id bytes from the touchpad
118 * see also SYN_MODEL_* macros
119 */
120static int synaptics_model_id(struct psmouse *psmouse)
121{
122 struct synaptics_data *priv = psmouse->private;
123 unsigned char mi[3];
124
125 if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
126 return -1;
127 priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
128 return 0;
129}
130
131/*
132 * Read the capability-bits from the touchpad
133 * see also the SYN_CAP_* macros
134 */
135static int synaptics_capability(struct psmouse *psmouse)
136{
137 struct synaptics_data *priv = psmouse->private;
138 unsigned char cap[3];
139
140 if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
141 return -1;
142 priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
Takashi Iwai5f57d672010-04-19 10:37:21 -0700143 priv->ext_cap = priv->ext_cap_0c = 0;
144
Dmitry Torokhov3619b8f2010-07-21 00:01:19 -0700145 /*
146 * Older firmwares had submodel ID fixed to 0x47
147 */
148 if (SYN_ID_FULL(priv->identity) < 0x705 &&
149 SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 return -1;
Dmitry Torokhov3619b8f2010-07-21 00:01:19 -0700151 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153 /*
154 * Unless capExtended is set the rest of the flags should be ignored
155 */
156 if (!SYN_CAP_EXTENDED(priv->capabilities))
157 priv->capabilities = 0;
158
159 if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
160 if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
161 printk(KERN_ERR "Synaptics claims to have extended capabilities,"
Takashi Iwai5f57d672010-04-19 10:37:21 -0700162 " but I'm not able to read them.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 } else {
164 priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
165
166 /*
167 * if nExtBtn is greater than 8 it should be considered
168 * invalid and treated as 0
169 */
170 if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
171 priv->ext_cap &= 0xff0fff;
172 }
173 }
Takashi Iwai5f57d672010-04-19 10:37:21 -0700174
175 if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) {
176 if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) {
177 printk(KERN_ERR "Synaptics claims to have extended capability 0x0c,"
178 " but I'm not able to read it.\n");
179 } else {
180 priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2];
181 }
182 }
183
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 return 0;
185}
186
187/*
188 * Identify Touchpad
189 * See also the SYN_ID_* macros
190 */
191static int synaptics_identify(struct psmouse *psmouse)
192{
193 struct synaptics_data *priv = psmouse->private;
194 unsigned char id[3];
195
196 if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
197 return -1;
198 priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
199 if (SYN_ID_IS_SYNAPTICS(priv->identity))
200 return 0;
201 return -1;
202}
203
Tero Saarniec20a022009-06-10 23:27:24 -0700204/*
Dmitry Torokhov83ba9ea2010-05-10 23:06:52 -0700205 * Read touchpad resolution and maximum reported coordinates
Tero Saarniec20a022009-06-10 23:27:24 -0700206 * Resolution is left zero if touchpad does not support the query
207 */
208static int synaptics_resolution(struct psmouse *psmouse)
209{
210 struct synaptics_data *priv = psmouse->private;
211 unsigned char res[3];
Dmitry Torokhov83ba9ea2010-05-10 23:06:52 -0700212 unsigned char max[3];
Tero Saarniec20a022009-06-10 23:27:24 -0700213
214 if (SYN_ID_MAJOR(priv->identity) < 4)
Takashi Iwaibbddd192010-07-14 09:32:46 -0700215 return 0;
Tero Saarniec20a022009-06-10 23:27:24 -0700216
Dmitry Torokhov83ba9ea2010-05-10 23:06:52 -0700217 if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res) == 0) {
218 if (res[0] != 0 && (res[1] & 0x80) && res[2] != 0) {
219 priv->x_res = res[0]; /* x resolution in units/mm */
220 priv->y_res = res[2]; /* y resolution in units/mm */
221 }
222 }
Tero Saarniec20a022009-06-10 23:27:24 -0700223
Dmitry Torokhov83ba9ea2010-05-10 23:06:52 -0700224 if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
225 SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
226 if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_DIMENSIONS, max)) {
227 printk(KERN_ERR "Synaptics claims to have dimensions query,"
228 " but I'm not able to read it.\n");
229 } else {
230 priv->x_max = (max[0] << 5) | ((max[1] & 0x0f) << 1);
231 priv->y_max = (max[2] << 5) | ((max[1] & 0xf0) >> 3);
232 }
Tero Saarniec20a022009-06-10 23:27:24 -0700233 }
234
235 return 0;
236}
237
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238static int synaptics_query_hardware(struct psmouse *psmouse)
239{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 if (synaptics_identify(psmouse))
241 return -1;
242 if (synaptics_model_id(psmouse))
243 return -1;
244 if (synaptics_capability(psmouse))
245 return -1;
Tero Saarniec20a022009-06-10 23:27:24 -0700246 if (synaptics_resolution(psmouse))
247 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249 return 0;
250}
251
252static int synaptics_set_absolute_mode(struct psmouse *psmouse)
253{
254 struct synaptics_data *priv = psmouse->private;
255
256 priv->mode = SYN_BIT_ABSOLUTE_MODE;
257 if (SYN_ID_MAJOR(priv->identity) >= 4)
258 priv->mode |= SYN_BIT_DISABLE_GESTURE;
259 if (SYN_CAP_EXTENDED(priv->capabilities))
260 priv->mode |= SYN_BIT_W_MODE;
261
262 if (synaptics_mode_cmd(psmouse, priv->mode))
263 return -1;
264
265 return 0;
266}
267
268static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
269{
270 struct synaptics_data *priv = psmouse->private;
271
272 if (rate >= 80) {
273 priv->mode |= SYN_BIT_HIGH_RATE;
274 psmouse->rate = 80;
275 } else {
276 priv->mode &= ~SYN_BIT_HIGH_RATE;
277 psmouse->rate = 40;
278 }
279
280 synaptics_mode_cmd(psmouse, priv->mode);
281}
282
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100283static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
284{
285 static unsigned char param = 0xc8;
286 struct synaptics_data *priv = psmouse->private;
287
288 if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
289 return 0;
290
291 if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
292 return -1;
293 if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
294 return -1;
295
296 /* Advanced gesture mode also sends multi finger data */
297 priv->capabilities |= BIT(1);
298
299 return 0;
300}
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302/*****************************************************************************
303 * Synaptics pass-through PS/2 port support
304 ****************************************************************************/
305static int synaptics_pt_write(struct serio *serio, unsigned char c)
306{
307 struct psmouse *parent = serio_get_drvdata(serio->parent);
308 char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
309
310 if (psmouse_sliced_command(parent, c))
311 return -1;
312 if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
313 return -1;
314 return 0;
315}
316
Dmitry Torokhova8b3c0f2010-10-04 21:46:10 -0700317static int synaptics_pt_start(struct serio *serio)
318{
319 struct psmouse *parent = serio_get_drvdata(serio->parent);
320 struct synaptics_data *priv = parent->private;
321
322 serio_pause_rx(parent->ps2dev.serio);
323 priv->pt_port = serio;
324 serio_continue_rx(parent->ps2dev.serio);
325
326 return 0;
327}
328
329static void synaptics_pt_stop(struct serio *serio)
330{
331 struct psmouse *parent = serio_get_drvdata(serio->parent);
332 struct synaptics_data *priv = parent->private;
333
334 serio_pause_rx(parent->ps2dev.serio);
335 priv->pt_port = NULL;
336 serio_continue_rx(parent->ps2dev.serio);
337}
338
339static int synaptics_is_pt_packet(unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
341 return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
342}
343
344static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet)
345{
346 struct psmouse *child = serio_get_drvdata(ptport);
347
348 if (child && child->state == PSMOUSE_ACTIVATED) {
David Howells7d12e782006-10-05 14:55:46 +0100349 serio_interrupt(ptport, packet[1], 0);
350 serio_interrupt(ptport, packet[4], 0);
351 serio_interrupt(ptport, packet[5], 0);
Sergey Vlasov33fdfa92005-07-24 00:53:32 -0500352 if (child->pktsize == 4)
David Howells7d12e782006-10-05 14:55:46 +0100353 serio_interrupt(ptport, packet[2], 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 } else
David Howells7d12e782006-10-05 14:55:46 +0100355 serio_interrupt(ptport, packet[1], 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356}
357
358static void synaptics_pt_activate(struct psmouse *psmouse)
359{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 struct synaptics_data *priv = psmouse->private;
Dmitry Torokhova8b3c0f2010-10-04 21:46:10 -0700361 struct psmouse *child = serio_get_drvdata(priv->pt_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
363 /* adjust the touchpad to child's choice of protocol */
364 if (child) {
Sergey Vlasov33fdfa92005-07-24 00:53:32 -0500365 if (child->pktsize == 4)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT;
367 else
368 priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
369
370 if (synaptics_mode_cmd(psmouse, priv->mode))
371 printk(KERN_INFO "synaptics: failed to switch guest protocol\n");
372 }
373}
374
375static void synaptics_pt_create(struct psmouse *psmouse)
376{
377 struct serio *serio;
378
Eric Sesterhennb39787a2006-03-14 00:09:16 -0500379 serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 if (!serio) {
381 printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
382 return;
383 }
384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 serio->id.type = SERIO_PS_PSTHRU;
386 strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
387 strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
388 serio->write = synaptics_pt_write;
Dmitry Torokhova8b3c0f2010-10-04 21:46:10 -0700389 serio->start = synaptics_pt_start;
390 serio->stop = synaptics_pt_stop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 serio->parent = psmouse->ps2dev.serio;
392
393 psmouse->pt_activate = synaptics_pt_activate;
394
395 printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys);
396 serio_register_port(serio);
397}
398
399/*****************************************************************************
400 * Functions to interpret the absolute mode packets
401 ****************************************************************************/
402
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100403static int synaptics_parse_hw_state(const unsigned char buf[],
404 struct synaptics_data *priv,
405 struct synaptics_hw_state *hw)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406{
407 memset(hw, 0, sizeof(struct synaptics_hw_state));
408
409 if (SYN_MODEL_NEWABS(priv->model_id)) {
410 hw->x = (((buf[3] & 0x10) << 8) |
411 ((buf[1] & 0x0f) << 8) |
412 buf[4]);
413 hw->y = (((buf[3] & 0x20) << 7) |
414 ((buf[1] & 0xf0) << 4) |
415 buf[5]);
416
417 hw->z = buf[2];
418 hw->w = (((buf[0] & 0x30) >> 2) |
419 ((buf[0] & 0x04) >> 1) |
420 ((buf[3] & 0x04) >> 2));
421
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100422 if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
423 /* Gesture packet: (x, y, z) at half resolution */
424 priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
425 priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
426 priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
427 return 1;
428 }
429
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 hw->left = (buf[0] & 0x01) ? 1 : 0;
431 hw->right = (buf[0] & 0x02) ? 1 : 0;
432
Takashi Iwai5f57d672010-04-19 10:37:21 -0700433 if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
434 /*
435 * Clickpad's button is transmitted as middle button,
436 * however, since it is primary button, we will report
437 * it as BTN_LEFT.
438 */
439 hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
440
441 } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
443 if (hw->w == 2)
444 hw->scroll = (signed char)(buf[1]);
445 }
446
447 if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
448 hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
449 hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
450 }
451
452 if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
453 ((buf[0] ^ buf[3]) & 0x02)) {
454 switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
455 default:
456 /*
457 * if nExtBtn is greater than 8 it should be
458 * considered invalid and treated as 0
459 */
460 break;
461 case 8:
462 hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0;
463 hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0;
464 case 6:
465 hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0;
466 hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0;
467 case 4:
468 hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0;
469 hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0;
470 case 2:
471 hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0;
472 hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0;
473 }
474 }
475 } else {
476 hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
477 hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
478
479 hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
480 hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
481
482 hw->left = (buf[0] & 0x01) ? 1 : 0;
483 hw->right = (buf[0] & 0x02) ? 1 : 0;
484 }
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100485
486 return 0;
487}
488
489static void set_slot(struct input_dev *dev, int slot, bool active, int x, int y)
490{
491 input_mt_slot(dev, slot);
492 input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
493 if (active) {
494 input_report_abs(dev, ABS_MT_POSITION_X, x);
495 input_report_abs(dev, ABS_MT_POSITION_Y,
496 YMAX_NOMINAL + YMIN_NOMINAL - y);
497 }
498}
499
500static void synaptics_report_semi_mt_data(struct input_dev *dev,
501 const struct synaptics_hw_state *a,
502 const struct synaptics_hw_state *b,
503 int num_fingers)
504{
505 if (num_fingers >= 2) {
506 set_slot(dev, 0, true, min(a->x, b->x), min(a->y, b->y));
507 set_slot(dev, 1, true, max(a->x, b->x), max(a->y, b->y));
508 } else if (num_fingers == 1) {
509 set_slot(dev, 0, true, a->x, a->y);
510 set_slot(dev, 1, false, 0, 0);
511 } else {
512 set_slot(dev, 0, false, 0, 0);
513 set_slot(dev, 1, false, 0, 0);
514 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515}
516
517/*
518 * called for each full received packet from the touchpad
519 */
520static void synaptics_process_packet(struct psmouse *psmouse)
521{
Dmitry Torokhov2e5b6362005-09-15 02:01:44 -0500522 struct input_dev *dev = psmouse->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 struct synaptics_data *priv = psmouse->private;
524 struct synaptics_hw_state hw;
525 int num_fingers;
526 int finger_width;
527 int i;
528
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100529 if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
530 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
532 if (hw.scroll) {
533 priv->scroll += hw.scroll;
534
535 while (priv->scroll >= 4) {
536 input_report_key(dev, BTN_BACK, !hw.down);
537 input_sync(dev);
538 input_report_key(dev, BTN_BACK, hw.down);
539 input_sync(dev);
540 priv->scroll -= 4;
541 }
542 while (priv->scroll <= -4) {
543 input_report_key(dev, BTN_FORWARD, !hw.up);
544 input_sync(dev);
545 input_report_key(dev, BTN_FORWARD, hw.up);
546 input_sync(dev);
547 priv->scroll += 4;
548 }
549 return;
550 }
551
Henrik Rydberg4f56ce92010-12-18 15:42:30 +0100552 if (hw.z > 0 && hw.x > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 num_fingers = 1;
554 finger_width = 5;
555 if (SYN_CAP_EXTENDED(priv->capabilities)) {
556 switch (hw.w) {
557 case 0 ... 1:
558 if (SYN_CAP_MULTIFINGER(priv->capabilities))
559 num_fingers = hw.w + 2;
560 break;
561 case 2:
562 if (SYN_MODEL_PEN(priv->model_id))
563 ; /* Nothing, treat a pen as a single finger */
564 break;
565 case 4 ... 15:
566 if (SYN_CAP_PALMDETECT(priv->capabilities))
567 finger_width = hw.w;
568 break;
569 }
570 }
571 } else {
572 num_fingers = 0;
573 finger_width = 0;
574 }
575
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100576 if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
577 synaptics_report_semi_mt_data(dev, &hw, &priv->mt, num_fingers);
578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 /* Post events
580 * BTN_TOUCH has to be first as mousedev relies on it when doing
581 * absolute -> relative conversion
582 */
583 if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
584 if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
585
Henrik Rydberg4f56ce92010-12-18 15:42:30 +0100586 if (num_fingers > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 input_report_abs(dev, ABS_X, hw.x);
588 input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
589 }
590 input_report_abs(dev, ABS_PRESSURE, hw.z);
591
Chris Bagwell2a8e7712010-07-19 09:06:15 -0700592 if (SYN_CAP_PALMDETECT(priv->capabilities))
593 input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 input_report_key(dev, BTN_LEFT, hw.left);
597 input_report_key(dev, BTN_RIGHT, hw.right);
598
Peter Hutterere42b6642008-11-20 15:24:42 -0500599 if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
600 input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
601 input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
602 }
603
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
605 input_report_key(dev, BTN_MIDDLE, hw.middle);
606
607 if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
608 input_report_key(dev, BTN_FORWARD, hw.up);
609 input_report_key(dev, BTN_BACK, hw.down);
610 }
611
612 for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
613 input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
614
615 input_sync(dev);
616}
617
618static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
619{
Helge Dellere38de672006-09-10 21:54:39 -0400620 static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
621 static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
622 static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
623 static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
624 static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
626 if (idx < 0 || idx > 4)
627 return 0;
628
629 switch (pkt_type) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
Dmitry Torokhova62f0d22010-05-19 10:39:17 -0700631 case SYN_NEWABS:
632 case SYN_NEWABS_RELAXED:
633 return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
Dmitry Torokhova62f0d22010-05-19 10:39:17 -0700635 case SYN_NEWABS_STRICT:
636 return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Dmitry Torokhova62f0d22010-05-19 10:39:17 -0700638 case SYN_OLDABS:
639 return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
640
641 default:
642 printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type);
643 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 }
645}
646
647static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
648{
649 int i;
650
651 for (i = 0; i < 5; i++)
652 if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) {
653 printk(KERN_INFO "synaptics: using relaxed packet validation\n");
654 return SYN_NEWABS_RELAXED;
655 }
656
657 return SYN_NEWABS_STRICT;
658}
659
David Howells7d12e782006-10-05 14:55:46 +0100660static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 struct synaptics_data *priv = psmouse->private;
663
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 if (psmouse->pktcnt >= 6) { /* Full packet received */
665 if (unlikely(priv->pkt_type == SYN_NEWABS))
666 priv->pkt_type = synaptics_detect_pkt_type(psmouse);
667
Dmitry Torokhova8b3c0f2010-10-04 21:46:10 -0700668 if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
669 synaptics_is_pt_packet(psmouse->packet)) {
670 if (priv->pt_port)
671 synaptics_pass_pt_packet(priv->pt_port, psmouse->packet);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 } else
673 synaptics_process_packet(psmouse);
674
675 return PSMOUSE_FULL_PACKET;
676 }
677
678 return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ?
679 PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
680}
681
682/*****************************************************************************
683 * Driver initialization/cleanup functions
684 ****************************************************************************/
685static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
686{
687 int i;
688
Henrik Rydbergc14890a82010-12-16 09:52:23 +0100689 __set_bit(INPUT_PROP_POINTER, dev->propbit);
690
Dmitry Torokhovb7802c52009-09-09 19:13:20 -0700691 __set_bit(EV_ABS, dev->evbit);
Dmitry Torokhov83ba9ea2010-05-10 23:06:52 -0700692 input_set_abs_params(dev, ABS_X,
693 XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0);
694 input_set_abs_params(dev, ABS_Y,
695 YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
Chris Bagwell2a8e7712010-07-19 09:06:15 -0700697
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100698 if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
699 __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
700 input_mt_init_slots(dev, 2);
701 input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
702 priv->x_max ?: XMAX_NOMINAL, 0, 0);
703 input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL,
704 priv->y_max ?: YMAX_NOMINAL, 0, 0);
705 }
706
Chris Bagwell2a8e7712010-07-19 09:06:15 -0700707 if (SYN_CAP_PALMDETECT(priv->capabilities))
Chris Bagwell58fb0212010-07-19 09:06:15 -0700708 input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
Dmitry Torokhovb7802c52009-09-09 19:13:20 -0700710 __set_bit(EV_KEY, dev->evbit);
711 __set_bit(BTN_TOUCH, dev->keybit);
712 __set_bit(BTN_TOOL_FINGER, dev->keybit);
713 __set_bit(BTN_LEFT, dev->keybit);
714 __set_bit(BTN_RIGHT, dev->keybit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715
Peter Hutterere42b6642008-11-20 15:24:42 -0500716 if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
Dmitry Torokhovb7802c52009-09-09 19:13:20 -0700717 __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
718 __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
Peter Hutterere42b6642008-11-20 15:24:42 -0500719 }
720
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
Dmitry Torokhovb7802c52009-09-09 19:13:20 -0700722 __set_bit(BTN_MIDDLE, dev->keybit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
725 SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
Dmitry Torokhovb7802c52009-09-09 19:13:20 -0700726 __set_bit(BTN_FORWARD, dev->keybit);
727 __set_bit(BTN_BACK, dev->keybit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 }
729
730 for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
Dmitry Torokhovb7802c52009-09-09 19:13:20 -0700731 __set_bit(BTN_0 + i, dev->keybit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
Dmitry Torokhovb7802c52009-09-09 19:13:20 -0700733 __clear_bit(EV_REL, dev->evbit);
734 __clear_bit(REL_X, dev->relbit);
735 __clear_bit(REL_Y, dev->relbit);
Tero Saarniec20a022009-06-10 23:27:24 -0700736
Daniel Mack987a6c02010-08-02 20:15:17 -0700737 input_abs_set_res(dev, ABS_X, priv->x_res);
738 input_abs_set_res(dev, ABS_Y, priv->y_res);
Takashi Iwai5f57d672010-04-19 10:37:21 -0700739
740 if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
Henrik Rydbergc14890a82010-12-16 09:52:23 +0100741 __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
Takashi Iwai5f57d672010-04-19 10:37:21 -0700742 /* Clickpads report only left button */
743 __clear_bit(BTN_RIGHT, dev->keybit);
744 __clear_bit(BTN_MIDDLE, dev->keybit);
745 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746}
747
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748static void synaptics_disconnect(struct psmouse *psmouse)
749{
750 synaptics_reset(psmouse);
751 kfree(psmouse->private);
752 psmouse->private = NULL;
753}
754
755static int synaptics_reconnect(struct psmouse *psmouse)
756{
757 struct synaptics_data *priv = psmouse->private;
758 struct synaptics_data old_priv = *priv;
Alexandre Peixoto Ferreirac63fe0a2011-01-28 22:05:14 -0800759 int retry = 0;
760 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Alexandre Peixoto Ferreirac63fe0a2011-01-28 22:05:14 -0800762 do {
763 psmouse_reset(psmouse);
Dmitry Torokhov5cb6d282011-12-12 00:05:53 -0800764 if (retry) {
765 /*
766 * On some boxes, right after resuming, the touchpad
767 * needs some time to finish initializing (I assume
768 * it needs time to calibrate) and start responding
769 * to Synaptics-specific queries, so let's wait a
770 * bit.
771 */
772 ssleep(1);
773 }
Alexandre Peixoto Ferreirac63fe0a2011-01-28 22:05:14 -0800774 error = synaptics_detect(psmouse, 0);
775 } while (error && ++retry < 3);
Andy Whitcroft4d368452009-02-28 12:51:01 -0800776
Alexandre Peixoto Ferreirac63fe0a2011-01-28 22:05:14 -0800777 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 return -1;
779
Alexandre Peixoto Ferreirac63fe0a2011-01-28 22:05:14 -0800780 if (retry > 1)
781 printk(KERN_DEBUG "Synaptics reconnected after %d tries\n",
782 retry);
783
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 if (synaptics_query_hardware(psmouse)) {
785 printk(KERN_ERR "Unable to query Synaptics hardware.\n");
786 return -1;
787 }
788
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 if (synaptics_set_absolute_mode(psmouse)) {
790 printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
791 return -1;
792 }
793
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100794 if (synaptics_set_advanced_gesture_mode(psmouse)) {
795 printk(KERN_ERR "Advanced gesture mode reconnect failed.\n");
796 return -1;
797 }
798
Alexandre Peixoto Ferreirabaddf582011-01-28 22:05:14 -0800799 if (old_priv.identity != priv->identity ||
800 old_priv.model_id != priv->model_id ||
801 old_priv.capabilities != priv->capabilities ||
802 old_priv.ext_cap != priv->ext_cap) {
803 printk(KERN_ERR "Synaptics hardware appears to be different: "
804 "id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n",
805 old_priv.identity, priv->identity,
806 old_priv.model_id, priv->model_id,
807 old_priv.capabilities, priv->capabilities,
808 old_priv.ext_cap, priv->ext_cap);
809 return -1;
810 }
811
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 return 0;
813}
814
Dmitry Torokhov7705d542009-12-03 23:21:14 -0800815static bool impaired_toshiba_kbc;
816
817static const struct dmi_system_id __initconst toshiba_dmi_table[] = {
818#if defined(CONFIG_DMI) && defined(CONFIG_X86)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 {
Dmitry Torokhov9961e252009-12-04 10:24:20 -0800820 /* Toshiba Satellite */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 .matches = {
822 DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
Richard Thrippleton53a26702006-04-02 00:10:18 -0500823 DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 },
825 },
Simon Horman9ba5eaa2005-07-11 01:07:20 -0500826 {
Dmitry Torokhov9961e252009-12-04 10:24:20 -0800827 /* Toshiba Dynabook */
Simon Horman9ba5eaa2005-07-11 01:07:20 -0500828 .matches = {
829 DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
Richard Thrippleton53a26702006-04-02 00:10:18 -0500830 DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"),
831 },
832 },
833 {
Dmitry Torokhov9961e252009-12-04 10:24:20 -0800834 /* Toshiba Portege M300 */
Richard Thrippleton53a26702006-04-02 00:10:18 -0500835 .matches = {
836 DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
837 DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"),
Simon Horman9ba5eaa2005-07-11 01:07:20 -0500838 },
Dmitry Torokhov5f5eeff2009-10-12 21:35:00 -0700839
840 },
841 {
Dmitry Torokhov9961e252009-12-04 10:24:20 -0800842 /* Toshiba Portege M300 */
Dmitry Torokhov5f5eeff2009-10-12 21:35:00 -0700843 .matches = {
844 DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
845 DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"),
846 DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"),
847 },
848
Simon Horman9ba5eaa2005-07-11 01:07:20 -0500849 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850#endif
Jan Beulich70874862011-03-31 00:01:58 -0700851 { }
Dmitry Torokhov7705d542009-12-03 23:21:14 -0800852};
853
Andres Salomonef8313b2010-12-23 01:19:38 -0800854static bool broken_olpc_ec;
855
856static const struct dmi_system_id __initconst olpc_dmi_table[] = {
857#if defined(CONFIG_DMI) && defined(CONFIG_OLPC)
858 {
859 /* OLPC XO-1 or XO-1.5 */
860 .matches = {
861 DMI_MATCH(DMI_SYS_VENDOR, "OLPC"),
862 DMI_MATCH(DMI_PRODUCT_NAME, "XO"),
863 },
864 },
Andres Salomonef8313b2010-12-23 01:19:38 -0800865#endif
Jan Beulich70874862011-03-31 00:01:58 -0700866 { }
Andres Salomonef8313b2010-12-23 01:19:38 -0800867};
868
Dmitry Torokhov7705d542009-12-03 23:21:14 -0800869void __init synaptics_module_init(void)
870{
871 impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
Andres Salomonef8313b2010-12-23 01:19:38 -0800872 broken_olpc_ec = dmi_check_system(olpc_dmi_table);
Dmitry Torokhov7705d542009-12-03 23:21:14 -0800873}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
875int synaptics_init(struct psmouse *psmouse)
876{
877 struct synaptics_data *priv;
878
Andres Salomonef8313b2010-12-23 01:19:38 -0800879 /*
880 * The OLPC XO has issues with Synaptics' absolute mode; similarly to
881 * the HGPK, it quickly degrades and the hardware becomes jumpy and
882 * overly sensitive. Not only that, but the constant packet spew
883 * (even at a lowered 40pps rate) overloads the EC such that key
884 * presses on the keyboard are missed. Given all of that, don't
885 * even attempt to use Synaptics mode. Relative mode seems to work
886 * just fine.
887 */
888 if (broken_olpc_ec) {
889 printk(KERN_INFO "synaptics: OLPC XO detected, not enabling Synaptics protocol.\n");
890 return -ENODEV;
891 }
892
Eric Sesterhennb39787a2006-03-14 00:09:16 -0500893 psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 if (!priv)
Davidlohr Bueso6792cbb2010-09-29 18:53:35 -0700895 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
Andy Whitcroft4d368452009-02-28 12:51:01 -0800897 psmouse_reset(psmouse);
898
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 if (synaptics_query_hardware(psmouse)) {
900 printk(KERN_ERR "Unable to query Synaptics hardware.\n");
901 goto init_fail;
902 }
903
904 if (synaptics_set_absolute_mode(psmouse)) {
905 printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
906 goto init_fail;
907 }
908
Henrik Rydbergfec6e522010-12-21 18:11:25 +0100909 if (synaptics_set_advanced_gesture_mode(psmouse)) {
910 printk(KERN_ERR "Advanced gesture mode init failed.\n");
911 goto init_fail;
912 }
913
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
915
Takashi Iwai5f57d672010-04-19 10:37:21 -0700916 printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n",
Dmitry Torokhov409b7502005-05-28 02:12:18 -0500917 SYN_ID_MODEL(priv->identity),
918 SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
Takashi Iwai5f57d672010-04-19 10:37:21 -0700919 priv->model_id, priv->capabilities, priv->ext_cap, priv->ext_cap_0c);
Dmitry Torokhov409b7502005-05-28 02:12:18 -0500920
Dmitry Torokhov2e5b6362005-09-15 02:01:44 -0500921 set_input_params(psmouse->dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
Dmitry Torokhov887cc122007-04-12 01:30:41 -0400923 /*
924 * Encode touchpad model so that it can be used to set
925 * input device->id.version and be visible to userspace.
926 * Because version is __u16 we have to drop something.
927 * Hardware info bits seem to be good candidates as they
928 * are documented to be for Synaptics corp. internal use.
929 */
930 psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
931 (priv->model_id & 0x000000ff);
932
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 psmouse->protocol_handler = synaptics_process_byte;
934 psmouse->set_rate = synaptics_set_rate;
935 psmouse->disconnect = synaptics_disconnect;
936 psmouse->reconnect = synaptics_reconnect;
Dmitry Torokhova1cec062007-02-18 01:40:24 -0500937 psmouse->cleanup = synaptics_reset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 psmouse->pktsize = 6;
Dmitry Torokhovf0d5c6f2006-01-14 00:27:37 -0500939 /* Synaptics can usually stay in sync without extra help */
940 psmouse->resync_time = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
942 if (SYN_CAP_PASS_THROUGH(priv->capabilities))
943 synaptics_pt_create(psmouse);
944
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 /*
946 * Toshiba's KBC seems to have trouble handling data from
Andres Salomon7ee99162010-12-23 01:18:28 -0800947 * Synaptics at full rate. Switch to a lower rate (roughly
948 * the same rate as a standard PS/2 mouse).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 */
Dmitry Torokhov7705d542009-12-03 23:21:14 -0800950 if (psmouse->rate >= 80 && impaired_toshiba_kbc) {
Simon Horman9ba5eaa2005-07-11 01:07:20 -0500951 printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n",
952 dmi_get_system_info(DMI_PRODUCT_NAME));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 psmouse->rate = 40;
954 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
956 return 0;
957
958 init_fail:
959 kfree(priv);
960 return -1;
961}
962
Daniel Drakee4e6efd2010-01-07 01:52:39 -0800963bool synaptics_supported(void)
964{
965 return true;
966}
967
Andres Salomon55e3d922007-03-10 01:39:54 -0500968#else /* CONFIG_MOUSE_PS2_SYNAPTICS */
969
Dmitry Torokhov7705d542009-12-03 23:21:14 -0800970void __init synaptics_module_init(void)
971{
972}
973
Andres Salomon55e3d922007-03-10 01:39:54 -0500974int synaptics_init(struct psmouse *psmouse)
975{
976 return -ENOSYS;
977}
978
Daniel Drakee4e6efd2010-01-07 01:52:39 -0800979bool synaptics_supported(void)
980{
981 return false;
982}
983
Andres Salomon55e3d922007-03-10 01:39:54 -0500984#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985