Blame view

drivers/input/mouse/elantech.c 45.1 KB
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1
  /*
3f8c0df43   Arjan Opmeer   Input: elantech -...
2
   * Elantech Touchpad driver (v6)
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
3
   *
3f8c0df43   Arjan Opmeer   Input: elantech -...
4
   * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
5
6
7
8
9
10
11
12
13
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * Trademarks are the property of their respective owners.
   */
  
  #include <linux/delay.h>
36189cc3c   Hans de Goede   Input: elantech -...
14
  #include <linux/dmi.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
15
  #include <linux/slab.h>
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
16
17
  #include <linux/module.h>
  #include <linux/input.h>
89eec4d71   Éric Piel   Input: elantech -...
18
  #include <linux/input/mt.h>
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
19
20
  #include <linux/serio.h>
  #include <linux/libps2.h>
a2418fc4a   Ulrik De Bie   Input: elantech -...
21
  #include <asm/unaligned.h>
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
22
23
  #include "psmouse.h"
  #include "elantech.h"
d4ae84a84   Dmitry Torokhov   Input: elantech -...
24
25
26
  #define elantech_debug(fmt, ...)					\
  	do {								\
  		if (etd->debug)						\
b5d217043   Dmitry Torokhov   Input: psmouse - ...
27
28
  			psmouse_printk(KERN_DEBUG, psmouse,		\
  					fmt, ##__VA_ARGS__);		\
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
29
30
31
32
33
34
35
36
37
38
  	} while (0)
  
  /*
   * Send a Synaptics style sliced query command
   */
  static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
  				unsigned char *param)
  {
  	if (psmouse_sliced_command(psmouse, c) ||
  	    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
39
40
  		psmouse_err(psmouse, "%s query 0x%02x failed.
  ", __func__, c);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
41
42
43
44
45
46
47
  		return -1;
  	}
  
  	return 0;
  }
  
  /*
b56b92a9a   JJ Ding   Input: elantech -...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
   * V3 and later support this fast command
   */
  static int elantech_send_cmd(struct psmouse *psmouse, unsigned char c,
  				unsigned char *param)
  {
  	struct ps2dev *ps2dev = &psmouse->ps2dev;
  
  	if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  	    ps2_command(ps2dev, NULL, c) ||
  	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
  		psmouse_err(psmouse, "%s query 0x%02x failed.
  ", __func__, c);
  		return -1;
  	}
  
  	return 0;
  }
  
  /*
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
   * A retrying version of ps2_command
   */
  static int elantech_ps2_command(struct psmouse *psmouse,
  				unsigned char *param, int command)
  {
  	struct ps2dev *ps2dev = &psmouse->ps2dev;
  	struct elantech_data *etd = psmouse->private;
  	int rc;
  	int tries = ETP_PS2_COMMAND_TRIES;
  
  	do {
  		rc = ps2_command(ps2dev, param, command);
  		if (rc == 0)
  			break;
  		tries--;
d4ae84a84   Dmitry Torokhov   Input: elantech -...
82
83
84
  		elantech_debug("retrying ps2 command 0x%02x (%d).
  ",
  				command, tries);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
85
86
87
88
  		msleep(ETP_PS2_COMMAND_DELAY);
  	} while (tries > 0);
  
  	if (rc)
b5d217043   Dmitry Torokhov   Input: psmouse - ...
89
90
  		psmouse_err(psmouse, "ps2 command 0x%02x failed.
  ", command);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
91
92
93
94
95
96
97
98
99
100
101
102
103
  
  	return rc;
  }
  
  /*
   * Send an Elantech style special command to read a value from a register
   */
  static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
  				unsigned char *val)
  {
  	struct elantech_data *etd = psmouse->private;
  	unsigned char param[3];
  	int rc = 0;
1dc6edec1   JJ Ding   Input: elantech -...
104
  	if (reg < 0x07 || reg > 0x26)
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  		return -1;
  
  	if (reg > 0x11 && reg < 0x20)
  		return -1;
  
  	switch (etd->hw_version) {
  	case 1:
  		if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||
  		    psmouse_sliced_command(psmouse, reg) ||
  		    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
  			rc = -1;
  		}
  		break;
  
  	case 2:
  		if (elantech_ps2_command(psmouse,  NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse,  NULL, ETP_REGISTER_READ) ||
  		    elantech_ps2_command(psmouse,  NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse,  NULL, reg) ||
  		    elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
  			rc = -1;
  		}
  		break;
28f496161   JJ Ding   Input: elantech -...
128

1dc6edec1   JJ Ding   Input: elantech -...
129
  	case 3 ... 4:
28f496161   JJ Ding   Input: elantech -...
130
131
132
133
134
135
136
137
  		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, reg) ||
  		    elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
  			rc = -1;
  		}
  		break;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
138
139
140
  	}
  
  	if (rc)
b5d217043   Dmitry Torokhov   Input: psmouse - ...
141
142
  		psmouse_err(psmouse, "failed to read register 0x%02x.
  ", reg);
1dc6edec1   JJ Ding   Input: elantech -...
143
  	else if (etd->hw_version != 4)
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
144
  		*val = param[0];
1dc6edec1   JJ Ding   Input: elantech -...
145
146
  	else
  		*val = param[1];
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
147
148
149
150
151
152
153
154
155
156
157
158
  
  	return rc;
  }
  
  /*
   * Send an Elantech style special command to write a register with a value
   */
  static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
  				unsigned char val)
  {
  	struct elantech_data *etd = psmouse->private;
  	int rc = 0;
1dc6edec1   JJ Ding   Input: elantech -...
159
  	if (reg < 0x07 || reg > 0x26)
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  		return -1;
  
  	if (reg > 0x11 && reg < 0x20)
  		return -1;
  
  	switch (etd->hw_version) {
  	case 1:
  		if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
  		    psmouse_sliced_command(psmouse, reg) ||
  		    psmouse_sliced_command(psmouse, val) ||
  		    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
  			rc = -1;
  		}
  		break;
  
  	case 2:
  		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, reg) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, val) ||
  		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
  			rc = -1;
  		}
  		break;
28f496161   JJ Ding   Input: elantech -...
186
187
188
189
190
191
192
193
194
195
196
197
  
  	case 3:
  		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, reg) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, val) ||
  		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
  			rc = -1;
  		}
  		break;
1dc6edec1   JJ Ding   Input: elantech -...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  
  	case 4:
  		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, reg) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
  		    elantech_ps2_command(psmouse, NULL, val) ||
  		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
  			rc = -1;
  		}
  		break;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
212
213
214
  	}
  
  	if (rc)
b5d217043   Dmitry Torokhov   Input: psmouse - ...
215
216
217
218
  		psmouse_err(psmouse,
  			    "failed to write register 0x%02x with value 0x%02x.
  ",
  			    reg, val);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
219
220
221
222
223
224
225
  
  	return rc;
  }
  
  /*
   * Dump a complete mouse movement packet to the syslog
   */
