1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /*- |
3 | * Finger Sensing Pad PS/2 mouse driver. |
4 | * |
5 | * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. |
6 | * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation. |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/input.h> |
11 | #include <linux/input/mt.h> |
12 | #include <linux/ctype.h> |
13 | #include <linux/libps2.h> |
14 | #include <linux/serio.h> |
15 | #include <linux/jiffies.h> |
16 | #include <linux/slab.h> |
17 | |
18 | #include "psmouse.h" |
19 | #include "sentelic.h" |
20 | |
21 | /* |
22 | * Timeout for FSP PS/2 command only (in milliseconds). |
23 | */ |
24 | #define FSP_CMD_TIMEOUT 200 |
25 | #define FSP_CMD_TIMEOUT2 30 |
26 | |
27 | #define GET_ABS_X(packet) ((packet[1] << 2) | ((packet[3] >> 2) & 0x03)) |
28 | #define GET_ABS_Y(packet) ((packet[2] << 2) | (packet[3] & 0x03)) |
29 | |
30 | /** Driver version. */ |
31 | static const char fsp_drv_ver[] = "1.1.0-K" ; |
32 | |
33 | /* |
34 | * Make sure that the value being sent to FSP will not conflict with |
35 | * possible sample rate values. |
36 | */ |
37 | static unsigned char fsp_test_swap_cmd(unsigned char reg_val) |
38 | { |
39 | switch (reg_val) { |
40 | case 10: case 20: case 40: case 60: case 80: case 100: case 200: |
41 | /* |
42 | * The requested value being sent to FSP matched to possible |
43 | * sample rates, swap the given value such that the hardware |
44 | * wouldn't get confused. |
45 | */ |
46 | return (reg_val >> 4) | (reg_val << 4); |
47 | default: |
48 | return reg_val; /* swap isn't necessary */ |
49 | } |
50 | } |
51 | |
52 | /* |
53 | * Make sure that the value being sent to FSP will not conflict with certain |
54 | * commands. |
55 | */ |
56 | static unsigned char fsp_test_invert_cmd(unsigned char reg_val) |
57 | { |
58 | switch (reg_val) { |
59 | case 0xe9: case 0xee: case 0xf2: case 0xff: |
60 | /* |
61 | * The requested value being sent to FSP matched to certain |
62 | * commands, inverse the given value such that the hardware |
63 | * wouldn't get confused. |
64 | */ |
65 | return ~reg_val; |
66 | default: |
67 | return reg_val; /* inversion isn't necessary */ |
68 | } |
69 | } |
70 | |
71 | static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) |
72 | { |
73 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
74 | unsigned char param[3]; |
75 | unsigned char addr; |
76 | int rc = -1; |
77 | |
78 | /* |
79 | * We need to shut off the device and switch it into command |
80 | * mode so we don't confuse our protocol handler. We don't need |
81 | * to do that for writes because sysfs set helper does this for |
82 | * us. |
83 | */ |
84 | psmouse_deactivate(psmouse); |
85 | |
86 | ps2_begin_command(ps2dev); |
87 | |
88 | if (ps2_sendbyte(ps2dev, byte: 0xf3, FSP_CMD_TIMEOUT) < 0) |
89 | goto out; |
90 | |
91 | /* should return 0xfe(request for resending) */ |
92 | ps2_sendbyte(ps2dev, byte: 0x66, FSP_CMD_TIMEOUT2); |
93 | /* should return 0xfc(failed) */ |
94 | ps2_sendbyte(ps2dev, byte: 0x88, FSP_CMD_TIMEOUT2); |
95 | |
96 | if (ps2_sendbyte(ps2dev, byte: 0xf3, FSP_CMD_TIMEOUT) < 0) |
97 | goto out; |
98 | |
99 | if ((addr = fsp_test_invert_cmd(reg_val: reg_addr)) != reg_addr) { |
100 | ps2_sendbyte(ps2dev, byte: 0x68, FSP_CMD_TIMEOUT2); |
101 | } else if ((addr = fsp_test_swap_cmd(reg_val: reg_addr)) != reg_addr) { |
102 | /* swapping is required */ |
103 | ps2_sendbyte(ps2dev, byte: 0xcc, FSP_CMD_TIMEOUT2); |
104 | /* expect 0xfe */ |
105 | } else { |
106 | /* swapping isn't necessary */ |
107 | ps2_sendbyte(ps2dev, byte: 0x66, FSP_CMD_TIMEOUT2); |
108 | /* expect 0xfe */ |
109 | } |
110 | /* should return 0xfc(failed) */ |
111 | ps2_sendbyte(ps2dev, byte: addr, FSP_CMD_TIMEOUT); |
112 | |
113 | if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0) |
114 | goto out; |
115 | |
116 | *reg_val = param[2]; |
117 | rc = 0; |
118 | |
119 | out: |
120 | ps2_end_command(ps2dev); |
121 | psmouse_activate(psmouse); |
122 | psmouse_dbg(psmouse, |
123 | "READ REG: 0x%02x is 0x%02x (rc = %d)\n" , |
124 | reg_addr, *reg_val, rc); |
125 | return rc; |
126 | } |
127 | |
128 | static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) |
129 | { |
130 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
131 | unsigned char v; |
132 | int rc = -1; |
133 | |
134 | ps2_begin_command(ps2dev); |
135 | |
136 | if (ps2_sendbyte(ps2dev, byte: 0xf3, FSP_CMD_TIMEOUT) < 0) |
137 | goto out; |
138 | |
139 | if ((v = fsp_test_invert_cmd(reg_val: reg_addr)) != reg_addr) { |
140 | /* inversion is required */ |
141 | ps2_sendbyte(ps2dev, byte: 0x74, FSP_CMD_TIMEOUT2); |
142 | } else { |
143 | if ((v = fsp_test_swap_cmd(reg_val: reg_addr)) != reg_addr) { |
144 | /* swapping is required */ |
145 | ps2_sendbyte(ps2dev, byte: 0x77, FSP_CMD_TIMEOUT2); |
146 | } else { |
147 | /* swapping isn't necessary */ |
148 | ps2_sendbyte(ps2dev, byte: 0x55, FSP_CMD_TIMEOUT2); |
149 | } |
150 | } |
151 | /* write the register address in correct order */ |
152 | ps2_sendbyte(ps2dev, byte: v, FSP_CMD_TIMEOUT2); |
153 | |
154 | if (ps2_sendbyte(ps2dev, byte: 0xf3, FSP_CMD_TIMEOUT) < 0) |
155 | goto out; |
156 | |
157 | if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { |
158 | /* inversion is required */ |
159 | ps2_sendbyte(ps2dev, byte: 0x47, FSP_CMD_TIMEOUT2); |
160 | } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { |
161 | /* swapping is required */ |
162 | ps2_sendbyte(ps2dev, byte: 0x44, FSP_CMD_TIMEOUT2); |
163 | } else { |
164 | /* swapping isn't necessary */ |
165 | ps2_sendbyte(ps2dev, byte: 0x33, FSP_CMD_TIMEOUT2); |
166 | } |
167 | |
168 | /* write the register value in correct order */ |
169 | ps2_sendbyte(ps2dev, byte: v, FSP_CMD_TIMEOUT2); |
170 | rc = 0; |
171 | |
172 | out: |
173 | ps2_end_command(ps2dev); |
174 | psmouse_dbg(psmouse, |
175 | "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n" , |
176 | reg_addr, reg_val, rc); |
177 | return rc; |
178 | } |
179 | |
180 | /* Enable register clock gating for writing certain registers */ |
181 | static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable) |
182 | { |
183 | int v, nv; |
184 | |
185 | if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, reg_val: &v) == -1) |
186 | return -1; |
187 | |
188 | if (enable) |
189 | nv = v | FSP_BIT_EN_REG_CLK; |
190 | else |
191 | nv = v & ~FSP_BIT_EN_REG_CLK; |
192 | |
193 | /* only write if necessary */ |
194 | if (nv != v) |
195 | if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, reg_val: nv) == -1) |
196 | return -1; |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) |
202 | { |
203 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
204 | unsigned char param[3]; |
205 | int rc = -1; |
206 | |
207 | psmouse_deactivate(psmouse); |
208 | |
209 | ps2_begin_command(ps2dev); |
210 | |
211 | if (ps2_sendbyte(ps2dev, byte: 0xf3, FSP_CMD_TIMEOUT) < 0) |
212 | goto out; |
213 | |
214 | ps2_sendbyte(ps2dev, byte: 0x66, FSP_CMD_TIMEOUT2); |
215 | ps2_sendbyte(ps2dev, byte: 0x88, FSP_CMD_TIMEOUT2); |
216 | |
217 | if (ps2_sendbyte(ps2dev, byte: 0xf3, FSP_CMD_TIMEOUT) < 0) |
218 | goto out; |
219 | |
220 | ps2_sendbyte(ps2dev, byte: 0x83, FSP_CMD_TIMEOUT2); |
221 | ps2_sendbyte(ps2dev, byte: 0x88, FSP_CMD_TIMEOUT2); |
222 | |
223 | /* get the returned result */ |
224 | if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) |
225 | goto out; |
226 | |
227 | *reg_val = param[2]; |
228 | rc = 0; |
229 | |
230 | out: |
231 | ps2_end_command(ps2dev); |
232 | psmouse_activate(psmouse); |
233 | psmouse_dbg(psmouse, |
234 | "READ PAGE REG: 0x%02x (rc = %d)\n" , |
235 | *reg_val, rc); |
236 | return rc; |
237 | } |
238 | |
239 | static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) |
240 | { |
241 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
242 | unsigned char v; |
243 | int rc = -1; |
244 | |
245 | ps2_begin_command(ps2dev); |
246 | |
247 | if (ps2_sendbyte(ps2dev, byte: 0xf3, FSP_CMD_TIMEOUT) < 0) |
248 | goto out; |
249 | |
250 | ps2_sendbyte(ps2dev, byte: 0x38, FSP_CMD_TIMEOUT2); |
251 | ps2_sendbyte(ps2dev, byte: 0x88, FSP_CMD_TIMEOUT2); |
252 | |
253 | if (ps2_sendbyte(ps2dev, byte: 0xf3, FSP_CMD_TIMEOUT) < 0) |
254 | goto out; |
255 | |
256 | if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { |
257 | ps2_sendbyte(ps2dev, byte: 0x47, FSP_CMD_TIMEOUT2); |
258 | } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { |
259 | /* swapping is required */ |
260 | ps2_sendbyte(ps2dev, byte: 0x44, FSP_CMD_TIMEOUT2); |
261 | } else { |
262 | /* swapping isn't necessary */ |
263 | ps2_sendbyte(ps2dev, byte: 0x33, FSP_CMD_TIMEOUT2); |
264 | } |
265 | |
266 | ps2_sendbyte(ps2dev, byte: v, FSP_CMD_TIMEOUT2); |
267 | rc = 0; |
268 | |
269 | out: |
270 | ps2_end_command(ps2dev); |
271 | psmouse_dbg(psmouse, |
272 | "WRITE PAGE REG: to 0x%02x (rc = %d)\n" , |
273 | reg_val, rc); |
274 | return rc; |
275 | } |
276 | |
277 | static int fsp_get_version(struct psmouse *psmouse, int *version) |
278 | { |
279 | if (fsp_reg_read(psmouse, FSP_REG_VERSION, reg_val: version)) |
280 | return -EIO; |
281 | |
282 | return 0; |
283 | } |
284 | |
285 | static int fsp_get_revision(struct psmouse *psmouse, int *rev) |
286 | { |
287 | if (fsp_reg_read(psmouse, FSP_REG_REVISION, reg_val: rev)) |
288 | return -EIO; |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | static int fsp_get_sn(struct psmouse *psmouse, int *sn) |
294 | { |
295 | int v0, v1, v2; |
296 | int rc = -EIO; |
297 | |
298 | /* production number since Cx is available at: 0x0b40 ~ 0x0b42 */ |
299 | if (fsp_page_reg_write(psmouse, FSP_PAGE_0B)) |
300 | goto out; |
301 | if (fsp_reg_read(psmouse, FSP_REG_SN0, reg_val: &v0)) |
302 | goto out; |
303 | if (fsp_reg_read(psmouse, FSP_REG_SN1, reg_val: &v1)) |
304 | goto out; |
305 | if (fsp_reg_read(psmouse, FSP_REG_SN2, reg_val: &v2)) |
306 | goto out; |
307 | *sn = (v0 << 16) | (v1 << 8) | v2; |
308 | rc = 0; |
309 | out: |
310 | fsp_page_reg_write(psmouse, FSP_PAGE_DEFAULT); |
311 | return rc; |
312 | } |
313 | |
314 | static int fsp_get_buttons(struct psmouse *psmouse, int *btn) |
315 | { |
316 | static const int buttons[] = { |
317 | 0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */ |
318 | 0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */ |
319 | 0x04, /* Left/Middle/Right & Scroll Up/Down */ |
320 | 0x02, /* Left/Middle/Right */ |
321 | }; |
322 | int val; |
323 | |
324 | if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS, reg_val: &val) == -1) |
325 | return -EIO; |
326 | |
327 | *btn = buttons[(val & 0x30) >> 4]; |
328 | return 0; |
329 | } |
330 | |
331 | /* Enable on-pad command tag output */ |
332 | static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) |
333 | { |
334 | int v, nv; |
335 | int res = 0; |
336 | |
337 | if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, reg_val: &v) == -1) { |
338 | psmouse_err(psmouse, "Unable get OPC state.\n" ); |
339 | return -EIO; |
340 | } |
341 | |
342 | if (enable) |
343 | nv = v | FSP_BIT_EN_OPC_TAG; |
344 | else |
345 | nv = v & ~FSP_BIT_EN_OPC_TAG; |
346 | |
347 | /* only write if necessary */ |
348 | if (nv != v) { |
349 | fsp_reg_write_enable(psmouse, enable: true); |
350 | res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, reg_val: nv); |
351 | fsp_reg_write_enable(psmouse, enable: false); |
352 | } |
353 | |
354 | if (res != 0) { |
355 | psmouse_err(psmouse, "Unable to enable OPC tag.\n" ); |
356 | res = -EIO; |
357 | } |
358 | |
359 | return res; |
360 | } |
361 | |
362 | static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable) |
363 | { |
364 | struct fsp_data *pad = psmouse->private; |
365 | int val; |
366 | |
367 | if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, reg_val: &val)) |
368 | return -EIO; |
369 | |
370 | pad->vscroll = enable; |
371 | |
372 | if (enable) |
373 | val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE); |
374 | else |
375 | val &= ~FSP_BIT_FIX_VSCR; |
376 | |
377 | if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, reg_val: val)) |
378 | return -EIO; |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable) |
384 | { |
385 | struct fsp_data *pad = psmouse->private; |
386 | int val, v2; |
387 | |
388 | if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, reg_val: &val)) |
389 | return -EIO; |
390 | |
391 | if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, reg_val: &v2)) |
392 | return -EIO; |
393 | |
394 | pad->hscroll = enable; |
395 | |
396 | if (enable) { |
397 | val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE); |
398 | v2 |= FSP_BIT_EN_MSID6; |
399 | } else { |
400 | val &= ~FSP_BIT_FIX_HSCR; |
401 | v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8); |
402 | } |
403 | |
404 | if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, reg_val: val)) |
405 | return -EIO; |
406 | |
407 | /* reconfigure horizontal scrolling packet output */ |
408 | if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, reg_val: v2)) |
409 | return -EIO; |
410 | |
411 | return 0; |
412 | } |
413 | |
414 | /* |
415 | * Write device specific initial parameters. |
416 | * |
417 | * ex: 0xab 0xcd - write oxcd into register 0xab |
418 | */ |
419 | static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, |
420 | const char *buf, size_t count) |
421 | { |
422 | unsigned int reg, val; |
423 | char *rest; |
424 | ssize_t retval; |
425 | |
426 | reg = simple_strtoul(buf, &rest, 16); |
427 | if (rest == buf || *rest != ' ' || reg > 0xff) |
428 | return -EINVAL; |
429 | |
430 | retval = kstrtouint(s: rest + 1, base: 16, res: &val); |
431 | if (retval) |
432 | return retval; |
433 | |
434 | if (val > 0xff) |
435 | return -EINVAL; |
436 | |
437 | if (fsp_reg_write_enable(psmouse, enable: true)) |
438 | return -EIO; |
439 | |
440 | retval = fsp_reg_write(psmouse, reg_addr: reg, reg_val: val) < 0 ? -EIO : count; |
441 | |
442 | fsp_reg_write_enable(psmouse, enable: false); |
443 | |
444 | return retval; |
445 | } |
446 | |
447 | PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg); |
448 | |
449 | static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse, |
450 | void *data, char *buf) |
451 | { |
452 | struct fsp_data *pad = psmouse->private; |
453 | |
454 | return sprintf(buf, fmt: "%02x%02x\n" , pad->last_reg, pad->last_val); |
455 | } |
456 | |
457 | /* |
458 | * Read a register from device. |
459 | * |
460 | * ex: 0xab -- read content from register 0xab |
461 | */ |
462 | static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data, |
463 | const char *buf, size_t count) |
464 | { |
465 | struct fsp_data *pad = psmouse->private; |
466 | unsigned int reg, val; |
467 | int err; |
468 | |
469 | err = kstrtouint(s: buf, base: 16, res: ®); |
470 | if (err) |
471 | return err; |
472 | |
473 | if (reg > 0xff) |
474 | return -EINVAL; |
475 | |
476 | if (fsp_reg_read(psmouse, reg_addr: reg, reg_val: &val)) |
477 | return -EIO; |
478 | |
479 | pad->last_reg = reg; |
480 | pad->last_val = val; |
481 | |
482 | return count; |
483 | } |
484 | |
485 | PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL, |
486 | fsp_attr_show_getreg, fsp_attr_set_getreg); |
487 | |
488 | static ssize_t (struct psmouse *psmouse, |
489 | void *data, char *buf) |
490 | { |
491 | int val = 0; |
492 | |
493 | if (fsp_page_reg_read(psmouse, reg_val: &val)) |
494 | return -EIO; |
495 | |
496 | return sprintf(buf, fmt: "%02x\n" , val); |
497 | } |
498 | |
499 | static ssize_t (struct psmouse *psmouse, void *data, |
500 | const char *buf, size_t count) |
501 | { |
502 | unsigned int val; |
503 | int err; |
504 | |
505 | err = kstrtouint(s: buf, base: 16, res: &val); |
506 | if (err) |
507 | return err; |
508 | |
509 | if (val > 0xff) |
510 | return -EINVAL; |
511 | |
512 | if (fsp_page_reg_write(psmouse, reg_val: val)) |
513 | return -EIO; |
514 | |
515 | return count; |
516 | } |
517 | |
518 | PSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL, |
519 | fsp_attr_show_pagereg, fsp_attr_set_pagereg); |
520 | |
521 | static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse, |
522 | void *data, char *buf) |
523 | { |
524 | struct fsp_data *pad = psmouse->private; |
525 | |
526 | return sprintf(buf, fmt: "%d\n" , pad->vscroll); |
527 | } |
528 | |
529 | static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data, |
530 | const char *buf, size_t count) |
531 | { |
532 | unsigned int val; |
533 | int err; |
534 | |
535 | err = kstrtouint(s: buf, base: 10, res: &val); |
536 | if (err) |
537 | return err; |
538 | |
539 | if (val > 1) |
540 | return -EINVAL; |
541 | |
542 | fsp_onpad_vscr(psmouse, enable: val); |
543 | |
544 | return count; |
545 | } |
546 | |
547 | PSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL, |
548 | fsp_attr_show_vscroll, fsp_attr_set_vscroll); |
549 | |
550 | static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse, |
551 | void *data, char *buf) |
552 | { |
553 | struct fsp_data *pad = psmouse->private; |
554 | |
555 | return sprintf(buf, fmt: "%d\n" , pad->hscroll); |
556 | } |
557 | |
558 | static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data, |
559 | const char *buf, size_t count) |
560 | { |
561 | unsigned int val; |
562 | int err; |
563 | |
564 | err = kstrtouint(s: buf, base: 10, res: &val); |
565 | if (err) |
566 | return err; |
567 | |
568 | if (val > 1) |
569 | return -EINVAL; |
570 | |
571 | fsp_onpad_hscr(psmouse, enable: val); |
572 | |
573 | return count; |
574 | } |
575 | |
576 | PSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL, |
577 | fsp_attr_show_hscroll, fsp_attr_set_hscroll); |
578 | |
579 | static ssize_t fsp_attr_show_flags(struct psmouse *psmouse, |
580 | void *data, char *buf) |
581 | { |
582 | struct fsp_data *pad = psmouse->private; |
583 | |
584 | return sprintf(buf, fmt: "%c\n" , |
585 | pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c'); |
586 | } |
587 | |
588 | static ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data, |
589 | const char *buf, size_t count) |
590 | { |
591 | struct fsp_data *pad = psmouse->private; |
592 | size_t i; |
593 | |
594 | for (i = 0; i < count; i++) { |
595 | switch (buf[i]) { |
596 | case 'C': |
597 | pad->flags |= FSPDRV_FLAG_EN_OPC; |
598 | break; |
599 | case 'c': |
600 | pad->flags &= ~FSPDRV_FLAG_EN_OPC; |
601 | break; |
602 | default: |
603 | return -EINVAL; |
604 | } |
605 | } |
606 | return count; |
607 | } |
608 | |
609 | PSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL, |
610 | fsp_attr_show_flags, fsp_attr_set_flags); |
611 | |
612 | static ssize_t fsp_attr_show_ver(struct psmouse *psmouse, |
613 | void *data, char *buf) |
614 | { |
615 | return sprintf(buf, fmt: "Sentelic FSP kernel module %s\n" , fsp_drv_ver); |
616 | } |
617 | |
618 | PSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver); |
619 | |
620 | static struct attribute *fsp_attributes[] = { |
621 | &psmouse_attr_setreg.dattr.attr, |
622 | &psmouse_attr_getreg.dattr.attr, |
623 | &psmouse_attr_page.dattr.attr, |
624 | &psmouse_attr_vscroll.dattr.attr, |
625 | &psmouse_attr_hscroll.dattr.attr, |
626 | &psmouse_attr_flags.dattr.attr, |
627 | &psmouse_attr_ver.dattr.attr, |
628 | NULL |
629 | }; |
630 | |
631 | static struct attribute_group fsp_attribute_group = { |
632 | .attrs = fsp_attributes, |
633 | }; |
634 | |
635 | #ifdef FSP_DEBUG |
636 | static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[]) |
637 | { |
638 | static unsigned int ps2_packet_cnt; |
639 | static unsigned int ps2_last_second; |
640 | unsigned int jiffies_msec; |
641 | const char *packet_type = "UNKNOWN" ; |
642 | unsigned short abs_x = 0, abs_y = 0; |
643 | |
644 | /* Interpret & dump the packet data. */ |
645 | switch (packet[0] >> FSP_PKT_TYPE_SHIFT) { |
646 | case FSP_PKT_TYPE_ABS: |
647 | packet_type = "Absolute" ; |
648 | abs_x = GET_ABS_X(packet); |
649 | abs_y = GET_ABS_Y(packet); |
650 | break; |
651 | case FSP_PKT_TYPE_NORMAL: |
652 | packet_type = "Normal" ; |
653 | break; |
654 | case FSP_PKT_TYPE_NOTIFY: |
655 | packet_type = "Notify" ; |
656 | break; |
657 | case FSP_PKT_TYPE_NORMAL_OPC: |
658 | packet_type = "Normal-OPC" ; |
659 | break; |
660 | } |
661 | |
662 | ps2_packet_cnt++; |
663 | jiffies_msec = jiffies_to_msecs(jiffies); |
664 | psmouse_dbg(psmouse, |
665 | "%08dms %s packets: %02x, %02x, %02x, %02x; " |
666 | "abs_x: %d, abs_y: %d\n" , |
667 | jiffies_msec, packet_type, |
668 | packet[0], packet[1], packet[2], packet[3], abs_x, abs_y); |
669 | |
670 | if (jiffies_msec - ps2_last_second > 1000) { |
671 | psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n" , ps2_packet_cnt); |
672 | ps2_packet_cnt = 0; |
673 | ps2_last_second = jiffies_msec; |
674 | } |
675 | } |
676 | #else |
677 | static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[]) |
678 | { |
679 | } |
680 | #endif |
681 | |
682 | static void fsp_set_slot(struct input_dev *dev, int slot, bool active, |
683 | unsigned int x, unsigned int y) |
684 | { |
685 | input_mt_slot(dev, slot); |
686 | input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); |
687 | if (active) { |
688 | input_report_abs(dev, ABS_MT_POSITION_X, value: x); |
689 | input_report_abs(dev, ABS_MT_POSITION_Y, value: y); |
690 | } |
691 | } |
692 | |
693 | static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) |
694 | { |
695 | struct input_dev *dev = psmouse->dev; |
696 | struct fsp_data *ad = psmouse->private; |
697 | unsigned char *packet = psmouse->packet; |
698 | unsigned char button_status = 0, lscroll = 0, rscroll = 0; |
699 | unsigned short abs_x, abs_y, fgrs = 0; |
700 | |
701 | if (psmouse->pktcnt < 4) |
702 | return PSMOUSE_GOOD_DATA; |
703 | |
704 | /* |
705 | * Full packet accumulated, process it |
706 | */ |
707 | |
708 | fsp_packet_debug(psmouse, packet); |
709 | |
710 | switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) { |
711 | case FSP_PKT_TYPE_ABS: |
712 | |
713 | if ((packet[0] == 0x48 || packet[0] == 0x49) && |
714 | packet[1] == 0 && packet[2] == 0) { |
715 | /* |
716 | * Ignore coordinate noise when finger leaving the |
717 | * surface, otherwise cursor may jump to upper-left |
718 | * corner. |
719 | */ |
720 | packet[3] &= 0xf0; |
721 | } |
722 | |
723 | abs_x = GET_ABS_X(packet); |
724 | abs_y = GET_ABS_Y(packet); |
725 | |
726 | if (packet[0] & FSP_PB0_MFMC) { |
727 | /* |
728 | * MFMC packet: assume that there are two fingers on |
729 | * pad |
730 | */ |
731 | fgrs = 2; |
732 | |
733 | /* MFMC packet */ |
734 | if (packet[0] & FSP_PB0_MFMC_FGR2) { |
735 | /* 2nd finger */ |
736 | if (ad->last_mt_fgr == 2) { |
737 | /* |
738 | * workaround for buggy firmware |
739 | * which doesn't clear MFMC bit if |
740 | * the 1st finger is up |
741 | */ |
742 | fgrs = 1; |
743 | fsp_set_slot(dev, slot: 0, active: false, x: 0, y: 0); |
744 | } |
745 | ad->last_mt_fgr = 2; |
746 | |
747 | fsp_set_slot(dev, slot: 1, active: fgrs == 2, x: abs_x, y: abs_y); |
748 | } else { |
749 | /* 1st finger */ |
750 | if (ad->last_mt_fgr == 1) { |
751 | /* |
752 | * workaround for buggy firmware |
753 | * which doesn't clear MFMC bit if |
754 | * the 2nd finger is up |
755 | */ |
756 | fgrs = 1; |
757 | fsp_set_slot(dev, slot: 1, active: false, x: 0, y: 0); |
758 | } |
759 | ad->last_mt_fgr = 1; |
760 | fsp_set_slot(dev, slot: 0, active: fgrs != 0, x: abs_x, y: abs_y); |
761 | } |
762 | } else { |
763 | /* SFAC packet */ |
764 | if ((packet[0] & (FSP_PB0_LBTN|FSP_PB0_PHY_BTN)) == |
765 | FSP_PB0_LBTN) { |
766 | /* On-pad click in SFAC mode should be handled |
767 | * by userspace. On-pad clicks in MFMC mode |
768 | * are real clickpad clicks, and not ignored. |
769 | */ |
770 | packet[0] &= ~FSP_PB0_LBTN; |
771 | } |
772 | |
773 | /* no multi-finger information */ |
774 | ad->last_mt_fgr = 0; |
775 | |
776 | if (abs_x != 0 && abs_y != 0) |
777 | fgrs = 1; |
778 | |
779 | fsp_set_slot(dev, slot: 0, active: fgrs > 0, x: abs_x, y: abs_y); |
780 | fsp_set_slot(dev, slot: 1, active: false, x: 0, y: 0); |
781 | } |
782 | if (fgrs == 1 || (fgrs == 2 && !(packet[0] & FSP_PB0_MFMC_FGR2))) { |
783 | input_report_abs(dev, ABS_X, value: abs_x); |
784 | input_report_abs(dev, ABS_Y, value: abs_y); |
785 | } |
786 | input_report_key(dev, BTN_LEFT, value: packet[0] & 0x01); |
787 | input_report_key(dev, BTN_RIGHT, value: packet[0] & 0x02); |
788 | input_report_key(dev, BTN_TOUCH, value: fgrs); |
789 | input_report_key(dev, BTN_TOOL_FINGER, value: fgrs == 1); |
790 | input_report_key(dev, BTN_TOOL_DOUBLETAP, value: fgrs == 2); |
791 | break; |
792 | |
793 | case FSP_PKT_TYPE_NORMAL_OPC: |
794 | /* on-pad click, filter it if necessary */ |
795 | if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC) |
796 | packet[0] &= ~FSP_PB0_LBTN; |
797 | fallthrough; |
798 | |
799 | case FSP_PKT_TYPE_NORMAL: |
800 | /* normal packet */ |
801 | /* special packet data translation from on-pad packets */ |
802 | if (packet[3] != 0) { |
803 | if (packet[3] & BIT(0)) |
804 | button_status |= 0x01; /* wheel down */ |
805 | if (packet[3] & BIT(1)) |
806 | button_status |= 0x0f; /* wheel up */ |
807 | if (packet[3] & BIT(2)) |
808 | button_status |= BIT(4);/* horizontal left */ |
809 | if (packet[3] & BIT(3)) |
810 | button_status |= BIT(5);/* horizontal right */ |
811 | /* push back to packet queue */ |
812 | if (button_status != 0) |
813 | packet[3] = button_status; |
814 | rscroll = (packet[3] >> 4) & 1; |
815 | lscroll = (packet[3] >> 5) & 1; |
816 | } |
817 | /* |
818 | * Processing wheel up/down and extra button events |
819 | */ |
820 | input_report_rel(dev, REL_WHEEL, |
821 | value: (int)(packet[3] & 8) - (int)(packet[3] & 7)); |
822 | input_report_rel(dev, REL_HWHEEL, value: lscroll - rscroll); |
823 | input_report_key(dev, BTN_BACK, value: lscroll); |
824 | input_report_key(dev, BTN_FORWARD, value: rscroll); |
825 | |
826 | /* |
827 | * Standard PS/2 Mouse |
828 | */ |
829 | psmouse_report_standard_packet(dev, packet); |
830 | break; |
831 | } |
832 | |
833 | input_sync(dev); |
834 | |
835 | return PSMOUSE_FULL_PACKET; |
836 | } |
837 | |
838 | static int fsp_activate_protocol(struct psmouse *psmouse) |
839 | { |
840 | struct fsp_data *pad = psmouse->private; |
841 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
842 | unsigned char param[2]; |
843 | int val; |
844 | |
845 | /* |
846 | * Standard procedure to enter FSP Intellimouse mode |
847 | * (scrolling wheel, 4th and 5th buttons) |
848 | */ |
849 | param[0] = 200; |
850 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); |
851 | param[0] = 200; |
852 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); |
853 | param[0] = 80; |
854 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); |
855 | |
856 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); |
857 | if (param[0] != 0x04) { |
858 | psmouse_err(psmouse, |
859 | "Unable to enable 4 bytes packet format.\n" ); |
860 | return -EIO; |
861 | } |
862 | |
863 | if (pad->ver < FSP_VER_STL3888_C0) { |
864 | /* Preparing relative coordinates output for older hardware */ |
865 | if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, reg_val: &val)) { |
866 | psmouse_err(psmouse, |
867 | "Unable to read SYSCTL5 register.\n" ); |
868 | return -EIO; |
869 | } |
870 | |
871 | if (fsp_get_buttons(psmouse, btn: &pad->buttons)) { |
872 | psmouse_err(psmouse, |
873 | "Unable to retrieve number of buttons.\n" ); |
874 | return -EIO; |
875 | } |
876 | |
877 | val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); |
878 | /* Ensure we are not in absolute mode */ |
879 | val &= ~FSP_BIT_EN_PKT_G0; |
880 | if (pad->buttons == 0x06) { |
881 | /* Left/Middle/Right & Scroll Up/Down/Right/Left */ |
882 | val |= FSP_BIT_EN_MSID6; |
883 | } |
884 | |
885 | if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, reg_val: val)) { |
886 | psmouse_err(psmouse, |
887 | "Unable to set up required mode bits.\n" ); |
888 | return -EIO; |
889 | } |
890 | |
891 | /* |
892 | * Enable OPC tags such that driver can tell the difference |
893 | * between on-pad and real button click |
894 | */ |
895 | if (fsp_opc_tag_enable(psmouse, enable: true)) |
896 | psmouse_warn(psmouse, |
897 | "Failed to enable OPC tag mode.\n" ); |
898 | /* enable on-pad click by default */ |
899 | pad->flags |= FSPDRV_FLAG_EN_OPC; |
900 | |
901 | /* Enable on-pad vertical and horizontal scrolling */ |
902 | fsp_onpad_vscr(psmouse, enable: true); |
903 | fsp_onpad_hscr(psmouse, enable: true); |
904 | } else { |
905 | /* Enable absolute coordinates output for Cx/Dx hardware */ |
906 | if (fsp_reg_write(psmouse, FSP_REG_SWC1, |
907 | FSP_BIT_SWC1_EN_ABS_1F | |
908 | FSP_BIT_SWC1_EN_ABS_2F | |
909 | FSP_BIT_SWC1_EN_FUP_OUT | |
910 | FSP_BIT_SWC1_EN_ABS_CON)) { |
911 | psmouse_err(psmouse, |
912 | "Unable to enable absolute coordinates output.\n" ); |
913 | return -EIO; |
914 | } |
915 | } |
916 | |
917 | return 0; |
918 | } |
919 | |
920 | static int fsp_set_input_params(struct psmouse *psmouse) |
921 | { |
922 | struct input_dev *dev = psmouse->dev; |
923 | struct fsp_data *pad = psmouse->private; |
924 | |
925 | if (pad->ver < FSP_VER_STL3888_C0) { |
926 | __set_bit(BTN_MIDDLE, dev->keybit); |
927 | __set_bit(BTN_BACK, dev->keybit); |
928 | __set_bit(BTN_FORWARD, dev->keybit); |
929 | __set_bit(REL_WHEEL, dev->relbit); |
930 | __set_bit(REL_HWHEEL, dev->relbit); |
931 | } else { |
932 | /* |
933 | * Hardware prior to Cx performs much better in relative mode; |
934 | * hence, only enable absolute coordinates output as well as |
935 | * multi-touch output for the newer hardware. |
936 | * |
937 | * Maximum coordinates can be computed as: |
938 | * |
939 | * number of scanlines * 64 - 57 |
940 | * |
941 | * where number of X/Y scanline lines are 16/12. |
942 | */ |
943 | int abs_x = 967, abs_y = 711; |
944 | |
945 | __set_bit(EV_ABS, dev->evbit); |
946 | __clear_bit(EV_REL, dev->evbit); |
947 | __set_bit(BTN_TOUCH, dev->keybit); |
948 | __set_bit(BTN_TOOL_FINGER, dev->keybit); |
949 | __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); |
950 | __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); |
951 | |
952 | input_set_abs_params(dev, ABS_X, min: 0, max: abs_x, fuzz: 0, flat: 0); |
953 | input_set_abs_params(dev, ABS_Y, min: 0, max: abs_y, fuzz: 0, flat: 0); |
954 | input_mt_init_slots(dev, num_slots: 2, flags: 0); |
955 | input_set_abs_params(dev, ABS_MT_POSITION_X, min: 0, max: abs_x, fuzz: 0, flat: 0); |
956 | input_set_abs_params(dev, ABS_MT_POSITION_Y, min: 0, max: abs_y, fuzz: 0, flat: 0); |
957 | } |
958 | |
959 | return 0; |
960 | } |
961 | |
962 | int fsp_detect(struct psmouse *psmouse, bool set_properties) |
963 | { |
964 | int id; |
965 | |
966 | if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, reg_val: &id)) |
967 | return -EIO; |
968 | |
969 | if (id != 0x01) |
970 | return -ENODEV; |
971 | |
972 | if (set_properties) { |
973 | psmouse->vendor = "Sentelic" ; |
974 | psmouse->name = "FingerSensingPad" ; |
975 | } |
976 | |
977 | return 0; |
978 | } |
979 | |
980 | static void fsp_reset(struct psmouse *psmouse) |
981 | { |
982 | fsp_opc_tag_enable(psmouse, enable: false); |
983 | fsp_onpad_vscr(psmouse, enable: false); |
984 | fsp_onpad_hscr(psmouse, enable: false); |
985 | } |
986 | |
987 | static void fsp_disconnect(struct psmouse *psmouse) |
988 | { |
989 | sysfs_remove_group(kobj: &psmouse->ps2dev.serio->dev.kobj, |
990 | grp: &fsp_attribute_group); |
991 | |
992 | fsp_reset(psmouse); |
993 | kfree(objp: psmouse->private); |
994 | } |
995 | |
996 | static int fsp_reconnect(struct psmouse *psmouse) |
997 | { |
998 | int version; |
999 | |
1000 | if (fsp_detect(psmouse, set_properties: 0)) |
1001 | return -ENODEV; |
1002 | |
1003 | if (fsp_get_version(psmouse, version: &version)) |
1004 | return -ENODEV; |
1005 | |
1006 | if (fsp_activate_protocol(psmouse)) |
1007 | return -EIO; |
1008 | |
1009 | return 0; |
1010 | } |
1011 | |
1012 | int fsp_init(struct psmouse *psmouse) |
1013 | { |
1014 | struct fsp_data *priv; |
1015 | int ver, rev, sn = 0; |
1016 | int error; |
1017 | |
1018 | if (fsp_get_version(psmouse, version: &ver) || |
1019 | fsp_get_revision(psmouse, rev: &rev)) { |
1020 | return -ENODEV; |
1021 | } |
1022 | if (ver >= FSP_VER_STL3888_C0) { |
1023 | /* firmware information is only available since C0 */ |
1024 | fsp_get_sn(psmouse, sn: &sn); |
1025 | } |
1026 | |
1027 | psmouse_info(psmouse, |
1028 | "Finger Sensing Pad, hw: %d.%d.%d, sn: %x, sw: %s\n" , |
1029 | ver >> 4, ver & 0x0F, rev, sn, fsp_drv_ver); |
1030 | |
1031 | psmouse->private = priv = kzalloc(size: sizeof(struct fsp_data), GFP_KERNEL); |
1032 | if (!priv) |
1033 | return -ENOMEM; |
1034 | |
1035 | priv->ver = ver; |
1036 | priv->rev = rev; |
1037 | |
1038 | psmouse->protocol_handler = fsp_process_byte; |
1039 | psmouse->disconnect = fsp_disconnect; |
1040 | psmouse->reconnect = fsp_reconnect; |
1041 | psmouse->cleanup = fsp_reset; |
1042 | psmouse->pktsize = 4; |
1043 | |
1044 | error = fsp_activate_protocol(psmouse); |
1045 | if (error) |
1046 | goto err_out; |
1047 | |
1048 | /* Set up various supported input event bits */ |
1049 | error = fsp_set_input_params(psmouse); |
1050 | if (error) |
1051 | goto err_out; |
1052 | |
1053 | error = sysfs_create_group(kobj: &psmouse->ps2dev.serio->dev.kobj, |
1054 | grp: &fsp_attribute_group); |
1055 | if (error) { |
1056 | psmouse_err(psmouse, |
1057 | "Failed to create sysfs attributes (%d)" , error); |
1058 | goto err_out; |
1059 | } |
1060 | |
1061 | return 0; |
1062 | |
1063 | err_out: |
1064 | kfree(objp: psmouse->private); |
1065 | psmouse->private = NULL; |
1066 | return error; |
1067 | } |
1068 | |