b5d217043   Dmitry Torokhov   Input: psmouse - ...
226
  static void elantech_packet_dump(struct psmouse *psmouse)
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
227
228
  {
  	int	i;
b5d217043   Dmitry Torokhov   Input: psmouse - ...
229
230
231
  	psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet [");
  	for (i = 0; i < psmouse->pktsize; i++)
  		printk("%s0x%02x ", i ? ", " : " ", psmouse->packet[i]);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
  	printk("]
  ");
  }
  
  /*
   * Interpret complete data packets and report absolute mode input events for
   * hardware version 1. (4 byte packets)
   */
  static void elantech_report_absolute_v1(struct psmouse *psmouse)
  {
  	struct input_dev *dev = psmouse->dev;
  	struct elantech_data *etd = psmouse->private;
  	unsigned char *packet = psmouse->packet;
  	int fingers;
504e8beed   Dmitry Torokhov   Input: elantech -...
246
  	if (etd->fw_version < 0x020000) {
e938fbfd4   Florian Ragwitz   Input: elantech -...
247
248
249
250
  		/*
  		 * byte 0:  D   U  p1  p2   1  p3   R   L
  		 * byte 1:  f   0  th  tw  x9  x8  y9  y8
  		 */
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
251
252
253
  		fingers = ((packet[1] & 0x80) >> 7) +
  				((packet[1] & 0x30) >> 4);
  	} else {
e938fbfd4   Florian Ragwitz   Input: elantech -...
254
255
256
257
  		/*
  		 * byte 0: n1  n0  p2  p1   1  p3   R   L
  		 * byte 1:  0   0   0   0  x9  x8  y9  y8
  		 */
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
258
259
  		fingers = (packet[0] & 0xc0) >> 6;
  	}
3f8c0df43   Arjan Opmeer   Input: elantech -...
260
  	if (etd->jumpy_cursor) {
7f29f17b5   Éric Piel   Input: elantech -...
261
262
263
264
265
  		if (fingers != 1) {
  			etd->single_finger_reports = 0;
  		} else if (etd->single_finger_reports < 2) {
  			/* Discard first 2 reports of one finger, bogus */
  			etd->single_finger_reports++;
d4ae84a84   Dmitry Torokhov   Input: elantech -...
266
267
  			elantech_debug("discarding packet
  ");
7f29f17b5   Éric Piel   Input: elantech -...
268
  			return;
3f8c0df43   Arjan Opmeer   Input: elantech -...
269
270
  		}
  	}
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
271
  	input_report_key(dev, BTN_TOUCH, fingers != 0);
e938fbfd4   Florian Ragwitz   Input: elantech -...
272
273
274
275
  	/*
  	 * byte 2: x7  x6  x5  x4  x3  x2  x1  x0
  	 * byte 3: y7  y6  y5  y4  y3  y2  y1  y0
  	 */
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
276
277
278
  	if (fingers) {
  		input_report_abs(dev, ABS_X,
  			((packet[1] & 0x0c) << 6) | packet[2]);
e938fbfd4   Florian Ragwitz   Input: elantech -...
279
  		input_report_abs(dev, ABS_Y,
230282a77   JJ Ding   Input: elantech -...
280
  			etd->y_max - (((packet[1] & 0x03) << 8) | packet[3]));
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
281
282
283
284
285
286
287
  	}
  
  	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
  	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
  	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
  	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
  	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
504e8beed   Dmitry Torokhov   Input: elantech -...
288
  	if (etd->fw_version < 0x020000 &&
230282a77   JJ Ding   Input: elantech -...
289
  	    (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
290
291
292
293
294
295
296
297
  		/* rocker up */
  		input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
  		/* rocker down */
  		input_report_key(dev, BTN_BACK, packet[0] & 0x80);
  	}
  
  	input_sync(dev);
  }
89eec4d71   Éric Piel   Input: elantech -...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  static void elantech_set_slot(struct input_dev *dev, int slot, bool active,
  			      unsigned int x, unsigned int y)
  {
  	input_mt_slot(dev, slot);
  	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
  	if (active) {
  		input_report_abs(dev, ABS_MT_POSITION_X, x);
  		input_report_abs(dev, ABS_MT_POSITION_Y, y);
  	}
  }
  
  /* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */
  static void elantech_report_semi_mt_data(struct input_dev *dev,
  					 unsigned int num_fingers,
  					 unsigned int x1, unsigned int y1,
  					 unsigned int x2, unsigned int y2)
  {
  	elantech_set_slot(dev, 0, num_fingers != 0, x1, y1);
  	elantech_set_slot(dev, 1, num_fingers == 2, x2, y2);
  }
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
318
319
320
321
322
323
  /*
   * Interpret complete data packets and report absolute mode input events for
   * hardware version 2. (6 byte packets)
   */
  static void elantech_report_absolute_v2(struct psmouse *psmouse)
  {
f941c705f   Éric Piel   Input: elantech -...
324
  	struct elantech_data *etd = psmouse->private;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
325
326
  	struct input_dev *dev = psmouse->dev;
  	unsigned char *packet = psmouse->packet;
461a79176   JJ Ding   Input: elantech -...
327
328
  	unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
  	unsigned int width = 0, pres = 0;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
329
330
331
  
  	/* byte 0: n1  n0   .   .   .   .   R   L */
  	fingers = (packet[0] & 0xc0) >> 6;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
332
333
  
  	switch (fingers) {
22462d9fc   Éric Piel   Input: elantech -...
334
335
336
337
338
339
340
341
  	case 3:
  		/*
  		 * Same as one finger, except report of more than 3 fingers:
  		 * byte 3:  n4  .   w1  w0   .   .   .   .
  		 */
  		if (packet[3] & 0x80)
  			fingers = 4;
  		/* pass through... */
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
342
  	case 1:
e938fbfd4   Florian Ragwitz   Input: elantech -...
343
  		/*
115596194   JJ Ding   Input: elantech -...
344
  		 * byte 1:  .   .   .   .  x11 x10 x9  x8
e938fbfd4   Florian Ragwitz   Input: elantech -...
345
346
  		 * byte 2: x7  x6  x5  x4  x4  x2  x1  x0
  		 */
115596194   JJ Ding   Input: elantech -...
347
  		x1 = ((packet[1] & 0x0f) << 8) | packet[2];
e938fbfd4   Florian Ragwitz   Input: elantech -...
348
  		/*
115596194   JJ Ding   Input: elantech -...
349
  		 * byte 4:  .   .   .   .  y11 y10 y9  y8
e938fbfd4   Florian Ragwitz   Input: elantech -...
350
351
  		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
  		 */
230282a77   JJ Ding   Input: elantech -...
352
  		y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
89eec4d71   Éric Piel   Input: elantech -...
353

f941c705f   Éric Piel   Input: elantech -...
354
355
  		pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
  		width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
356
357
358
  		break;
  
  	case 2:
e938fbfd4   Florian Ragwitz   Input: elantech -...
359
360
361
362
363
364
  		/*
  		 * The coordinate of each finger is reported separately
  		 * with a lower resolution for two finger touches:
  		 * byte 0:  .   .  ay8 ax8  .   .   .   .
  		 * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0
  		 */
461a79176   JJ Ding   Input: elantech -...
365
  		x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
366
  		/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
230282a77   JJ Ding   Input: elantech -...
367
  		y1 = etd->y_max -
461a79176   JJ Ding   Input: elantech -...
368
  			((((packet[0] & 0x20) << 3) | packet[2]) << 2);
e938fbfd4   Florian Ragwitz   Input: elantech -...
369
370
371
372
  		/*
  		 * byte 3:  .   .  by8 bx8  .   .   .   .
  		 * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
  		 */
461a79176   JJ Ding   Input: elantech -...
373
  		x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
374
  		/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
230282a77   JJ Ding   Input: elantech -...
375
  		y2 = etd->y_max -
461a79176   JJ Ding   Input: elantech -...
376
  			((((packet[3] & 0x20) << 3) | packet[5]) << 2);
f941c705f   Éric Piel   Input: elantech -...
377
378
379
380
  
  		/* Unknown so just report sensible values */
  		pres = 127;
  		width = 7;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
381
382
  		break;
  	}
461a79176   JJ Ding   Input: elantech -...
383
384
385
386
387
  	input_report_key(dev, BTN_TOUCH, fingers != 0);
  	if (fingers != 0) {
  		input_report_abs(dev, ABS_X, x1);
  		input_report_abs(dev, ABS_Y, y1);
  	}
89eec4d71   Éric Piel   Input: elantech -...
388
  	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
389
390
391
  	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
  	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
  	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
22462d9fc   Éric Piel   Input: elantech -...
392
  	input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
393
394
  	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
  	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
f941c705f   Éric Piel   Input: elantech -...
395
396
397
398
  	if (etd->reports_pressure) {
  		input_report_abs(dev, ABS_PRESSURE, pres);
  		input_report_abs(dev, ABS_TOOL_WIDTH, width);
  	}
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
399
400
401
  
  	input_sync(dev);
  }
a2418fc4a   Ulrik De Bie   Input: elantech -...
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  static void elantech_report_trackpoint(struct psmouse *psmouse,
  				       int packet_type)
  {
  	/*
  	 * byte 0:  0   0  sx  sy   0   M   R   L
  	 * byte 1:~sx   0   0   0   0   0   0   0
  	 * byte 2:~sy   0   0   0   0   0   0   0
  	 * byte 3:  0   0 ~sy ~sx   0   1   1   0
  	 * byte 4: x7  x6  x5  x4  x3  x2  x1  x0
  	 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
  	 *
  	 * x and y are written in two's complement spread
  	 * over 9 bits with sx/sy the relative top bit and
  	 * x7..x0 and y7..y0 the lower bits.
  	 * The sign of y is opposite to what the input driver
  	 * expects for a relative movement
  	 */
  
  	struct elantech_data *etd = psmouse->private;
  	struct input_dev *tp_dev = etd->tp_dev;
  	unsigned char *packet = psmouse->packet;
  	int x, y;
  	u32 t;
a2418fc4a   Ulrik De Bie   Input: elantech -...
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  	t = get_unaligned_le32(&packet[0]);
  
  	switch (t & ~7U) {
  	case 0x06000030U:
  	case 0x16008020U:
  	case 0x26800010U:
  	case 0x36808000U:
  		x = packet[4] - (int)((packet[1]^0x80) << 1);
  		y = (int)((packet[2]^0x80) << 1) - packet[5];
  
  		input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01);
  		input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02);
  		input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04);
  
  		input_report_rel(tp_dev, REL_X, x);
  		input_report_rel(tp_dev, REL_Y, y);
  
  		input_sync(tp_dev);
  
  		break;
  
  	default:
  		/* Dump unexpected packet sequences if debug=1 (default) */
  		if (etd->debug == 1)
  			elantech_packet_dump(psmouse);
  
  		break;
  	}
  }
28f496161   JJ Ding   Input: elantech -...
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
  /*
   * Interpret complete data packets and report absolute mode input events for
   * hardware version 3. (12 byte packets for two fingers)
   */
  static void elantech_report_absolute_v3(struct psmouse *psmouse,
  					int packet_type)
  {
  	struct input_dev *dev = psmouse->dev;
  	struct elantech_data *etd = psmouse->private;
  	unsigned char *packet = psmouse->packet;
  	unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
  	unsigned int width = 0, pres = 0;
  
  	/* byte 0: n1  n0   .   .   .   .   R   L */
  	fingers = (packet[0] & 0xc0) >> 6;
  
  	switch (fingers) {
  	case 3:
  	case 1:
  		/*
  		 * byte 1:  .   .   .   .  x11 x10 x9  x8
  		 * byte 2: x7  x6  x5  x4  x4  x2  x1  x0
  		 */
  		x1 = ((packet[1] & 0x0f) << 8) | packet[2];
  		/*
  		 * byte 4:  .   .   .   .  y11 y10 y9  y8
  		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
  		 */
  		y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
  		break;
  
  	case 2:
  		if (packet_type == PACKET_V3_HEAD) {
  			/*
  			 * byte 1:   .    .    .    .  ax11 ax10 ax9  ax8
  			 * byte 2: ax7  ax6  ax5  ax4  ax3  ax2  ax1  ax0
  			 */
1dc6edec1   JJ Ding   Input: elantech -...
491
  			etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2];
28f496161   JJ Ding   Input: elantech -...
492
493
494
495
  			/*
  			 * byte 4:   .    .    .    .  ay11 ay10 ay9  ay8
  			 * byte 5: ay7  ay6  ay5  ay4  ay3  ay2  ay1  ay0
  			 */
1dc6edec1   JJ Ding   Input: elantech -...
496
  			etd->mt[0].y = etd->y_max -
28f496161   JJ Ding   Input: elantech -...
497
498
499
500
501
502
503
504
  				(((packet[4] & 0x0f) << 8) | packet[5]);
  			/*
  			 * wait for next packet
  			 */
  			return;
  		}
  
  		/* packet_type == PACKET_V3_TAIL */
1dc6edec1   JJ Ding   Input: elantech -...
505
506
  		x1 = etd->mt[0].x;
  		y1 = etd->mt[0].y;
28f496161   JJ Ding   Input: elantech -...
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  		x2 = ((packet[1] & 0x0f) << 8) | packet[2];
  		y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
  		break;
  	}
  
  	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
  	width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
  
  	input_report_key(dev, BTN_TOUCH, fingers != 0);
  	if (fingers != 0) {
  		input_report_abs(dev, ABS_X, x1);
  		input_report_abs(dev, ABS_Y, y1);
  	}
  	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
  	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
  	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
  	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
cd9e83e27   Hans de Goede   Input: elantech -...
524
525
526
527
528
529
530
531
  
  	/* For clickpads map both buttons to BTN_LEFT */
  	if (etd->fw_version & 0x001000) {
  		input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
  	} else {
  		input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
  		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
  	}
28f496161   JJ Ding   Input: elantech -...
532
533
534
535
536
  	input_report_abs(dev, ABS_PRESSURE, pres);
  	input_report_abs(dev, ABS_TOOL_WIDTH, width);
  
  	input_sync(dev);
  }
1dc6edec1   JJ Ding   Input: elantech -...
537
538
539
  static void elantech_input_sync_v4(struct psmouse *psmouse)
  {
  	struct input_dev *dev = psmouse->dev;
cd9e83e27   Hans de Goede   Input: elantech -...
540
  	struct elantech_data *etd = psmouse->private;
1dc6edec1   JJ Ding   Input: elantech -...
541
  	unsigned char *packet = psmouse->packet;
cd9e83e27   Hans de Goede   Input: elantech -...
542
543
544
545
546
547
  	/* For clickpads map both buttons to BTN_LEFT */
  	if (etd->fw_version & 0x001000) {
  		input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
  	} else {
  		input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
  		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
f386474e1   Ulrik De Bie   Input: elantech -...
548
  		input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04);
cd9e83e27   Hans de Goede   Input: elantech -...
549
  	}
1dc6edec1   JJ Ding   Input: elantech -...
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
  	input_mt_report_pointer_emulation(dev, true);
  	input_sync(dev);
  }
  
  static void process_packet_status_v4(struct psmouse *psmouse)
  {
  	struct input_dev *dev = psmouse->dev;
  	unsigned char *packet = psmouse->packet;
  	unsigned fingers;
  	int i;
  
  	/* notify finger state change */
  	fingers = packet[1] & 0x1f;
  	for (i = 0; i < ETP_MAX_FINGERS; i++) {
  		if ((fingers & (1 << i)) == 0) {
  			input_mt_slot(dev, i);
  			input_mt_report_slot_state(dev, MT_TOOL_FINGER, false);
  		}
  	}
  
  	elantech_input_sync_v4(psmouse);
  }
  
  static void process_packet_head_v4(struct psmouse *psmouse)
  {
  	struct input_dev *dev = psmouse->dev;
  	struct elantech_data *etd = psmouse->private;
  	unsigned char *packet = psmouse->packet;
  	int id = ((packet[3] & 0xe0) >> 5) - 1;
  	int pres, traces;
  
  	if (id < 0)
  		return;
  
  	etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2];
  	etd->mt[id].y = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
  	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
  	traces = (packet[0] & 0xf0) >> 4;
  
  	input_mt_slot(dev, id);
  	input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
  
  	input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x);
  	input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y);
  	input_report_abs(dev, ABS_MT_PRESSURE, pres);
  	input_report_abs(dev, ABS_MT_TOUCH_MAJOR, traces * etd->width);
  	/* report this for backwards compatibility */
  	input_report_abs(dev, ABS_TOOL_WIDTH, traces);
  
  	elantech_input_sync_v4(psmouse);
  }
  
  static void process_packet_motion_v4(struct psmouse *psmouse)
  {
  	struct input_dev *dev = psmouse->dev;
  	struct elantech_data *etd = psmouse->private;
  	unsigned char *packet = psmouse->packet;
  	int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0;
  	int id, sid;
  
  	id = ((packet[0] & 0xe0) >> 5) - 1;
  	if (id < 0)
  		return;
  
  	sid = ((packet[3] & 0xe0) >> 5) - 1;
  	weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1;
  	/*
  	 * Motion packets give us the delta of x, y values of specific fingers,
  	 * but in two's complement. Let the compiler do the conversion for us.
  	 * Also _enlarge_ the numbers to int, in case of overflow.
  	 */
  	delta_x1 = (signed char)packet[1];
  	delta_y1 = (signed char)packet[2];
  	delta_x2 = (signed char)packet[4];
  	delta_y2 = (signed char)packet[5];
  
  	etd->mt[id].x += delta_x1 * weight;
  	etd->mt[id].y -= delta_y1 * weight;
  	input_mt_slot(dev, id);
  	input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x);
  	input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y);
  
  	if (sid >= 0) {
  		etd->mt[sid].x += delta_x2 * weight;
  		etd->mt[sid].y -= delta_y2 * weight;
  		input_mt_slot(dev, sid);
  		input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[sid].x);
  		input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[sid].y);
  	}
  
  	elantech_input_sync_v4(psmouse);
  }
  
  static void elantech_report_absolute_v4(struct psmouse *psmouse,
  					int packet_type)
  {
  	switch (packet_type) {
  	case PACKET_V4_STATUS:
  		process_packet_status_v4(psmouse);
  		break;
  
  	case PACKET_V4_HEAD:
  		process_packet_head_v4(psmouse);
  		break;
  
  	case PACKET_V4_MOTION:
  		process_packet_motion_v4(psmouse);
  		break;
  
  	case PACKET_UNKNOWN:
  	default:
  		/* impossible to get here */
  		break;
  	}
  }
7894f21b1   JJ Ding   Input: elantech -...
665
  static int elantech_packet_check_v1(struct psmouse *psmouse)
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
666
667
668
669
670
671
  {
  	struct elantech_data *etd = psmouse->private;
  	unsigned char *packet = psmouse->packet;
  	unsigned char p1, p2, p3;
  
  	/* Parity bits are placed differently */
504e8beed   Dmitry Torokhov   Input: elantech -...
672
  	if (etd->fw_version < 0x020000) {
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
  		/* byte 0:  D   U  p1  p2   1  p3   R   L */
  		p1 = (packet[0] & 0x20) >> 5;
  		p2 = (packet[0] & 0x10) >> 4;
  	} else {
  		/* byte 0: n1  n0  p2  p1   1  p3   R   L */
  		p1 = (packet[0] & 0x10) >> 4;
  		p2 = (packet[0] & 0x20) >> 5;
  	}
  
  	p3 = (packet[0] & 0x04) >> 2;
  
  	return etd->parity[packet[1]] == p1 &&
  	       etd->parity[packet[2]] == p2 &&
  	       etd->parity[packet[3]] == p3;
  }
84a90b610   JJ Ding   Input: elantech -...
688
689
690
691
692
693
694
695
696
697
698
  static int elantech_debounce_check_v2(struct psmouse *psmouse)
  {
          /*
           * When we encounter packet that matches this exactly, it means the
           * hardware is in debounce status. Just ignore the whole packet.
           */
          const u8 debounce_packet[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff };
          unsigned char *packet = psmouse->packet;
  
          return !memcmp(packet, debounce_packet, sizeof(debounce_packet));
  }
7894f21b1   JJ Ding   Input: elantech -...
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
  static int elantech_packet_check_v2(struct psmouse *psmouse)
  {
  	struct elantech_data *etd = psmouse->private;
  	unsigned char *packet = psmouse->packet;
  
  	/*
  	 * V2 hardware has two flavors. Older ones that do not report pressure,
  	 * and newer ones that reports pressure and width. With newer ones, all
  	 * packets (1, 2, 3 finger touch) have the same constant bits. With
  	 * older ones, 1/3 finger touch packets and 2 finger touch packets
  	 * have different constant bits.
  	 * With all three cases, if the constant bits are not exactly what I
  	 * expected, I consider them invalid.
  	 */
  	if (etd->reports_pressure)
  		return (packet[0] & 0x0c) == 0x04 &&
  		       (packet[3] & 0x0f) == 0x02;
  
  	if ((packet[0] & 0xc0) == 0x80)
  		return (packet[0] & 0x0c) == 0x0c &&
  		       (packet[3] & 0x0e) == 0x08;
  
  	return (packet[0] & 0x3c) == 0x3c &&
  	       (packet[1] & 0xf0) == 0x00 &&
  	       (packet[3] & 0x3e) == 0x38 &&
  	       (packet[4] & 0xf0) == 0x00;
  }
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
726
  /*
28f496161   JJ Ding   Input: elantech -...
727
   * We check the constant bits to determine what packet type we get,
1dc6edec1   JJ Ding   Input: elantech -...
728
   * so packet checking is mandatory for v3 and later hardware.
28f496161   JJ Ding   Input: elantech -...
729
730
731
   */
  static int elantech_packet_check_v3(struct psmouse *psmouse)
  {
acc0444bf   Matteo Delfino   Input: elantech -...
732
  	struct elantech_data *etd = psmouse->private;
28f496161   JJ Ding   Input: elantech -...
733
734
735
736
737
738
739
740
741
  	const u8 debounce_packet[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
  	unsigned char *packet = psmouse->packet;
  
  	/*
  	 * check debounce first, it has the same signature in byte 0
  	 * and byte 3 as PACKET_V3_HEAD.
  	 */
  	if (!memcmp(packet, debounce_packet, sizeof(debounce_packet)))
  		return PACKET_DEBOUNCE;
acc0444bf   Matteo Delfino   Input: elantech -...
742
743
744
745
746
747
748
749
750
751
752
753
754
  	/*
  	 * If the hardware flag 'crc_enabled' is set the packets have
  	 * different signatures.
  	 */
  	if (etd->crc_enabled) {
  		if ((packet[3] & 0x09) == 0x08)
  			return PACKET_V3_HEAD;
  
  		if ((packet[3] & 0x09) == 0x09)
  			return PACKET_V3_TAIL;
  	} else {
  		if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02)
  			return PACKET_V3_HEAD;
28f496161   JJ Ding   Input: elantech -...
755

acc0444bf   Matteo Delfino   Input: elantech -...
756
757
  		if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c)
  			return PACKET_V3_TAIL;
a2418fc4a   Ulrik De Bie   Input: elantech -...
758
759
  		if ((packet[3] & 0x0f) == 0x06)
  			return PACKET_TRACKPOINT;
acc0444bf   Matteo Delfino   Input: elantech -...
760
  	}
28f496161   JJ Ding   Input: elantech -...
761
762
763
  
  	return PACKET_UNKNOWN;
  }
1dc6edec1   JJ Ding   Input: elantech -...
764
765
  static int elantech_packet_check_v4(struct psmouse *psmouse)
  {
acc0444bf   Matteo Delfino   Input: elantech -...
766
  	struct elantech_data *etd = psmouse->private;
1dc6edec1   JJ Ding   Input: elantech -...
767
  	unsigned char *packet = psmouse->packet;
9eebed7de   Matteo Delfino   Input: elantech -...
768
  	unsigned char packet_type = packet[3] & 0x03;
acc0444bf   Matteo Delfino   Input: elantech -...
769
  	bool sanity_check;
d0ab54783   Dmitry Torokhov   Input: elantech -...
770
  	if (etd->tp_dev && (packet[3] & 0x0f) == 0x06)
caeb0d37f   Ulrik De Bie   Input: elantech -...
771
  		return PACKET_TRACKPOINT;
acc0444bf   Matteo Delfino   Input: elantech -...
772
773
774
775
776
777
778
779
780
781
782
783
784
785
  	/*
  	 * Sanity check based on the constant bits of a packet.
  	 * The constant bits change depending on the value of
  	 * the hardware flag 'crc_enabled' but are the same for
  	 * every packet, regardless of the type.
  	 */
  	if (etd->crc_enabled)
  		sanity_check = ((packet[3] & 0x08) == 0x00);
  	else
  		sanity_check = ((packet[0] & 0x0c) == 0x04 &&
  				(packet[3] & 0x1c) == 0x10);
  
  	if (!sanity_check)
  		return PACKET_UNKNOWN;
1dc6edec1   JJ Ding   Input: elantech -...
786

9eebed7de   Matteo Delfino   Input: elantech -...
787
788
789
790
791
  	switch (packet_type) {
  	case 0:
  		return PACKET_V4_STATUS;
  
  	case 1:
1dc6edec1   JJ Ding   Input: elantech -...
792
  		return PACKET_V4_HEAD;
9eebed7de   Matteo Delfino   Input: elantech -...
793
  	case 2:
1dc6edec1   JJ Ding   Input: elantech -...
794
  		return PACKET_V4_MOTION;
9eebed7de   Matteo Delfino   Input: elantech -...
795
  	}
1dc6edec1   JJ Ding   Input: elantech -...
796
797
798
  
  	return PACKET_UNKNOWN;
  }
28f496161   JJ Ding   Input: elantech -...
799
  /*
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
800
801
802
803
804
   * Process byte stream from mouse and handle complete packets
   */
  static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
  {
  	struct elantech_data *etd = psmouse->private;
28f496161   JJ Ding   Input: elantech -...
805
  	int packet_type;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
806
807
808
809
810
  
  	if (psmouse->pktcnt < psmouse->pktsize)
  		return PSMOUSE_GOOD_DATA;
  
  	if (etd->debug > 1)
b5d217043   Dmitry Torokhov   Input: psmouse - ...
811
  		elantech_packet_dump(psmouse);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
812
813
814
  
  	switch (etd->hw_version) {
  	case 1:
7894f21b1   JJ Ding   Input: elantech -...
815
  		if (etd->paritycheck && !elantech_packet_check_v1(psmouse))
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
816
817
818
819
820
821
  			return PSMOUSE_BAD_DATA;
  
  		elantech_report_absolute_v1(psmouse);
  		break;
  
  	case 2:
84a90b610   JJ Ding   Input: elantech -...
822
823
824
  		/* ignore debounce */
  		if (elantech_debounce_check_v2(psmouse))
  			return PSMOUSE_FULL_PACKET;
7894f21b1   JJ Ding   Input: elantech -...
825
826
  		if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
  			return PSMOUSE_BAD_DATA;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
827
828
  		elantech_report_absolute_v2(psmouse);
  		break;
28f496161   JJ Ding   Input: elantech -...
829
830
831
  
  	case 3:
  		packet_type = elantech_packet_check_v3(psmouse);
a2418fc4a   Ulrik De Bie   Input: elantech -...
832
833
  		switch (packet_type) {
  		case PACKET_UNKNOWN:
28f496161   JJ Ding   Input: elantech -...
834
  			return PSMOUSE_BAD_DATA;
a2418fc4a   Ulrik De Bie   Input: elantech -...
835
836
837
838
839
840
841
842
843
844
845
846
  		case PACKET_DEBOUNCE:
  			/* ignore debounce */
  			break;
  
  		case PACKET_TRACKPOINT:
  			elantech_report_trackpoint(psmouse, packet_type);
  			break;
  
  		default:
  			elantech_report_absolute_v3(psmouse, packet_type);
  			break;
  		}
28f496161   JJ Ding   Input: elantech -...
847
  		break;
1dc6edec1   JJ Ding   Input: elantech -...
848
849
850
  
  	case 4:
  		packet_type = elantech_packet_check_v4(psmouse);
caeb0d37f   Ulrik De Bie   Input: elantech -...
851
852
  		switch (packet_type) {
  		case PACKET_UNKNOWN:
1dc6edec1   JJ Ding   Input: elantech -...
853
  			return PSMOUSE_BAD_DATA;
caeb0d37f   Ulrik De Bie   Input: elantech -...
854
855
856
857
858
859
860
861
  		case PACKET_TRACKPOINT:
  			elantech_report_trackpoint(psmouse, packet_type);
  			break;
  
  		default:
  			elantech_report_absolute_v4(psmouse, packet_type);
  			break;
  		}
1dc6edec1   JJ Ding   Input: elantech -...
862
  		break;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
  	}
  
  	return PSMOUSE_FULL_PACKET;
  }
  
  /*
   * Put the touchpad into absolute mode
   */
  static int elantech_set_absolute_mode(struct psmouse *psmouse)
  {
  	struct elantech_data *etd = psmouse->private;
  	unsigned char val;
  	int tries = ETP_READ_BACK_TRIES;
  	int rc = 0;
  
  	switch (etd->hw_version) {
  	case 1:
  		etd->reg_10 = 0x16;
  		etd->reg_11 = 0x8f;
  		if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
  		    elantech_write_reg(psmouse, 0x11, etd->reg_11)) {
  			rc = -1;
  		}
  		break;
  
  	case 2:
  					/* Windows driver values */
  		etd->reg_10 = 0x54;
  		etd->reg_11 = 0x88;	/* 0x8a */
  		etd->reg_21 = 0x60;	/* 0x00 */
  		if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
  		    elantech_write_reg(psmouse, 0x11, etd->reg_11) ||
  		    elantech_write_reg(psmouse, 0x21, etd->reg_21)) {
  			rc = -1;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
897
  		}
28f496161   JJ Ding   Input: elantech -...
898
899
900
  		break;
  
  	case 3:
36189cc3c   Hans de Goede   Input: elantech -...
901
902
903
  		if (etd->set_hw_resolution)
  			etd->reg_10 = 0x0b;
  		else
fb4f8f568   Hans de Goede   Input: elantech -...
904
  			etd->reg_10 = 0x01;
36189cc3c   Hans de Goede   Input: elantech -...
905

28f496161   JJ Ding   Input: elantech -...
906
907
908
909
  		if (elantech_write_reg(psmouse, 0x10, etd->reg_10))
  			rc = -1;
  
  		break;
1dc6edec1   JJ Ding   Input: elantech -...
910
911
912
913
914
915
916
  
  	case 4:
  		etd->reg_07 = 0x01;
  		if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
  			rc = -1;
  
  		goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */
b2546df69   Arjan Opmeer   Input: elantech -...
917
918
919
  	}
  
  	if (rc == 0) {
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
920
  		/*
b2546df69   Arjan Opmeer   Input: elantech -...
921
922
  		 * Read back reg 0x10. For hardware version 1 we must make
  		 * sure the absolute mode bit is set. For hardware version 2
b5d217043   Dmitry Torokhov   Input: psmouse - ...
923
  		 * the touchpad is probably initializing and not ready until
b2546df69   Arjan Opmeer   Input: elantech -...
924
  		 * we read back the value we just wrote.
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
925
926
927
928
929
930
  		 */
  		do {
  			rc = elantech_read_reg(psmouse, 0x10, &val);
  			if (rc == 0)
  				break;
  			tries--;
d4ae84a84   Dmitry Torokhov   Input: elantech -...
931
932
  			elantech_debug("retrying read (%d).
  ", tries);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
933
934
  			msleep(ETP_READ_BACK_DELAY);
  		} while (tries > 0);
b2546df69   Arjan Opmeer   Input: elantech -...
935
936
  
  		if (rc) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
937
938
939
  			psmouse_err(psmouse,
  				    "failed to read back register 0x10.
  ");
b2546df69   Arjan Opmeer   Input: elantech -...
940
941
  		} else if (etd->hw_version == 1 &&
  			   !(val & ETP_R10_ABSOLUTE_MODE)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
942
943
944
  			psmouse_err(psmouse,
  				    "touchpad refuses to switch to absolute mode.
  ");
b2546df69   Arjan Opmeer   Input: elantech -...
945
946
  			rc = -1;
  		}
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
947
  	}
1dc6edec1   JJ Ding   Input: elantech -...
948
   skip_readback_reg_10:
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
949
  	if (rc)
b5d217043   Dmitry Torokhov   Input: psmouse - ...
950
951
  		psmouse_err(psmouse, "failed to initialise registers.
  ");
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
952
953
954
  
  	return rc;
  }
28f496161   JJ Ding   Input: elantech -...
955
956
  static int elantech_set_range(struct psmouse *psmouse,
  			      unsigned int *x_min, unsigned int *y_min,
1dc6edec1   JJ Ding   Input: elantech -...
957
958
  			      unsigned int *x_max, unsigned int *y_max,
  			      unsigned int *width)
230282a77   JJ Ding   Input: elantech -...
959
960
  {
  	struct elantech_data *etd = psmouse->private;
28f496161   JJ Ding   Input: elantech -...
961
  	unsigned char param[3];
1dc6edec1   JJ Ding   Input: elantech -...
962
  	unsigned char traces;
230282a77   JJ Ding   Input: elantech -...
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
  
  	switch (etd->hw_version) {
  	case 1:
  		*x_min = ETP_XMIN_V1;
  		*y_min = ETP_YMIN_V1;
  		*x_max = ETP_XMAX_V1;
  		*y_max = ETP_YMAX_V1;
  		break;
  
  	case 2:
  		if (etd->fw_version == 0x020800 ||
  		    etd->fw_version == 0x020b00 ||
  		    etd->fw_version == 0x020030) {
  			*x_min = ETP_XMIN_V2;
  			*y_min = ETP_YMIN_V2;
  			*x_max = ETP_XMAX_V2;
  			*y_max = ETP_YMAX_V2;
  		} else {
84a90b610   JJ Ding   Input: elantech -...
981
982
  			int i;
  			int fixed_dpi;
230282a77   JJ Ding   Input: elantech -...
983
984
  			i = (etd->fw_version > 0x020800 &&
  			     etd->fw_version < 0x020900) ? 1 : 2;
84a90b610   JJ Ding   Input: elantech -...
985

b56b92a9a   JJ Ding   Input: elantech -...
986
  			if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
84a90b610   JJ Ding   Input: elantech -...
987
988
989
990
991
  				return -1;
  
  			fixed_dpi = param[1] & 0x10;
  
  			if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
b56b92a9a   JJ Ding   Input: elantech -...
992
  				if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
84a90b610   JJ Ding   Input: elantech -...
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
  					return -1;
  
  				*x_max = (etd->capabilities[1] - i) * param[1] / 2;
  				*y_max = (etd->capabilities[2] - i) * param[2] / 2;
  			} else if (etd->fw_version == 0x040216) {
  				*x_max = 819;
  				*y_max = 405;
  			} else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) {
  				*x_max = 900;
  				*y_max = 500;
  			} else {
  				*x_max = (etd->capabilities[1] - i) * 64;
  				*y_max = (etd->capabilities[2] - i) * 64;
  			}
230282a77   JJ Ding   Input: elantech -...
1007
1008
  		}
  		break;
28f496161   JJ Ding   Input: elantech -...
1009
1010
  
  	case 3:
b56b92a9a   JJ Ding   Input: elantech -...
1011
  		if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
28f496161   JJ Ding   Input: elantech -...
1012
1013
1014
1015
1016
  			return -1;
  
  		*x_max = (0x0f & param[0]) << 8 | param[1];
  		*y_max = (0xf0 & param[0]) << 4 | param[2];
  		break;
1dc6edec1   JJ Ding   Input: elantech -...
1017
1018
  
  	case 4:
b56b92a9a   JJ Ding   Input: elantech -...
1019
  		if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
1dc6edec1   JJ Ding   Input: elantech -...
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
  			return -1;
  
  		*x_max = (0x0f & param[0]) << 8 | param[1];
  		*y_max = (0xf0 & param[0]) << 4 | param[2];
  		traces = etd->capabilities[1];
  		if ((traces < 2) || (traces > *x_max))
  			return -1;
  
  		*width = *x_max / (traces - 1);
  		break;
230282a77   JJ Ding   Input: elantech -...
1030
  	}
28f496161   JJ Ding   Input: elantech -...
1031
1032
  
  	return 0;
230282a77   JJ Ding   Input: elantech -...
1033
  }
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1034
  /*
3d95fd6ad   JJ Ding   Input: elantech -...
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
   * (value from firmware) * 10 + 790 = dpi
   * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
   */
  static unsigned int elantech_convert_res(unsigned int val)
  {
  	return (val * 10 + 790) * 10 / 254;
  }
  
  static int elantech_get_resolution_v4(struct psmouse *psmouse,
  				      unsigned int *x_res,
  				      unsigned int *y_res)
  {
  	unsigned char param[3];
  
  	if (elantech_send_cmd(psmouse, ETP_RESOLUTION_QUERY, param))
  		return -1;
  
  	*x_res = elantech_convert_res(param[1] & 0x0f);
  	*y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
  
  	return 0;
  }
  
  /*
c15bdfd5b   Hans de Goede   Input: elantech -...
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
   * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
   * fw_version for this is based on the following fw_version & caps table:
   *
   * Laptop-model:           fw_version:     caps:           buttons:
   * Acer S3                 0x461f00        10, 13, 0e      clickpad
   * Acer S7-392             0x581f01        50, 17, 0d      clickpad
   * Acer V5-131             0x461f02        01, 16, 0c      clickpad
   * Acer V5-551             0x461f00        ?               clickpad
   * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons
   * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons
   * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons
   * Asus UX31               0x361f00        20, 15, 0e      clickpad
   * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
   * Avatar AVIU-145A2       0x361f00        ?               clickpad
a2418fc4a   Ulrik De Bie   Input: elantech -...
1073
   * Fujitsu H730            0x570f00        c0, 14, 0c      3 hw buttons (**)
c15bdfd5b   Hans de Goede   Input: elantech -...
1074
1075
   * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons
   * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*)
a2418fc4a   Ulrik De Bie   Input: elantech -...
1076
   * Lenovo L530             0x350f02        b9, 15, 0c      2 hw buttons (*)
c15bdfd5b   Hans de Goede   Input: elantech -...
1077
1078
1079
1080
1081
1082
1083
1084
1085
   * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons
   * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad
   * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad
   * Samsung NP900X3E-A02    0x575f03        ?               clickpad
   * Samsung NP-QX410        0x851b00        19, 14, 0c      clickpad
   * Samsung RC512           0x450f00        08, 15, 0c      2 hw buttons
   * Samsung RF710           0x450f00        ?               2 hw buttons
   * System76 Pangolin       0x250f01        ?               2 hw buttons
   * (*) + 3 trackpoint buttons
a2418fc4a   Ulrik De Bie   Input: elantech -...
1086
1087
   * (**) + 0 trackpoint buttons
   * Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps
c15bdfd5b   Hans de Goede   Input: elantech -...
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
   */
  static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
  {
  	struct input_dev *dev = psmouse->dev;
  	struct elantech_data *etd = psmouse->private;
  
  	if (etd->fw_version & 0x001000) {
  		__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
  		__clear_bit(BTN_RIGHT, dev->keybit);
  	}
  }
  
  /*
f386474e1   Ulrik De Bie   Input: elantech -...
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
   * Some hw_version 4 models do have a middle button
   */
  static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
  #if defined(CONFIG_DMI) && defined(CONFIG_X86)
  	{
  		/* Fujitsu H730 has a middle button */
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
  			DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
  		},
  	},
  #endif
  	{ }
  };
  
  /*
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1117
1118
   * Set the appropriate event bits for the input subsystem
   */
28f496161   JJ Ding   Input: elantech -...
1119
  static int elantech_set_input_params(struct psmouse *psmouse)
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1120
1121
1122
  {
  	struct input_dev *dev = psmouse->dev;
  	struct elantech_data *etd = psmouse->private;
1dc6edec1   JJ Ding   Input: elantech -...
1123
  	unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
3d95fd6ad   JJ Ding   Input: elantech -...
1124
  	unsigned int x_res = 0, y_res = 0;
230282a77   JJ Ding   Input: elantech -...
1125

1dc6edec1   JJ Ding   Input: elantech -...
1126
  	if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
28f496161   JJ Ding   Input: elantech -...
1127
  		return -1;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1128

e3dde4fba   JJ Ding   Input: elantech -...
1129
  	__set_bit(INPUT_PROP_POINTER, dev->propbit);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1130
1131
  	__set_bit(EV_KEY, dev->evbit);
  	__set_bit(EV_ABS, dev->evbit);
c7a1f3ccf   Dmitry Torokhov   Input: elantech -...
1132
  	__clear_bit(EV_REL, dev->evbit);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1133
1134
  
  	__set_bit(BTN_LEFT, dev->keybit);
f386474e1   Ulrik De Bie   Input: elantech -...
1135
1136
  	if (dmi_check_system(elantech_dmi_has_middle_button))
  		__set_bit(BTN_MIDDLE, dev->keybit);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
  	__set_bit(BTN_RIGHT, dev->keybit);
  
  	__set_bit(BTN_TOUCH, dev->keybit);
  	__set_bit(BTN_TOOL_FINGER, dev->keybit);
  	__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
  	__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
  
  	switch (etd->hw_version) {
  	case 1:
  		/* Rocker button */
504e8beed   Dmitry Torokhov   Input: elantech -...
1147
  		if (etd->fw_version < 0x020000 &&
230282a77   JJ Ding   Input: elantech -...
1148
  		    (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1149
1150
1151
  			__set_bit(BTN_FORWARD, dev->keybit);
  			__set_bit(BTN_BACK, dev->keybit);
  		}
230282a77   JJ Ding   Input: elantech -...
1152
1153
  		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
  		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1154
1155
1156
  		break;
  
  	case 2:
22462d9fc   Éric Piel   Input: elantech -...
1157
  		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
28f496161   JJ Ding   Input: elantech -...
1158
1159
1160
  		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
  		/* fall through */
  	case 3:
c15bdfd5b   Hans de Goede   Input: elantech -...
1161
1162
  		if (etd->hw_version == 3)
  			elantech_set_buttonpad_prop(psmouse);
230282a77   JJ Ding   Input: elantech -...
1163
1164
  		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
  		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
f941c705f   Éric Piel   Input: elantech -...
1165
1166
1167
1168
1169
1170
  		if (etd->reports_pressure) {
  			input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
  					     ETP_PMAX_V2, 0, 0);
  			input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
  					     ETP_WMAX_V2, 0, 0);
  		}
b4adbbefc   Henrik Rydberg   Input: MT - Add f...
1171
  		input_mt_init_slots(dev, 2, 0);
230282a77   JJ Ding   Input: elantech -...
1172
1173
  		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
  		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1174
  		break;
1dc6edec1   JJ Ding   Input: elantech -...
1175
1176
  
  	case 4:
3d95fd6ad   JJ Ding   Input: elantech -...
1177
1178
1179
1180
1181
1182
1183
1184
  		if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) {
  			/*
  			 * if query failed, print a warning and leave the values
  			 * zero to resemble synaptics.c behavior.
  			 */
  			psmouse_warn(psmouse, "couldn't query resolution data.
  ");
  		}
c15bdfd5b   Hans de Goede   Input: elantech -...
1185
  		elantech_set_buttonpad_prop(psmouse);
1dc6edec1   JJ Ding   Input: elantech -...
1186
1187
1188
1189
  		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
  		/* For X to recognize me as touchpad. */
  		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
  		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
3d95fd6ad   JJ Ding   Input: elantech -...
1190
1191
  		input_abs_set_res(dev, ABS_X, x_res);
  		input_abs_set_res(dev, ABS_Y, y_res);
1dc6edec1   JJ Ding   Input: elantech -...
1192
1193
1194
1195
1196
1197
1198
1199
1200
  		/*
  		 * range of pressure and width is the same as v2,
  		 * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility.
  		 */
  		input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
  				     ETP_PMAX_V2, 0, 0);
  		input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
  				     ETP_WMAX_V2, 0, 0);
  		/* Multitouch capable pad, up to 5 fingers. */
b4adbbefc   Henrik Rydberg   Input: MT - Add f...
1201
  		input_mt_init_slots(dev, ETP_MAX_FINGERS, 0);
1dc6edec1   JJ Ding   Input: elantech -...
1202
1203
  		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
  		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
3d95fd6ad   JJ Ding   Input: elantech -...
1204
1205
  		input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
  		input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res);
1dc6edec1   JJ Ding   Input: elantech -...
1206
1207
1208
1209
1210
1211
1212
1213
1214
  		input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2,
  				     ETP_PMAX_V2, 0, 0);
  		/*
  		 * The firmware reports how many trace lines the finger spans,
  		 * convert to surface unit as Protocol-B requires.
  		 */
  		input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0,
  				     ETP_WMAX_V2 * width, 0, 0);
  		break;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1215
  	}
230282a77   JJ Ding   Input: elantech -...
1216
1217
  
  	etd->y_max = y_max;
1dc6edec1   JJ Ding   Input: elantech -...
1218
  	etd->width = width;
28f496161   JJ Ding   Input: elantech -...
1219
1220
  
  	return 0;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
  }
  
  struct elantech_attr_data {
  	size_t		field_offset;
  	unsigned char	reg;
  };
  
  /*
   * Display a register value by reading a sysfs entry
   */
  static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data,
  					char *buf)
  {
  	struct elantech_data *etd = psmouse->private;
  	struct elantech_attr_data *attr = data;
  	unsigned char *reg = (unsigned char *) etd + attr->field_offset;
  	int rc = 0;
  
  	if (attr->reg)
  		rc = elantech_read_reg(psmouse, attr->reg, reg);
  
  	return sprintf(buf, "0x%02x
  ", (attr->reg && rc) ? -1 : *reg);
  }
  
  /*
   * Write a register value by writing a sysfs entry
   */
  static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
  				     void *data, const char *buf, size_t count)
  {
  	struct elantech_data *etd = psmouse->private;
  	struct elantech_attr_data *attr = data;
  	unsigned char *reg = (unsigned char *) etd + attr->field_offset;
76496e7a0   JJ Ding   Input: convert ob...
1255
  	unsigned char value;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1256
  	int err;
76496e7a0   JJ Ding   Input: convert ob...
1257
  	err = kstrtou8(buf, 16, &value);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1258
1259
  	if (err)
  		return err;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
  	/* Do we need to preserve some bits for version 2 hardware too? */
  	if (etd->hw_version == 1) {
  		if (attr->reg == 0x10)
  			/* Force absolute mode always on */
  			value |= ETP_R10_ABSOLUTE_MODE;
  		else if (attr->reg == 0x11)
  			/* Force 4 byte mode always on */
  			value |= ETP_R11_4_BYTE_MODE;
  	}
  
  	if (!attr->reg || elantech_write_reg(psmouse, attr->reg, value) == 0)
  		*reg = value;
  
  	return count;
  }
  
  #define ELANTECH_INT_ATTR(_name, _register)				\
  	static struct elantech_attr_data elantech_attr_##_name = {	\
  		.field_offset = offsetof(struct elantech_data, _name),	\
  		.reg = _register,					\
  	};								\
  	PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,			\
  			    &elantech_attr_##_name,			\
  			    elantech_show_int_attr,			\
  			    elantech_set_int_attr)
1dc6edec1   JJ Ding   Input: elantech -...
1285
  ELANTECH_INT_ATTR(reg_07, 0x07);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
  ELANTECH_INT_ATTR(reg_10, 0x10);
  ELANTECH_INT_ATTR(reg_11, 0x11);
  ELANTECH_INT_ATTR(reg_20, 0x20);
  ELANTECH_INT_ATTR(reg_21, 0x21);
  ELANTECH_INT_ATTR(reg_22, 0x22);
  ELANTECH_INT_ATTR(reg_23, 0x23);
  ELANTECH_INT_ATTR(reg_24, 0x24);
  ELANTECH_INT_ATTR(reg_25, 0x25);
  ELANTECH_INT_ATTR(reg_26, 0x26);
  ELANTECH_INT_ATTR(debug, 0);
  ELANTECH_INT_ATTR(paritycheck, 0);
2d9eb81fd   Ulrik De Bie   Input: elantech -...
1297
  ELANTECH_INT_ATTR(crc_enabled, 0);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1298
1299
  
  static struct attribute *elantech_attrs[] = {
1dc6edec1   JJ Ding   Input: elantech -...
1300
  	&psmouse_attr_reg_07.dattr.attr,
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
  	&psmouse_attr_reg_10.dattr.attr,
  	&psmouse_attr_reg_11.dattr.attr,
  	&psmouse_attr_reg_20.dattr.attr,
  	&psmouse_attr_reg_21.dattr.attr,
  	&psmouse_attr_reg_22.dattr.attr,
  	&psmouse_attr_reg_23.dattr.attr,
  	&psmouse_attr_reg_24.dattr.attr,
  	&psmouse_attr_reg_25.dattr.attr,
  	&psmouse_attr_reg_26.dattr.attr,
  	&psmouse_attr_debug.dattr.attr,
  	&psmouse_attr_paritycheck.dattr.attr,
2d9eb81fd   Ulrik De Bie   Input: elantech -...
1312
  	&psmouse_attr_crc_enabled.dattr.attr,
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1313
1314
1315
1316
1317
1318
  	NULL
  };
  
  static struct attribute_group elantech_attr_group = {
  	.attrs = elantech_attrs,
  };
a083632ea   Dmitry Torokhov   Input: elantech -...
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
  static bool elantech_is_signature_valid(const unsigned char *param)
  {
  	static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 };
  	int i;
  
  	if (param[0] == 0)
  		return false;
  
  	if (param[1] == 0)
  		return true;
271329b3c   Hans de Goede   Input: elantech -...
1329
1330
1331
1332
1333
1334
  	/*
  	 * Some models have a revision higher then 20. Meaning param[2] may
  	 * be 10 or 20, skip the rates check for these.
  	 */
  	if (param[0] == 0x46 && (param[1] & 0xef) == 0x0f && param[2] < 40)
  		return true;
a083632ea   Dmitry Torokhov   Input: elantech -...
1335
1336
1337
1338
1339
1340
  	for (i = 0; i < ARRAY_SIZE(rates); i++)
  		if (param[2] == rates[i])
  			return false;
  
  	return true;
  }
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1341
1342
1343
  /*
   * Use magic knock to detect Elantech touchpad
   */
b7802c5c1   Dmitry Torokhov   Input: psmouse - ...
1344
  int elantech_detect(struct psmouse *psmouse, bool set_properties)
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
  {
  	struct ps2dev *ps2dev = &psmouse->ps2dev;
  	unsigned char param[3];
  
  	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
  
  	if (ps2_command(ps2dev,  NULL, PSMOUSE_CMD_DISABLE) ||
  	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
  	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
  	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
  	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1356
1357
  		psmouse_dbg(psmouse, "sending Elantech magic knock failed.
  ");
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1358
1359
1360
1361
1362
1363
1364
  		return -1;
  	}
  
  	/*
  	 * Report this in case there are Elantech models that use a different
  	 * set of magic numbers
  	 */
28f496161   JJ Ding   Input: elantech -...
1365
1366
  	if (param[0] != 0x3c || param[1] != 0x03 ||
  	    (param[2] != 0xc8 && param[2] != 0x00)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1367
1368
1369
1370
  		psmouse_dbg(psmouse,
  			    "unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.
  ",
  			    param[0], param[1], param[2]);
9ab7b25e6   Arjan Opmeer   Input: elantech -...
1371
1372
1373
1374
1375
1376
1377
1378
1379
  		return -1;
  	}
  
  	/*
  	 * Query touchpad's firmware version and see if it reports known
  	 * value to avoid mis-detection. Logitech mice are known to respond
  	 * to Elantech magic knock and there might be more.
  	 */
  	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1380
1381
  		psmouse_dbg(psmouse, "failed to query firmware version.
  ");
9ab7b25e6   Arjan Opmeer   Input: elantech -...
1382
1383
  		return -1;
  	}
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1384
1385
1386
1387
  	psmouse_dbg(psmouse,
  		    "Elantech version query result 0x%02x, 0x%02x, 0x%02x.
  ",
  		    param[0], param[1], param[2]);
9ab7b25e6   Arjan Opmeer   Input: elantech -...
1388

a083632ea   Dmitry Torokhov   Input: elantech -...
1389
  	if (!elantech_is_signature_valid(param)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1390
1391
1392
  		psmouse_dbg(psmouse,
  			    "Probably not a real Elantech touchpad. Aborting.
  ");
4af61e902   JJ Ding   Input: elantech -...
1393
  		return -1;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
  	}
  
  	if (set_properties) {
  		psmouse->vendor = "Elantech";
  		psmouse->name = "Touchpad";
  	}
  
  	return 0;
  }
  
  /*
   * Clean up sysfs entries when disconnecting
   */
  static void elantech_disconnect(struct psmouse *psmouse)
  {
a2418fc4a   Ulrik De Bie   Input: elantech -...
1409
1410
1411
1412
  	struct elantech_data *etd = psmouse->private;
  
  	if (etd->tp_dev)
  		input_unregister_device(etd->tp_dev);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
  	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
  			   &elantech_attr_group);
  	kfree(psmouse->private);
  	psmouse->private = NULL;
  }
  
  /*
   * Put the touchpad back into absolute mode when reconnecting
   */
  static int elantech_reconnect(struct psmouse *psmouse)
  {
a67ada7a7   JJ Ding   Input: elantech -...
1424
  	psmouse_reset(psmouse);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1425
1426
1427
1428
  	if (elantech_detect(psmouse, 0))
  		return -1;
  
  	if (elantech_set_absolute_mode(psmouse)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1429
1430
1431
  		psmouse_err(psmouse,
  			    "failed to put touchpad back into absolute mode.
  ");
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1432
1433
1434
1435
1436
1437
1438
  		return -1;
  	}
  
  	return 0;
  }
  
  /*
0dc158790   Ulrik De Bie   Input: elantech -...
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
   * Some hw_version 4 models do not work with crc_disabled
   */
  static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
  #if defined(CONFIG_DMI) && defined(CONFIG_X86)
  	{
  		/* Fujitsu H730 does not work with crc_enabled == 0 */
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
  			DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
  		},
  	},
  #endif
  	{ }
  };
  
  /*
fb4f8f568   Hans de Goede   Input: elantech -...
1455
1456
   * Some hw_version 3 models go into error state when we try to set
   * bit 3 and/or bit 1 of r10.
36189cc3c   Hans de Goede   Input: elantech -...
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
   */
  static const struct dmi_system_id no_hw_res_dmi_table[] = {
  #if defined(CONFIG_DMI) && defined(CONFIG_X86)
  	{
  		/* Gigabyte U2442 */
  		.matches = {
  			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
  			DMI_MATCH(DMI_PRODUCT_NAME, "U2442"),
  		},
  	},
  #endif
  	{ }
  };
  
  /*
3c8bbb951   JJ Ding   Input: elantech -...
1472
1473
   * determine hardware version and set some properties according to it.
   */
28f496161   JJ Ding   Input: elantech -...
1474
  static int elantech_set_properties(struct elantech_data *etd)
3c8bbb951   JJ Ding   Input: elantech -...
1475
  {
3940d6185   JJ Ding   Input: elantech -...
1476
  	/* This represents the version of IC body. */
1dc6edec1   JJ Ding   Input: elantech -...
1477
  	int ver = (etd->fw_version & 0x0f0000) >> 16;
3940d6185   JJ Ding   Input: elantech -...
1478
  	/* Early version of Elan touchpads doesn't obey the rule. */
3c8bbb951   JJ Ding   Input: elantech -...
1479
1480
  	if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
  		etd->hw_version = 1;
3940d6185   JJ Ding   Input: elantech -...
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
  	else {
  		switch (ver) {
  		case 2:
  		case 4:
  			etd->hw_version = 2;
  			break;
  		case 5:
  			etd->hw_version = 3;
  			break;
  		case 6:
9eebed7de   Matteo Delfino   Input: elantech -...
1491
  		case 7:
9cb80b965   Matt Walker   Input: elantech -...
1492
  		case 8:
ae4bedf06   Jordan Rife   Input: elantech -...
1493
  		case 9:
17afe4769   Sam hung   Input: elantech -...
1494
1495
  		case 10:
  		case 13:
3940d6185   JJ Ding   Input: elantech -...
1496
1497
1498
1499
1500
1501
  			etd->hw_version = 4;
  			break;
  		default:
  			return -1;
  		}
  	}
3c8bbb951   JJ Ding   Input: elantech -...
1502

b56b92a9a   JJ Ding   Input: elantech -...
1503
1504
1505
1506
1507
  	/* decide which send_cmd we're gonna use early */
  	etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd :
  					       synaptics_send_cmd;
  
  	/* Turn on packet checking by default */
3c8bbb951   JJ Ding   Input: elantech -...
1508
1509
1510
1511
1512
1513
1514
1515
1516
  	etd->paritycheck = 1;
  
  	/*
  	 * This firmware suffers from misreporting coordinates when
  	 * a touch action starts causing the mouse cursor or scrolled page
  	 * to jump. Enable a workaround.
  	 */
  	etd->jumpy_cursor =
  		(etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
28f496161   JJ Ding   Input: elantech -...
1517
  	if (etd->hw_version > 1) {
3c8bbb951   JJ Ding   Input: elantech -...
1518
1519
1520
1521
1522
1523
  		/* For now show extra debug information */
  		etd->debug = 1;
  
  		if (etd->fw_version >= 0x020800)
  			etd->reports_pressure = true;
  	}
28f496161   JJ Ding   Input: elantech -...
1524

acc0444bf   Matteo Delfino   Input: elantech -...
1525
1526
1527
1528
  	/*
  	 * The signatures of v3 and v4 packets change depending on the
  	 * value of this hardware flag.
  	 */
0dc158790   Ulrik De Bie   Input: elantech -...
1529
1530
  	etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 ||
  			   dmi_check_system(elantech_dmi_force_crc_enabled);
acc0444bf   Matteo Delfino   Input: elantech -...
1531

36189cc3c   Hans de Goede   Input: elantech -...
1532
1533
  	/* Enable real hardware resolution on hw_version 3 ? */
  	etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
28f496161   JJ Ding   Input: elantech -...
1534
  	return 0;
3c8bbb951   JJ Ding   Input: elantech -...
1535
1536
1537
  }
  
  /*
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1538
1539
1540
1541
1542
   * Initialize the touchpad and create sysfs entries
   */
  int elantech_init(struct psmouse *psmouse)
  {
  	struct elantech_data *etd;
a2418fc4a   Ulrik De Bie   Input: elantech -...
1543
1544
  	int i;
  	int error = -EINVAL;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1545
  	unsigned char param[3];
a2418fc4a   Ulrik De Bie   Input: elantech -...
1546
  	struct input_dev *tp_dev;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1547

9ab7b25e6   Arjan Opmeer   Input: elantech -...
1548
  	psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1549
  	if (!etd)
6792cbbb2   Davidlohr Bueso   Input: return -EN...
1550
  		return -ENOMEM;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1551

a67ada7a7   JJ Ding   Input: elantech -...
1552
  	psmouse_reset(psmouse);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1553
1554
1555
1556
1557
  	etd->parity[0] = 1;
  	for (i = 1; i < 256; i++)
  		etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
  
  	/*
9ab7b25e6   Arjan Opmeer   Input: elantech -...
1558
  	 * Do the version query again so we can store the result
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1559
1560
  	 */
  	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1561
1562
  		psmouse_err(psmouse, "failed to query firmware version.
  ");
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1563
1564
  		goto init_fail;
  	}
504e8beed   Dmitry Torokhov   Input: elantech -...
1565
  	etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
28f496161   JJ Ding   Input: elantech -...
1566
1567
  
  	if (elantech_set_properties(etd)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1568
1569
  		psmouse_err(psmouse, "unknown hardware version, aborting...
  ");
28f496161   JJ Ding   Input: elantech -...
1570
1571
  		goto init_fail;
  	}
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1572
1573
1574
1575
  	psmouse_info(psmouse,
  		     "assuming hardware version %d (with firmware version 0x%02x%02x%02x)
  ",
  		     etd->hw_version, param[0], param[1], param[2]);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1576

b56b92a9a   JJ Ding   Input: elantech -...
1577
  	if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
230282a77   JJ Ding   Input: elantech -...
1578
  	    etd->capabilities)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1579
1580
  		psmouse_err(psmouse, "failed to query capabilities.
  ");
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1581
1582
  		goto init_fail;
  	}
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1583
1584
1585
1586
1587
  	psmouse_info(psmouse,
  		     "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.
  ",
  		     etd->capabilities[0], etd->capabilities[1],
  		     etd->capabilities[2]);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1588
1589
  
  	if (elantech_set_absolute_mode(psmouse)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1590
1591
1592
  		psmouse_err(psmouse,
  			    "failed to put touchpad into absolute mode.
  ");
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1593
1594
  		goto init_fail;
  	}
28f496161   JJ Ding   Input: elantech -...
1595
  	if (elantech_set_input_params(psmouse)) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1596
1597
  		psmouse_err(psmouse, "failed to query touchpad range.
  ");
28f496161   JJ Ding   Input: elantech -...
1598
1599
  		goto init_fail;
  	}
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1600
1601
1602
1603
  
  	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
  				   &elantech_attr_group);
  	if (error) {
b5d217043   Dmitry Torokhov   Input: psmouse - ...
1604
1605
1606
1607
  		psmouse_err(psmouse,
  			    "failed to create sysfs attributes, error: %d.
  ",
  			    error);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1608
1609
  		goto init_fail;
  	}
a2418fc4a   Ulrik De Bie   Input: elantech -...
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
  	/* The MSB indicates the presence of the trackpoint */
  	if ((etd->capabilities[0] & 0x80) == 0x80) {
  		tp_dev = input_allocate_device();
  
  		if (!tp_dev) {
  			error = -ENOMEM;
  			goto init_fail_tp_alloc;
  		}
  
  		etd->tp_dev = tp_dev;
  		snprintf(etd->tp_phys, sizeof(etd->tp_phys), "%s/input1",
  			psmouse->ps2dev.serio->phys);
  		tp_dev->phys = etd->tp_phys;
  		tp_dev->name = "Elantech PS/2 TrackPoint";
  		tp_dev->id.bustype = BUS_I8042;
  		tp_dev->id.vendor  = 0x0002;
  		tp_dev->id.product = PSMOUSE_ELANTECH;
  		tp_dev->id.version = 0x0000;
  		tp_dev->dev.parent = &psmouse->ps2dev.serio->dev;
  		tp_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
  		tp_dev->relbit[BIT_WORD(REL_X)] =
  			BIT_MASK(REL_X) | BIT_MASK(REL_Y);
  		tp_dev->keybit[BIT_WORD(BTN_LEFT)] =
  			BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) |
  			BIT_MASK(BTN_RIGHT);
7611392fe   Hans de Goede   Input: add INPUT_...
1635

01d4cd5c4   Hans de Goede   Input: add missin...
1636
  		__set_bit(INPUT_PROP_POINTER, tp_dev->propbit);
7611392fe   Hans de Goede   Input: add INPUT_...
1637
  		__set_bit(INPUT_PROP_POINTING_STICK, tp_dev->propbit);
a2418fc4a   Ulrik De Bie   Input: elantech -...
1638
1639
1640
1641
  		error = input_register_device(etd->tp_dev);
  		if (error < 0)
  			goto init_fail_tp_reg;
  	}
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1642
1643
1644
  	psmouse->protocol_handler = elantech_process_byte;
  	psmouse->disconnect = elantech_disconnect;
  	psmouse->reconnect = elantech_reconnect;
28f496161   JJ Ding   Input: elantech -...
1645
  	psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1646
1647
  
  	return 0;
a2418fc4a   Ulrik De Bie   Input: elantech -...
1648
1649
1650
1651
1652
   init_fail_tp_reg:
  	input_free_device(tp_dev);
   init_fail_tp_alloc:
  	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
  			   &elantech_attr_group);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1653
   init_fail:
ac84eba22   Ulrik De Bie   Input: elantech -...
1654
  	psmouse_reset(psmouse);
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1655
  	kfree(etd);
a2418fc4a   Ulrik De Bie   Input: elantech -...
1656
  	return error;
2a0bd75e5   Arjan Opmeer   Input: psmouse - ...
1657
  }