File: | dev/pckbc/pms.c |
Warning: | line 1727, column 15 The left operand of '&' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: pms.c,v 1.98 2023/08/16 20:53:47 bru Exp $ */ | |||
2 | /* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */ | |||
3 | ||||
4 | /*- | |||
5 | * Copyright (c) 1994 Charles M. Hannum. | |||
6 | * Copyright (c) 1992, 1993 Erik Forsberg. | |||
7 | * All rights reserved. | |||
8 | * | |||
9 | * Redistribution and use in source and binary forms, with or without | |||
10 | * modification, are permitted provided that the following conditions | |||
11 | * are met: | |||
12 | * 1. Redistributions of source code must retain the above copyright | |||
13 | * notice, this list of conditions and the following disclaimer. | |||
14 | * | |||
15 | * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED | |||
16 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |||
17 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |||
18 | * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
22 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
25 | */ | |||
26 | ||||
27 | #include <sys/param.h> | |||
28 | #include <sys/systm.h> | |||
29 | #include <sys/rwlock.h> | |||
30 | #include <sys/device.h> | |||
31 | #include <sys/ioctl.h> | |||
32 | #include <sys/malloc.h> | |||
33 | #include <sys/task.h> | |||
34 | #include <sys/timeout.h> | |||
35 | ||||
36 | #include <machine/bus.h> | |||
37 | ||||
38 | #include <dev/ic/pckbcvar.h> | |||
39 | ||||
40 | #include <dev/pckbc/pmsreg.h> | |||
41 | ||||
42 | #include <dev/wscons/wsconsio.h> | |||
43 | #include <dev/wscons/wsmousevar.h> | |||
44 | ||||
45 | #if defined(__i386__) || defined(__amd64__1) | |||
46 | #include "acpi.h" | |||
47 | #endif | |||
48 | ||||
49 | #if !defined(SMALL_KERNEL) && NACPI1 > 0 | |||
50 | extern int mouse_has_softbtn; | |||
51 | #else | |||
52 | int mouse_has_softbtn; | |||
53 | #endif | |||
54 | ||||
55 | #ifdef DEBUG | |||
56 | #define DPRINTF(x...) do { printf(x); } while (0); | |||
57 | #else | |||
58 | #define DPRINTF(x...) | |||
59 | #endif | |||
60 | ||||
61 | #define DEVNAME(sc)((sc)->sc_dev.dv_xname) ((sc)->sc_dev.dv_xname) | |||
62 | ||||
63 | #define WSMOUSE_BUTTON(x)(1 << ((x) - 1)) (1 << ((x) - 1)) | |||
64 | ||||
65 | struct pms_softc; | |||
66 | ||||
67 | struct pms_protocol { | |||
68 | int type; | |||
69 | #define PMS_STANDARD0 0 | |||
70 | #define PMS_INTELLI1 1 | |||
71 | #define PMS_SYNAPTICS2 2 | |||
72 | #define PMS_ALPS3 3 | |||
73 | #define PMS_ELANTECH_V14 4 | |||
74 | #define PMS_ELANTECH_V25 5 | |||
75 | #define PMS_ELANTECH_V36 6 | |||
76 | #define PMS_ELANTECH_V47 7 | |||
77 | u_int packetsize; | |||
78 | int (*enable)(struct pms_softc *); | |||
79 | int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *); | |||
80 | int (*sync)(struct pms_softc *, int); | |||
81 | void (*proc)(struct pms_softc *); | |||
82 | void (*disable)(struct pms_softc *); | |||
83 | }; | |||
84 | ||||
85 | struct synaptics_softc { | |||
86 | int identify; | |||
87 | int capabilities, ext_capabilities, ext2_capabilities; | |||
88 | int model, ext_model; | |||
89 | int modes; | |||
90 | ||||
91 | int mode; | |||
92 | ||||
93 | int mask; | |||
94 | #define SYNAPTICS_MASK_NEWABS_STRICT0xc8 0xc8 | |||
95 | #define SYNAPTICS_MASK_NEWABS_RELAXED0xc0 0xc0 | |||
96 | #define SYNAPTICS_VALID_NEWABS_FIRST0x80 0x80 | |||
97 | #define SYNAPTICS_VALID_NEWABS_NEXT0xc0 0xc0 | |||
98 | ||||
99 | u_int sec_buttons; | |||
100 | ||||
101 | #define SYNAPTICS_PRESSURE_HI30 30 | |||
102 | #define SYNAPTICS_PRESSURE_LO25 25 | |||
103 | #define SYNAPTICS_PRESSURE30 SYNAPTICS_PRESSURE_HI30 | |||
104 | #define SYNAPTICS_SCALE4 4 | |||
105 | #define SYNAPTICS_MAX_FINGERS3 3 | |||
106 | }; | |||
107 | ||||
108 | struct alps_softc { | |||
109 | int model; | |||
110 | #define ALPS_GLIDEPOINT(1 << 1) (1 << 1) | |||
111 | #define ALPS_DUALPOINT(1 << 2) (1 << 2) | |||
112 | #define ALPS_PASSTHROUGH(1 << 3) (1 << 3) | |||
113 | #define ALPS_INTERLEAVED(1 << 4) (1 << 4) | |||
114 | ||||
115 | int mask; | |||
116 | int version; | |||
117 | ||||
118 | u_int gesture; | |||
119 | ||||
120 | u_int sec_buttons; /* trackpoint */ | |||
121 | ||||
122 | int old_x, old_y; | |||
123 | #define ALPS_PRESSURE40 40 | |||
124 | }; | |||
125 | ||||
126 | struct elantech_softc { | |||
127 | int flags; | |||
128 | #define ELANTECH_F_REPORTS_PRESSURE0x01 0x01 | |||
129 | #define ELANTECH_F_HAS_ROCKER0x02 0x02 | |||
130 | #define ELANTECH_F_2FINGER_PACKET0x04 0x04 | |||
131 | #define ELANTECH_F_HW_V1_OLD0x08 0x08 | |||
132 | #define ELANTECH_F_CRC_ENABLED0x10 0x10 | |||
133 | #define ELANTECH_F_TRACKPOINT0x20 0x20 | |||
134 | int fw_version; | |||
135 | ||||
136 | u_int mt_slots; | |||
137 | ||||
138 | int width; | |||
139 | ||||
140 | u_char parity[256]; | |||
141 | u_char p1, p2, p3; | |||
142 | ||||
143 | int max_x, max_y; | |||
144 | int old_x, old_y; | |||
145 | int initial_pkt; | |||
146 | }; | |||
147 | #define ELANTECH_IS_CLICKPAD(sc)(((sc)->elantech->fw_version & 0x1000) != 0) (((sc)->elantech->fw_version & 0x1000) != 0) | |||
148 | ||||
149 | struct pms_softc { /* driver status information */ | |||
150 | struct device sc_dev; | |||
151 | ||||
152 | pckbc_tag_t sc_kbctag; | |||
153 | ||||
154 | int sc_state; | |||
155 | #define PMS_STATE_DISABLED0 0 | |||
156 | #define PMS_STATE_ENABLED1 1 | |||
157 | #define PMS_STATE_SUSPENDED2 2 | |||
158 | ||||
159 | struct rwlock sc_state_lock; | |||
160 | ||||
161 | int sc_dev_enable; | |||
162 | #define PMS_DEV_IGNORE0x00 0x00 | |||
163 | #define PMS_DEV_PRIMARY0x01 0x01 | |||
164 | #define PMS_DEV_SECONDARY0x02 0x02 | |||
165 | ||||
166 | struct task sc_rsttask; | |||
167 | struct timeout sc_rsttimo; | |||
168 | int sc_rststate; | |||
169 | #define PMS_RST_COMMENCE0x01 0x01 | |||
170 | #define PMS_RST_ANNOUNCED0x02 0x02 | |||
171 | ||||
172 | int poll; | |||
173 | int inputstate; | |||
174 | ||||
175 | const struct pms_protocol *protocol; | |||
176 | struct synaptics_softc *synaptics; | |||
177 | struct alps_softc *alps; | |||
178 | struct elantech_softc *elantech; | |||
179 | ||||
180 | u_char packet[8]; | |||
181 | ||||
182 | struct device *sc_wsmousedev; | |||
183 | struct device *sc_sec_wsmousedev; | |||
184 | }; | |||
185 | ||||
186 | static const u_int butmap[8] = { | |||
187 | 0, | |||
188 | WSMOUSE_BUTTON(1)(1 << ((1) - 1)), | |||
189 | WSMOUSE_BUTTON(3)(1 << ((3) - 1)), | |||
190 | WSMOUSE_BUTTON(1)(1 << ((1) - 1)) | WSMOUSE_BUTTON(3)(1 << ((3) - 1)), | |||
191 | WSMOUSE_BUTTON(2)(1 << ((2) - 1)), | |||
192 | WSMOUSE_BUTTON(1)(1 << ((1) - 1)) | WSMOUSE_BUTTON(2)(1 << ((2) - 1)), | |||
193 | WSMOUSE_BUTTON(2)(1 << ((2) - 1)) | WSMOUSE_BUTTON(3)(1 << ((3) - 1)), | |||
194 | WSMOUSE_BUTTON(1)(1 << ((1) - 1)) | WSMOUSE_BUTTON(2)(1 << ((2) - 1)) | WSMOUSE_BUTTON(3)(1 << ((3) - 1)) | |||
195 | }; | |||
196 | ||||
197 | static const struct alps_model { | |||
198 | int version; | |||
199 | int mask; | |||
200 | int model; | |||
201 | } alps_models[] = { | |||
202 | { 0x2021, 0xf8, ALPS_DUALPOINT(1 << 2) | ALPS_PASSTHROUGH(1 << 3) }, | |||
203 | { 0x2221, 0xf8, ALPS_DUALPOINT(1 << 2) | ALPS_PASSTHROUGH(1 << 3) }, | |||
204 | { 0x2222, 0xff, ALPS_DUALPOINT(1 << 2) | ALPS_PASSTHROUGH(1 << 3) }, | |||
205 | { 0x3222, 0xf8, ALPS_DUALPOINT(1 << 2) | ALPS_PASSTHROUGH(1 << 3) }, | |||
206 | { 0x5212, 0xff, ALPS_DUALPOINT(1 << 2) | ALPS_PASSTHROUGH(1 << 3) | ALPS_INTERLEAVED(1 << 4) }, | |||
207 | { 0x5321, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
208 | { 0x5322, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
209 | { 0x603b, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
210 | { 0x6222, 0xcf, ALPS_DUALPOINT(1 << 2) | ALPS_PASSTHROUGH(1 << 3) | ALPS_INTERLEAVED(1 << 4) }, | |||
211 | { 0x6321, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
212 | { 0x6322, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
213 | { 0x6323, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
214 | { 0x6324, 0x8f, ALPS_GLIDEPOINT(1 << 1) }, | |||
215 | { 0x6325, 0xef, ALPS_GLIDEPOINT(1 << 1) }, | |||
216 | { 0x6326, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
217 | { 0x7301, 0xf8, ALPS_DUALPOINT(1 << 2) }, | |||
218 | { 0x7321, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
219 | { 0x7322, 0xf8, ALPS_GLIDEPOINT(1 << 1) }, | |||
220 | { 0x7325, 0xcf, ALPS_GLIDEPOINT(1 << 1) }, | |||
221 | #if 0 | |||
222 | /* | |||
223 | * This model has a clitpad sending almost compatible PS2 | |||
224 | * packets but not compatible enough to be used with the | |||
225 | * ALPS protocol. | |||
226 | */ | |||
227 | { 0x633b, 0xf8, ALPS_DUALPOINT(1 << 2) | ALPS_PASSTHROUGH(1 << 3) }, | |||
228 | ||||
229 | { 0x7326, 0, 0 }, /* XXX Uses unknown v3 protocol */ | |||
230 | ||||
231 | { 0x7331, 0x8f, ALPS_DUALPOINT(1 << 2) }, /* not supported */ | |||
232 | #endif | |||
233 | }; | |||
234 | ||||
235 | static struct wsmouse_param synaptics_params[] = { | |||
236 | { WSMOUSECFG_PRESSURE_LO, SYNAPTICS_PRESSURE_LO25 }, | |||
237 | { WSMOUSECFG_PRESSURE_HI, SYNAPTICS_PRESSURE_HI30 } | |||
238 | }; | |||
239 | ||||
240 | static struct wsmouse_param alps_params[] = { | |||
241 | { WSMOUSECFG_SMOOTHING, 3 } | |||
242 | }; | |||
243 | ||||
244 | int pmsprobe(struct device *, void *, void *); | |||
245 | void pmsattach(struct device *, struct device *, void *); | |||
246 | int pmsactivate(struct device *, int); | |||
247 | ||||
248 | void pmsinput(void *, int); | |||
249 | ||||
250 | int pms_change_state(struct pms_softc *, int, int); | |||
251 | ||||
252 | int pms_ioctl(void *, u_long, caddr_t, int, struct proc *); | |||
253 | int pms_enable(void *); | |||
254 | void pms_disable(void *); | |||
255 | ||||
256 | int pms_sec_ioctl(void *, u_long, caddr_t, int, struct proc *); | |||
257 | int pms_sec_enable(void *); | |||
258 | void pms_sec_disable(void *); | |||
259 | ||||
260 | int pms_cmd(struct pms_softc *, u_char *, int, u_char *, int); | |||
261 | int pms_spec_cmd(struct pms_softc *, int); | |||
262 | int pms_get_devid(struct pms_softc *, u_char *); | |||
263 | int pms_get_status(struct pms_softc *, u_char *); | |||
264 | int pms_set_rate(struct pms_softc *, int); | |||
265 | int pms_set_resolution(struct pms_softc *, int); | |||
266 | int pms_set_scaling(struct pms_softc *, int); | |||
267 | int pms_reset(struct pms_softc *); | |||
268 | int pms_dev_enable(struct pms_softc *); | |||
269 | int pms_dev_disable(struct pms_softc *); | |||
270 | void pms_protocol_lookup(struct pms_softc *); | |||
271 | void pms_reset_detect(struct pms_softc *, int); | |||
272 | void pms_reset_task(void *); | |||
273 | void pms_reset_timo(void *); | |||
274 | ||||
275 | int pms_enable_intelli(struct pms_softc *); | |||
276 | ||||
277 | int pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *); | |||
278 | int pms_sync_mouse(struct pms_softc *, int); | |||
279 | void pms_proc_mouse(struct pms_softc *); | |||
280 | ||||
281 | int pms_enable_synaptics(struct pms_softc *); | |||
282 | int pms_ioctl_synaptics(struct pms_softc *, u_long, caddr_t, int, struct proc *); | |||
283 | int pms_sync_synaptics(struct pms_softc *, int); | |||
284 | void pms_proc_synaptics(struct pms_softc *); | |||
285 | void pms_disable_synaptics(struct pms_softc *); | |||
286 | ||||
287 | int pms_enable_alps(struct pms_softc *); | |||
288 | int pms_ioctl_alps(struct pms_softc *, u_long, caddr_t, int, struct proc *); | |||
289 | int pms_sync_alps(struct pms_softc *, int); | |||
290 | void pms_proc_alps(struct pms_softc *); | |||
291 | ||||
292 | int pms_enable_elantech_v1(struct pms_softc *); | |||
293 | int pms_enable_elantech_v2(struct pms_softc *); | |||
294 | int pms_enable_elantech_v3(struct pms_softc *); | |||
295 | int pms_enable_elantech_v4(struct pms_softc *); | |||
296 | int pms_ioctl_elantech(struct pms_softc *, u_long, caddr_t, int, | |||
297 | struct proc *); | |||
298 | int pms_sync_elantech_v1(struct pms_softc *, int); | |||
299 | int pms_sync_elantech_v2(struct pms_softc *, int); | |||
300 | int pms_sync_elantech_v3(struct pms_softc *, int); | |||
301 | int pms_sync_elantech_v4(struct pms_softc *, int); | |||
302 | void pms_proc_elantech_v1(struct pms_softc *); | |||
303 | void pms_proc_elantech_v2(struct pms_softc *); | |||
304 | void pms_proc_elantech_v3(struct pms_softc *); | |||
305 | void pms_proc_elantech_v4(struct pms_softc *); | |||
306 | ||||
307 | int synaptics_knock(struct pms_softc *); | |||
308 | int synaptics_set_mode(struct pms_softc *, int, int); | |||
309 | int synaptics_query(struct pms_softc *, int, int *); | |||
310 | int synaptics_get_hwinfo(struct pms_softc *); | |||
311 | void synaptics_sec_proc(struct pms_softc *); | |||
312 | ||||
313 | int alps_sec_proc(struct pms_softc *); | |||
314 | int alps_get_hwinfo(struct pms_softc *); | |||
315 | ||||
316 | int elantech_knock(struct pms_softc *); | |||
317 | int elantech_get_hwinfo_v1(struct pms_softc *); | |||
318 | int elantech_get_hwinfo_v2(struct pms_softc *); | |||
319 | int elantech_get_hwinfo_v3(struct pms_softc *); | |||
320 | int elantech_get_hwinfo_v4(struct pms_softc *); | |||
321 | int elantech_ps2_cmd(struct pms_softc *, u_char); | |||
322 | int elantech_set_absolute_mode_v1(struct pms_softc *); | |||
323 | int elantech_set_absolute_mode_v2(struct pms_softc *); | |||
324 | int elantech_set_absolute_mode_v3(struct pms_softc *); | |||
325 | int elantech_set_absolute_mode_v4(struct pms_softc *); | |||
326 | ||||
327 | const struct cfattach pms_ca = { | |||
328 | sizeof(struct pms_softc), pmsprobe, pmsattach, NULL((void *)0), | |||
329 | pmsactivate | |||
330 | }; | |||
331 | ||||
332 | struct cfdriver pms_cd = { | |||
333 | NULL((void *)0), "pms", DV_DULL | |||
334 | }; | |||
335 | ||||
336 | const struct wsmouse_accessops pms_accessops = { | |||
337 | pms_enable, | |||
338 | pms_ioctl, | |||
339 | pms_disable, | |||
340 | }; | |||
341 | ||||
342 | const struct wsmouse_accessops pms_sec_accessops = { | |||
343 | pms_sec_enable, | |||
344 | pms_sec_ioctl, | |||
345 | pms_sec_disable, | |||
346 | }; | |||
347 | ||||
348 | const struct pms_protocol pms_protocols[] = { | |||
349 | /* Generic PS/2 mouse */ | |||
350 | { | |||
351 | PMS_STANDARD0, 3, | |||
352 | NULL((void *)0), | |||
353 | pms_ioctl_mouse, | |||
354 | pms_sync_mouse, | |||
355 | pms_proc_mouse, | |||
356 | NULL((void *)0) | |||
357 | }, | |||
358 | /* Synaptics touchpad */ | |||
359 | { | |||
360 | PMS_SYNAPTICS2, 6, | |||
361 | pms_enable_synaptics, | |||
362 | pms_ioctl_synaptics, | |||
363 | pms_sync_synaptics, | |||
364 | pms_proc_synaptics, | |||
365 | pms_disable_synaptics | |||
366 | }, | |||
367 | /* ALPS touchpad */ | |||
368 | { | |||
369 | PMS_ALPS3, 6, | |||
370 | pms_enable_alps, | |||
371 | pms_ioctl_alps, | |||
372 | pms_sync_alps, | |||
373 | pms_proc_alps, | |||
374 | NULL((void *)0) | |||
375 | }, | |||
376 | /* Elantech touchpad (hardware version 1) */ | |||
377 | { | |||
378 | PMS_ELANTECH_V14, 4, | |||
379 | pms_enable_elantech_v1, | |||
380 | pms_ioctl_elantech, | |||
381 | pms_sync_elantech_v1, | |||
382 | pms_proc_elantech_v1, | |||
383 | NULL((void *)0) | |||
384 | }, | |||
385 | /* Elantech touchpad (hardware version 2) */ | |||
386 | { | |||
387 | PMS_ELANTECH_V25, 6, | |||
388 | pms_enable_elantech_v2, | |||
389 | pms_ioctl_elantech, | |||
390 | pms_sync_elantech_v2, | |||
391 | pms_proc_elantech_v2, | |||
392 | NULL((void *)0) | |||
393 | }, | |||
394 | /* Elantech touchpad (hardware version 3) */ | |||
395 | { | |||
396 | PMS_ELANTECH_V36, 6, | |||
397 | pms_enable_elantech_v3, | |||
398 | pms_ioctl_elantech, | |||
399 | pms_sync_elantech_v3, | |||
400 | pms_proc_elantech_v3, | |||
401 | NULL((void *)0) | |||
402 | }, | |||
403 | /* Elantech touchpad (hardware version 4) */ | |||
404 | { | |||
405 | PMS_ELANTECH_V47, 6, | |||
406 | pms_enable_elantech_v4, | |||
407 | pms_ioctl_elantech, | |||
408 | pms_sync_elantech_v4, | |||
409 | pms_proc_elantech_v4, | |||
410 | NULL((void *)0) | |||
411 | }, | |||
412 | /* Microsoft IntelliMouse */ | |||
413 | { | |||
414 | PMS_INTELLI1, 4, | |||
415 | pms_enable_intelli, | |||
416 | pms_ioctl_mouse, | |||
417 | pms_sync_mouse, | |||
418 | pms_proc_mouse, | |||
419 | NULL((void *)0) | |||
420 | }, | |||
421 | }; | |||
422 | ||||
423 | int | |||
424 | pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen) | |||
425 | { | |||
426 | if (sc->poll) { | |||
427 | return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT1, | |||
428 | cmd, len, resplen, resp, 1); | |||
429 | } else { | |||
430 | return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT1, | |||
431 | cmd, len, resplen, 1, resp); | |||
432 | } | |||
433 | } | |||
434 | ||||
435 | int | |||
436 | pms_spec_cmd(struct pms_softc *sc, int cmd) | |||
437 | { | |||
438 | if (pms_set_scaling(sc, 1) || | |||
439 | pms_set_resolution(sc, (cmd >> 6) & 0x03) || | |||
440 | pms_set_resolution(sc, (cmd >> 4) & 0x03) || | |||
441 | pms_set_resolution(sc, (cmd >> 2) & 0x03) || | |||
442 | pms_set_resolution(sc, (cmd >> 0) & 0x03)) | |||
443 | return (-1); | |||
444 | return (0); | |||
445 | } | |||
446 | ||||
447 | int | |||
448 | pms_get_devid(struct pms_softc *sc, u_char *resp) | |||
449 | { | |||
450 | u_char cmd[1]; | |||
451 | ||||
452 | cmd[0] = PMS_SEND_DEV_ID0xf2; | |||
453 | return (pms_cmd(sc, cmd, 1, resp, 1)); | |||
454 | } | |||
455 | ||||
456 | int | |||
457 | pms_get_status(struct pms_softc *sc, u_char *resp) | |||
458 | { | |||
459 | u_char cmd[1]; | |||
460 | ||||
461 | cmd[0] = PMS_SEND_DEV_STATUS0xe9; | |||
462 | return (pms_cmd(sc, cmd, 1, resp, 3)); | |||
463 | } | |||
464 | ||||
465 | int | |||
466 | pms_set_rate(struct pms_softc *sc, int value) | |||
467 | { | |||
468 | u_char cmd[2]; | |||
469 | ||||
470 | cmd[0] = PMS_SET_SAMPLE0xf3; | |||
471 | cmd[1] = value; | |||
472 | return (pms_cmd(sc, cmd, 2, NULL((void *)0), 0)); | |||
473 | } | |||
474 | ||||
475 | int | |||
476 | pms_set_resolution(struct pms_softc *sc, int value) | |||
477 | { | |||
478 | u_char cmd[2]; | |||
479 | ||||
480 | cmd[0] = PMS_SET_RES0xe8; | |||
481 | cmd[1] = value; | |||
482 | return (pms_cmd(sc, cmd, 2, NULL((void *)0), 0)); | |||
483 | } | |||
484 | ||||
485 | int | |||
486 | pms_set_scaling(struct pms_softc *sc, int scale) | |||
487 | { | |||
488 | u_char cmd[1]; | |||
489 | ||||
490 | switch (scale) { | |||
491 | case 1: | |||
492 | default: | |||
493 | cmd[0] = PMS_SET_SCALE110xe6; | |||
494 | break; | |||
495 | case 2: | |||
496 | cmd[0] = PMS_SET_SCALE210xe7; | |||
497 | break; | |||
498 | } | |||
499 | return (pms_cmd(sc, cmd, 1, NULL((void *)0), 0)); | |||
500 | } | |||
501 | ||||
502 | int | |||
503 | pms_reset(struct pms_softc *sc) | |||
504 | { | |||
505 | u_char cmd[1], resp[2]; | |||
506 | int res; | |||
507 | ||||
508 | cmd[0] = PMS_RESET0xff; | |||
509 | res = pms_cmd(sc, cmd, 1, resp, 2); | |||
510 | #ifdef DEBUG | |||
511 | if (res || resp[0] != PMS_RSTDONE0xaa || resp[1] != 0) | |||
512 | printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n", | |||
513 | DEVNAME(sc)((sc)->sc_dev.dv_xname), res, resp[0], resp[1]); | |||
514 | #endif | |||
515 | return (res); | |||
516 | } | |||
517 | ||||
518 | int | |||
519 | pms_dev_enable(struct pms_softc *sc) | |||
520 | { | |||
521 | u_char cmd[1]; | |||
522 | int res; | |||
523 | ||||
524 | cmd[0] = PMS_DEV_ENABLE0xf4; | |||
525 | res = pms_cmd(sc, cmd, 1, NULL((void *)0), 0); | |||
526 | if (res) | |||
527 | printf("%s: enable error\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
528 | return (res); | |||
529 | } | |||
530 | ||||
531 | int | |||
532 | pms_dev_disable(struct pms_softc *sc) | |||
533 | { | |||
534 | u_char cmd[1]; | |||
535 | int res; | |||
536 | ||||
537 | cmd[0] = PMS_DEV_DISABLE0xf5; | |||
538 | res = pms_cmd(sc, cmd, 1, NULL((void *)0), 0); | |||
539 | if (res) | |||
540 | printf("%s: disable error\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
541 | return (res); | |||
542 | } | |||
543 | ||||
544 | void | |||
545 | pms_protocol_lookup(struct pms_softc *sc) | |||
546 | { | |||
547 | int i; | |||
548 | ||||
549 | sc->protocol = &pms_protocols[0]; | |||
550 | for (i = 1; i < nitems(pms_protocols)(sizeof((pms_protocols)) / sizeof((pms_protocols)[0])); i++) { | |||
551 | pms_reset(sc); | |||
552 | if (pms_protocols[i].enable(sc)) { | |||
553 | sc->protocol = &pms_protocols[i]; | |||
554 | break; | |||
555 | } | |||
556 | } | |||
557 | ||||
558 | DPRINTF("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type); | |||
559 | } | |||
560 | ||||
561 | /* | |||
562 | * Detect reset announcement ([0xaa, 0x0]). | |||
563 | * The sequence will be sent as input on rare occasions when the touchpad was | |||
564 | * reset due to a power failure. | |||
565 | */ | |||
566 | void | |||
567 | pms_reset_detect(struct pms_softc *sc, int data) | |||
568 | { | |||
569 | switch (sc->sc_rststate) { | |||
570 | case PMS_RST_COMMENCE0x01: | |||
571 | if (data == 0x0) { | |||
572 | sc->sc_rststate = PMS_RST_ANNOUNCED0x02; | |||
573 | timeout_add_msec(&sc->sc_rsttimo, 100); | |||
574 | } else if (data != PMS_RSTDONE0xaa) { | |||
575 | sc->sc_rststate = 0; | |||
576 | } | |||
577 | break; | |||
578 | default: | |||
579 | if (data == PMS_RSTDONE0xaa) | |||
580 | sc->sc_rststate = PMS_RST_COMMENCE0x01; | |||
581 | else | |||
582 | sc->sc_rststate = 0; | |||
583 | } | |||
584 | } | |||
585 | ||||
586 | void | |||
587 | pms_reset_timo(void *v) | |||
588 | { | |||
589 | struct pms_softc *sc = v; | |||
590 | int s = spltty()splraise(0x9); | |||
591 | ||||
592 | /* | |||
593 | * Do nothing if the reset was a false positive or if the device already | |||
594 | * is disabled. | |||
595 | */ | |||
596 | if (sc->sc_rststate == PMS_RST_ANNOUNCED0x02 && | |||
597 | sc->sc_state != PMS_STATE_DISABLED0) | |||
598 | task_add(systq, &sc->sc_rsttask); | |||
599 | ||||
600 | splx(s)spllower(s); | |||
601 | } | |||
602 | ||||
603 | void | |||
604 | pms_reset_task(void *v) | |||
605 | { | |||
606 | struct pms_softc *sc = v; | |||
607 | int s = spltty()splraise(0x9); | |||
608 | ||||
609 | #ifdef DIAGNOSTIC1 | |||
610 | printf("%s: device reset (state = %d)\n", DEVNAME(sc)((sc)->sc_dev.dv_xname), sc->sc_rststate); | |||
611 | #endif | |||
612 | ||||
613 | rw_enter_write(&sc->sc_state_lock); | |||
614 | ||||
615 | if (sc->sc_sec_wsmousedev != NULL((void *)0)) | |||
616 | pms_change_state(sc, PMS_STATE_DISABLED0, PMS_DEV_SECONDARY0x02); | |||
617 | pms_change_state(sc, PMS_STATE_DISABLED0, PMS_DEV_PRIMARY0x01); | |||
618 | ||||
619 | pms_change_state(sc, PMS_STATE_ENABLED1, PMS_DEV_PRIMARY0x01); | |||
620 | if (sc->sc_sec_wsmousedev != NULL((void *)0)) | |||
621 | pms_change_state(sc, PMS_STATE_ENABLED1, PMS_DEV_SECONDARY0x02); | |||
622 | ||||
623 | rw_exit_write(&sc->sc_state_lock); | |||
624 | splx(s)spllower(s); | |||
625 | } | |||
626 | ||||
627 | int | |||
628 | pms_enable_intelli(struct pms_softc *sc) | |||
629 | { | |||
630 | u_char resp; | |||
631 | ||||
632 | /* the special sequence to enable the third button and the roller */ | |||
633 | if (pms_set_rate(sc, PMS_INTELLI_MAGIC1200) || | |||
634 | pms_set_rate(sc, PMS_INTELLI_MAGIC2100) || | |||
635 | pms_set_rate(sc, PMS_INTELLI_MAGIC380) || | |||
636 | pms_get_devid(sc, &resp) || | |||
637 | resp != PMS_INTELLI_ID0x03) | |||
638 | return (0); | |||
639 | ||||
640 | return (1); | |||
641 | } | |||
642 | ||||
643 | int | |||
644 | pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, | |||
645 | struct proc *p) | |||
646 | { | |||
647 | int i; | |||
648 | ||||
649 | switch (cmd) { | |||
650 | case WSMOUSEIO_GTYPE((unsigned long)0x40000000 | ((sizeof(u_int) & 0x1fff) << 16) | ((('W')) << 8) | ((32))): | |||
651 | *(u_int *)data = WSMOUSE_TYPE_PS22; | |||
652 | break; | |||
653 | case WSMOUSEIO_SRES((unsigned long)0x80000000 | ((sizeof(u_int) & 0x1fff) << 16) | ((('W')) << 8) | ((33))): | |||
654 | i = ((int) *(u_int *)data - 12) / 25; | |||
655 | /* valid values are {0,1,2,3} */ | |||
656 | if (i < 0) | |||
657 | i = 0; | |||
658 | if (i > 3) | |||
659 | i = 3; | |||
660 | ||||
661 | if (pms_set_resolution(sc, i)) | |||
662 | printf("%s: SET_RES command error\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
663 | break; | |||
664 | default: | |||
665 | return (-1); | |||
666 | } | |||
667 | return (0); | |||
668 | } | |||
669 | ||||
670 | int | |||
671 | pms_sync_mouse(struct pms_softc *sc, int data) | |||
672 | { | |||
673 | if (sc->inputstate != 0) | |||
674 | return (0); | |||
675 | ||||
676 | switch (sc->protocol->type) { | |||
677 | case PMS_STANDARD0: | |||
678 | if ((data & 0xc0) != 0) | |||
679 | return (-1); | |||
680 | break; | |||
681 | case PMS_INTELLI1: | |||
682 | if ((data & 0x08) != 0x08) | |||
683 | return (-1); | |||
684 | break; | |||
685 | } | |||
686 | ||||
687 | return (0); | |||
688 | } | |||
689 | ||||
690 | void | |||
691 | pms_proc_mouse(struct pms_softc *sc) | |||
692 | { | |||
693 | u_int buttons; | |||
694 | int dx, dy, dz; | |||
695 | ||||
696 | buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK0x07]; | |||
697 | dx = (sc->packet[0] & PMS_PS2_XNEG0x10) ? | |||
698 | (int)sc->packet[1] - 256 : sc->packet[1]; | |||
699 | dy = (sc->packet[0] & PMS_PS2_YNEG0x20) ? | |||
700 | (int)sc->packet[2] - 256 : sc->packet[2]; | |||
701 | ||||
702 | if (sc->protocol->type == PMS_INTELLI1) | |||
703 | dz = (signed char)sc->packet[3]; | |||
704 | else | |||
705 | dz = 0; | |||
706 | ||||
707 | WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, dz, 0)do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_motion ((sc->sc_wsmousedev), (dx), (dy), (dz), (0)); wsmouse_input_sync (sc->sc_wsmousedev); } while (0); | |||
708 | } | |||
709 | ||||
710 | int | |||
711 | pmsprobe(struct device *parent, void *match, void *aux) | |||
712 | { | |||
713 | struct pckbc_attach_args *pa = aux; | |||
714 | u_char cmd[1], resp[2]; | |||
715 | int res; | |||
716 | ||||
717 | if (pa->pa_slot != PCKBC_AUX_SLOT1) | |||
718 | return (0); | |||
719 | ||||
720 | /* Flush any garbage. */ | |||
721 | pckbc_flush(pa->pa_tag, pa->pa_slot); | |||
722 | ||||
723 | /* reset the device */ | |||
724 | cmd[0] = PMS_RESET0xff; | |||
725 | res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); | |||
726 | if (res || resp[0] != PMS_RSTDONE0xaa || resp[1] != 0) { | |||
727 | #ifdef DEBUG | |||
728 | printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n", | |||
729 | res, resp[0], resp[1]); | |||
730 | #endif | |||
731 | return (0); | |||
732 | } | |||
733 | ||||
734 | return (1); | |||
735 | } | |||
736 | ||||
737 | void | |||
738 | pmsattach(struct device *parent, struct device *self, void *aux) | |||
739 | { | |||
740 | struct pms_softc *sc = (void *)self; | |||
741 | struct pckbc_attach_args *pa = aux; | |||
742 | struct wsmousedev_attach_args a; | |||
743 | ||||
744 | sc->sc_kbctag = pa->pa_tag; | |||
745 | ||||
746 | pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT1, | |||
747 | pmsinput, sc, DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
748 | ||||
749 | printf("\n"); | |||
750 | ||||
751 | a.accessops = &pms_accessops; | |||
752 | a.accesscookie = sc; | |||
753 | ||||
754 | rw_init(&sc->sc_state_lock, "pmsst")_rw_init_flags(&sc->sc_state_lock, "pmsst", 0, ((void * )0)); | |||
755 | ||||
756 | /* | |||
757 | * Attach the wsmouse, saving a handle to it. | |||
758 | * Note that we don't need to check this pointer against NULL | |||
759 | * here or in pmsintr, because if this fails pms_enable() will | |||
760 | * never be called, so pmsinput() will never be called. | |||
761 | */ | |||
762 | sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint)config_found_sm((self), (&a), (wsmousedevprint), ((void * )0)); | |||
763 | ||||
764 | task_set(&sc->sc_rsttask, pms_reset_task, sc); | |||
765 | timeout_set(&sc->sc_rsttimo, pms_reset_timo, sc); | |||
766 | ||||
767 | sc->poll = 1; | |||
768 | sc->sc_dev_enable = 0; | |||
769 | ||||
770 | /* See if the device understands an extended (touchpad) protocol. */ | |||
771 | pms_protocol_lookup(sc); | |||
772 | ||||
773 | /* no interrupts until enabled */ | |||
774 | pms_change_state(sc, PMS_STATE_DISABLED0, PMS_DEV_IGNORE0x00); | |||
775 | } | |||
776 | ||||
777 | int | |||
778 | pmsactivate(struct device *self, int act) | |||
779 | { | |||
780 | struct pms_softc *sc = (struct pms_softc *)self; | |||
781 | ||||
782 | switch (act) { | |||
783 | case DVACT_SUSPEND3: | |||
784 | if (sc->sc_state == PMS_STATE_ENABLED1) | |||
785 | pms_change_state(sc, PMS_STATE_SUSPENDED2, | |||
786 | PMS_DEV_IGNORE0x00); | |||
787 | break; | |||
788 | case DVACT_RESUME4: | |||
789 | if (sc->sc_state == PMS_STATE_SUSPENDED2) | |||
790 | pms_change_state(sc, PMS_STATE_ENABLED1, | |||
791 | PMS_DEV_IGNORE0x00); | |||
792 | break; | |||
793 | } | |||
794 | return (0); | |||
795 | } | |||
796 | ||||
797 | int | |||
798 | pms_change_state(struct pms_softc *sc, int newstate, int dev) | |||
799 | { | |||
800 | if (dev != PMS_DEV_IGNORE0x00) { | |||
801 | switch (newstate) { | |||
802 | case PMS_STATE_ENABLED1: | |||
803 | if (sc->sc_dev_enable & dev) | |||
804 | return (EBUSY16); | |||
805 | ||||
806 | sc->sc_dev_enable |= dev; | |||
807 | ||||
808 | if (sc->sc_state == PMS_STATE_ENABLED1) | |||
809 | return (0); | |||
810 | ||||
811 | break; | |||
812 | case PMS_STATE_DISABLED0: | |||
813 | sc->sc_dev_enable &= ~dev; | |||
814 | ||||
815 | if (sc->sc_dev_enable) | |||
816 | return (0); | |||
817 | ||||
818 | break; | |||
819 | } | |||
820 | } | |||
821 | ||||
822 | switch (newstate) { | |||
823 | case PMS_STATE_ENABLED1: | |||
824 | sc->inputstate = 0; | |||
825 | sc->sc_rststate = 0; | |||
826 | ||||
827 | pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT1, 1); | |||
828 | ||||
829 | if (sc->poll) | |||
830 | pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT1); | |||
831 | ||||
832 | pms_reset(sc); | |||
833 | if (sc->protocol->enable != NULL((void *)0) && | |||
834 | sc->protocol->enable(sc) == 0) | |||
835 | pms_protocol_lookup(sc); | |||
836 | ||||
837 | pms_dev_enable(sc); | |||
838 | break; | |||
839 | case PMS_STATE_DISABLED0: | |||
840 | case PMS_STATE_SUSPENDED2: | |||
841 | pms_dev_disable(sc); | |||
842 | ||||
843 | if (sc->protocol->disable) | |||
844 | sc->protocol->disable(sc); | |||
845 | ||||
846 | pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT1, 0); | |||
847 | break; | |||
848 | } | |||
849 | ||||
850 | sc->sc_state = newstate; | |||
851 | sc->poll = (newstate == PMS_STATE_SUSPENDED2) ? 1 : 0; | |||
852 | ||||
853 | return (0); | |||
854 | } | |||
855 | ||||
856 | int | |||
857 | pms_enable(void *v) | |||
858 | { | |||
859 | struct pms_softc *sc = v; | |||
860 | int rv; | |||
861 | ||||
862 | rw_enter_write(&sc->sc_state_lock); | |||
863 | rv = pms_change_state(sc, PMS_STATE_ENABLED1, PMS_DEV_PRIMARY0x01); | |||
864 | rw_exit_write(&sc->sc_state_lock); | |||
865 | ||||
866 | return (rv); | |||
867 | } | |||
868 | ||||
869 | void | |||
870 | pms_disable(void *v) | |||
871 | { | |||
872 | struct pms_softc *sc = v; | |||
873 | ||||
874 | rw_enter_write(&sc->sc_state_lock); | |||
875 | pms_change_state(sc, PMS_STATE_DISABLED0, PMS_DEV_PRIMARY0x01); | |||
876 | rw_exit_write(&sc->sc_state_lock); | |||
877 | } | |||
878 | ||||
879 | int | |||
880 | pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) | |||
881 | { | |||
882 | struct pms_softc *sc = v; | |||
883 | ||||
884 | if (sc->protocol->ioctl) | |||
885 | return (sc->protocol->ioctl(sc, cmd, data, flag, p)); | |||
886 | else | |||
887 | return (-1); | |||
888 | } | |||
889 | ||||
890 | int | |||
891 | pms_sec_enable(void *v) | |||
892 | { | |||
893 | struct pms_softc *sc = v; | |||
894 | int rv; | |||
895 | ||||
896 | rw_enter_write(&sc->sc_state_lock); | |||
897 | rv = pms_change_state(sc, PMS_STATE_ENABLED1, PMS_DEV_SECONDARY0x02); | |||
898 | rw_exit_write(&sc->sc_state_lock); | |||
899 | ||||
900 | return (rv); | |||
901 | } | |||
902 | ||||
903 | void | |||
904 | pms_sec_disable(void *v) | |||
905 | { | |||
906 | struct pms_softc *sc = v; | |||
907 | ||||
908 | rw_enter_write(&sc->sc_state_lock); | |||
909 | pms_change_state(sc, PMS_STATE_DISABLED0, PMS_DEV_SECONDARY0x02); | |||
910 | rw_exit_write(&sc->sc_state_lock); | |||
911 | } | |||
912 | ||||
913 | int | |||
914 | pms_sec_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) | |||
915 | { | |||
916 | switch (cmd) { | |||
917 | case WSMOUSEIO_GTYPE((unsigned long)0x40000000 | ((sizeof(u_int) & 0x1fff) << 16) | ((('W')) << 8) | ((32))): | |||
918 | *(u_int *)data = WSMOUSE_TYPE_PS22; | |||
919 | break; | |||
920 | default: | |||
921 | return (-1); | |||
922 | } | |||
923 | return (0); | |||
924 | } | |||
925 | ||||
926 | #ifdef DIAGNOSTIC1 | |||
927 | static inline void | |||
928 | pms_print_packet(struct pms_softc *sc) | |||
929 | { | |||
930 | int i, state, size; | |||
931 | ||||
932 | state = sc->inputstate; | |||
933 | size = sc->protocol->packetsize; | |||
934 | for (i = 0; i < size; i++) | |||
935 | printf(i == state ? " %02x |" : " %02x", sc->packet[i]); | |||
936 | } | |||
937 | #endif | |||
938 | ||||
939 | void | |||
940 | pmsinput(void *vsc, int data) | |||
941 | { | |||
942 | struct pms_softc *sc = vsc; | |||
943 | ||||
944 | if (sc->sc_state != PMS_STATE_ENABLED1) { | |||
945 | /* Interrupts are not expected. Discard the byte. */ | |||
946 | return; | |||
947 | } | |||
948 | ||||
949 | sc->packet[sc->inputstate] = data; | |||
950 | pms_reset_detect(sc, data); | |||
951 | if (sc->protocol->sync(sc, data)) { | |||
952 | #ifdef DIAGNOSTIC1 | |||
953 | printf("%s: not in sync yet, discard input " | |||
954 | "(state = %d,", | |||
955 | DEVNAME(sc)((sc)->sc_dev.dv_xname), sc->inputstate); | |||
956 | pms_print_packet(sc); | |||
957 | printf(")\n"); | |||
958 | #endif | |||
959 | ||||
960 | sc->inputstate = 0; | |||
961 | return; | |||
962 | } | |||
963 | ||||
964 | sc->inputstate++; | |||
965 | ||||
966 | if (sc->inputstate != sc->protocol->packetsize) | |||
967 | return; | |||
968 | ||||
969 | sc->inputstate = 0; | |||
970 | sc->protocol->proc(sc); | |||
971 | } | |||
972 | ||||
973 | int | |||
974 | synaptics_set_mode(struct pms_softc *sc, int mode, int rate) | |||
975 | { | |||
976 | struct synaptics_softc *syn = sc->synaptics; | |||
977 | ||||
978 | if (pms_spec_cmd(sc, mode) || | |||
979 | pms_set_rate(sc, rate == 0 ? SYNAPTICS_CMD_SET_MODE0x14 : rate)) | |||
980 | return (-1); | |||
981 | ||||
982 | /* | |||
983 | * Make sure that the set mode command has finished. | |||
984 | * Otherwise enabling the device before that will make it fail. | |||
985 | */ | |||
986 | delay(10000)(*delay_func)(10000); | |||
987 | ||||
988 | if (rate == 0) | |||
989 | syn->mode = mode; | |||
990 | ||||
991 | return (0); | |||
992 | } | |||
993 | ||||
994 | int | |||
995 | synaptics_query(struct pms_softc *sc, int query, int *val) | |||
996 | { | |||
997 | u_char resp[3]; | |||
998 | ||||
999 | if (pms_spec_cmd(sc, query) || | |||
1000 | pms_get_status(sc, resp)) | |||
1001 | return (-1); | |||
1002 | ||||
1003 | if (val) | |||
1004 | *val = (resp[0] << 16) | (resp[1] << 8) | resp[2]; | |||
1005 | ||||
1006 | return (0); | |||
1007 | } | |||
1008 | ||||
1009 | int | |||
1010 | synaptics_get_hwinfo(struct pms_softc *sc) | |||
1011 | { | |||
1012 | struct synaptics_softc *syn = sc->synaptics; | |||
1013 | struct wsmousehw *hw; | |||
1014 | int resolution = 0, max_coords = 0, min_coords = 0; | |||
1015 | ||||
1016 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
1017 | ||||
1018 | if (synaptics_query(sc, SYNAPTICS_QUE_IDENTIFY0x00, &syn->identify)) | |||
1019 | return (-1); | |||
1020 | if (synaptics_query(sc, SYNAPTICS_QUE_CAPABILITIES0x02, | |||
1021 | &syn->capabilities)) | |||
1022 | return (-1); | |||
1023 | if (synaptics_query(sc, SYNAPTICS_QUE_MODEL0x03, &syn->model)) | |||
1024 | return (-1); | |||
1025 | if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities)(((syn->capabilities) >> 20) & 0x07) >= 1) && | |||
1026 | synaptics_query(sc, SYNAPTICS_QUE_EXT_MODEL0x09, &syn->ext_model)) | |||
1027 | return (-1); | |||
1028 | if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities)(((syn->capabilities) >> 20) & 0x07) >= 4) && | |||
1029 | synaptics_query(sc, SYNAPTICS_QUE_EXT_CAPABILITIES0x0c, | |||
1030 | &syn->ext_capabilities)) | |||
1031 | return (-1); | |||
1032 | if ((SYNAPTICS_ID_MAJOR(syn->identify)((syn->identify) & 0x0f) >= 4) && | |||
1033 | synaptics_query(sc, SYNAPTICS_QUE_RESOLUTION0x08, &resolution)) | |||
1034 | return (-1); | |||
1035 | if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities)(((syn->capabilities) >> 20) & 0x07) >= 5) && | |||
1036 | (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MAX_COORDS(1 << 17)) && | |||
1037 | synaptics_query(sc, SYNAPTICS_QUE_EXT_MAX_COORDS0x0d, &max_coords)) | |||
1038 | return (-1); | |||
1039 | if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities)(((syn->capabilities) >> 20) & 0x07) >= 7 || | |||
1040 | SYNAPTICS_ID_FULL(syn->identify)(((syn->identify) & 0x0f) << 8 | (((syn->identify ) >> 16) & 0xff)) == 0x801) && | |||
1041 | (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MIN_COORDS(1 << 13)) && | |||
1042 | synaptics_query(sc, SYNAPTICS_QUE_EXT_MIN_COORDS0x0f, &min_coords)) | |||
1043 | return (-1); | |||
1044 | ||||
1045 | if (SYNAPTICS_ID_FULL(syn->identify)(((syn->identify) & 0x0f) << 8 | (((syn->identify ) >> 16) & 0xff)) >= 0x705) { | |||
1046 | if (synaptics_query(sc, SYNAPTICS_QUE_MODES0x01, &syn->modes)) | |||
1047 | return (-1); | |||
1048 | if ((syn->modes & SYNAPTICS_EXT2_CAP(1 << 17)) && | |||
1049 | synaptics_query(sc, SYNAPTICS_QUE_EXT2_CAPABILITIES0x10, | |||
1050 | &syn->ext2_capabilities)) | |||
1051 | return (-1); | |||
1052 | } | |||
1053 | ||||
1054 | if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD(1 << 20)) && | |||
1055 | !(syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK(1 << 16)) | |||
1056 | && mouse_has_softbtn) | |||
1057 | hw->type = WSMOUSE_TYPE_SYNAP_SBTN19; | |||
1058 | else | |||
1059 | hw->type = WSMOUSE_TYPE_SYNAPTICS15; | |||
1060 | ||||
1061 | hw->hw_type = (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD(1 << 20)) | |||
1062 | ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD; | |||
1063 | ||||
1064 | if (resolution & SYNAPTICS_RESOLUTION_VALID(1 << 15)) { | |||
1065 | hw->h_res = SYNAPTICS_RESOLUTION_X(resolution)(((resolution) >> 16) & 0xff); | |||
1066 | hw->v_res = SYNAPTICS_RESOLUTION_Y(resolution)((resolution) & 0xff); | |||
1067 | } | |||
1068 | ||||
1069 | hw->x_min = (min_coords ? | |||
1070 | SYNAPTICS_X_LIMIT(min_coords)((((min_coords) & 0xff0000) >> 11) | (((min_coords) & 0xf00) >> 7)) : SYNAPTICS_XMIN_BEZEL1472); | |||
1071 | hw->y_min = (min_coords ? | |||
1072 | SYNAPTICS_Y_LIMIT(min_coords)((((min_coords) & 0xff) << 5) | (((min_coords) & 0xf000) >> 11)) : SYNAPTICS_YMIN_BEZEL1408); | |||
1073 | hw->x_max = (max_coords ? | |||
1074 | SYNAPTICS_X_LIMIT(max_coords)((((max_coords) & 0xff0000) >> 11) | (((max_coords) & 0xf00) >> 7)) : SYNAPTICS_XMAX_BEZEL5472); | |||
1075 | hw->y_max = (max_coords ? | |||
1076 | SYNAPTICS_Y_LIMIT(max_coords)((((max_coords) & 0xff) << 5) | (((max_coords) & 0xf000) >> 11)) : SYNAPTICS_YMAX_BEZEL4448); | |||
1077 | ||||
1078 | if ((syn->capabilities & SYNAPTICS_CAP_MULTIFINGER(1 << 1)) || | |||
1079 | SYNAPTICS_SUPPORTS_AGM(syn->ext_capabilities)((syn->ext_capabilities) & ((1 << 19) | (1 << 11)))) | |||
1080 | hw->contacts_max = SYNAPTICS_MAX_FINGERS3; | |||
1081 | else | |||
1082 | hw->contacts_max = 1; | |||
1083 | ||||
1084 | syn->sec_buttons = 0; | |||
1085 | ||||
1086 | if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model)((syn->ext_model >> 12) & 0x0f) > 8) | |||
1087 | syn->ext_model &= ~0xf000; | |||
1088 | ||||
1089 | if ((syn->model & SYNAPTICS_MODEL_NEWABS(1 << 7)) == 0) { | |||
1090 | printf("%s: don't support Synaptics OLDABS\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1091 | return (-1); | |||
1092 | } | |||
1093 | ||||
1094 | if ((SYNAPTICS_ID_MAJOR(syn->identify)((syn->identify) & 0x0f) == 5) && | |||
1095 | (SYNAPTICS_ID_MINOR(syn->identify)(((syn->identify) >> 16) & 0xff) == 9)) | |||
1096 | syn->mask = SYNAPTICS_MASK_NEWABS_RELAXED0xc0; | |||
1097 | else | |||
1098 | syn->mask = SYNAPTICS_MASK_NEWABS_STRICT0xc8; | |||
1099 | ||||
1100 | return (0); | |||
1101 | } | |||
1102 | ||||
1103 | void | |||
1104 | synaptics_sec_proc(struct pms_softc *sc) | |||
1105 | { | |||
1106 | struct synaptics_softc *syn = sc->synaptics; | |||
1107 | u_int buttons; | |||
1108 | int dx, dy; | |||
1109 | ||||
1110 | if ((sc->sc_dev_enable & PMS_DEV_SECONDARY0x02) == 0) | |||
1111 | return; | |||
1112 | ||||
1113 | buttons = butmap[sc->packet[1] & PMS_PS2_BUTTONSMASK0x07]; | |||
1114 | buttons |= syn->sec_buttons; | |||
1115 | dx = (sc->packet[1] & PMS_PS2_XNEG0x10) ? | |||
1116 | (int)sc->packet[4] - 256 : sc->packet[4]; | |||
1117 | dy = (sc->packet[1] & PMS_PS2_YNEG0x20) ? | |||
1118 | (int)sc->packet[5] - 256 : sc->packet[5]; | |||
1119 | ||||
1120 | WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0)do { wsmouse_buttons((sc->sc_sec_wsmousedev), (buttons)); wsmouse_motion ((sc->sc_sec_wsmousedev), (dx), (dy), (0), (0)); wsmouse_input_sync (sc->sc_sec_wsmousedev); } while (0); | |||
1121 | } | |||
1122 | ||||
1123 | int | |||
1124 | synaptics_knock(struct pms_softc *sc) | |||
1125 | { | |||
1126 | u_char resp[3]; | |||
1127 | ||||
1128 | if (pms_set_resolution(sc, 0) || | |||
1129 | pms_set_resolution(sc, 0) || | |||
1130 | pms_set_resolution(sc, 0) || | |||
1131 | pms_set_resolution(sc, 0) || | |||
1132 | pms_get_status(sc, resp) || | |||
1133 | resp[1] != SYNAPTICS_ID_MAGIC0x47) | |||
1134 | return (-1); | |||
1135 | ||||
1136 | return (0); | |||
1137 | } | |||
1138 | ||||
1139 | int | |||
1140 | pms_enable_synaptics(struct pms_softc *sc) | |||
1141 | { | |||
1142 | struct synaptics_softc *syn = sc->synaptics; | |||
1143 | struct wsmousedev_attach_args a; | |||
1144 | int mode, i; | |||
1145 | ||||
1146 | if (synaptics_knock(sc)) { | |||
1147 | if (sc->synaptics == NULL((void *)0)) | |||
1148 | goto err; | |||
1149 | /* | |||
1150 | * Some synaptics touchpads don't resume quickly. | |||
1151 | * Retry a few times. | |||
1152 | */ | |||
1153 | for (i = 10; i > 0; --i) { | |||
1154 | printf("%s: device not resuming, retrying\n", | |||
1155 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1156 | pms_reset(sc); | |||
1157 | if (synaptics_knock(sc) == 0) | |||
1158 | break; | |||
1159 | delay(100000)(*delay_func)(100000); | |||
1160 | } | |||
1161 | if (i == 0) { | |||
1162 | printf("%s: lost device\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1163 | goto err; | |||
1164 | } | |||
1165 | } | |||
1166 | ||||
1167 | if (sc->synaptics == NULL((void *)0)) { | |||
1168 | sc->synaptics = syn = malloc(sizeof(struct synaptics_softc), | |||
1169 | M_DEVBUF2, M_WAITOK0x0001 | M_ZERO0x0008); | |||
1170 | if (syn == NULL((void *)0)) { | |||
1171 | printf("%s: synaptics: not enough memory\n", | |||
1172 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1173 | goto err; | |||
1174 | } | |||
1175 | ||||
1176 | if (synaptics_get_hwinfo(sc)) { | |||
1177 | free(sc->synaptics, M_DEVBUF2, | |||
1178 | sizeof(struct synaptics_softc)); | |||
1179 | sc->synaptics = NULL((void *)0); | |||
1180 | goto err; | |||
1181 | } | |||
1182 | ||||
1183 | /* enable pass-through PS/2 port if supported */ | |||
1184 | if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH(1 << 7)) { | |||
1185 | a.accessops = &pms_sec_accessops; | |||
1186 | a.accesscookie = sc; | |||
1187 | sc->sc_sec_wsmousedev = config_found((void *)sc, &a,config_found_sm(((void *)sc), (&a), (wsmousedevprint), (( void *)0)) | |||
1188 | wsmousedevprint)config_found_sm(((void *)sc), (&a), (wsmousedevprint), (( void *)0)); | |||
1189 | } | |||
1190 | ||||
1191 | if (wsmouse_configure(sc->sc_wsmousedev, synaptics_params, | |||
1192 | nitems(synaptics_params)(sizeof((synaptics_params)) / sizeof((synaptics_params)[0])))) | |||
1193 | goto err; | |||
1194 | ||||
1195 | printf("%s: Synaptics %s, firmware %d.%d, " | |||
1196 | "0x%x 0x%x 0x%x 0x%x 0x%x\n", | |||
1197 | DEVNAME(sc)((sc)->sc_dev.dv_xname), | |||
1198 | (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD(1 << 20) ? | |||
1199 | "clickpad" : "touchpad"), | |||
1200 | SYNAPTICS_ID_MAJOR(syn->identify)((syn->identify) & 0x0f), | |||
1201 | SYNAPTICS_ID_MINOR(syn->identify)(((syn->identify) >> 16) & 0xff), | |||
1202 | syn->model, syn->ext_model, syn->modes, | |||
1203 | syn->capabilities, syn->ext_capabilities); | |||
1204 | } | |||
1205 | ||||
1206 | /* | |||
1207 | * Enable absolute mode, plain W-mode and "advanced gesture mode" | |||
1208 | * (AGM), if possible. AGM, which seems to be a prerequisite for the | |||
1209 | * extended W-mode, might not always be necessary here, but at least | |||
1210 | * some older Synaptics models do not report finger counts without it. | |||
1211 | */ | |||
1212 | mode = SYNAPTICS_ABSOLUTE_MODE(1 << 7) | SYNAPTICS_HIGH_RATE(1 << 6); | |||
1213 | if (syn->capabilities & SYNAPTICS_CAP_EXTENDED(1 << 23)) | |||
1214 | mode |= SYNAPTICS_W_MODE(1 << 0); | |||
1215 | else if (SYNAPTICS_ID_MAJOR(syn->identify)((syn->identify) & 0x0f) >= 4) | |||
1216 | mode |= SYNAPTICS_DISABLE_GESTURE(1 << 2); | |||
1217 | if (synaptics_set_mode(sc, mode, 0)) | |||
1218 | goto err; | |||
1219 | ||||
1220 | if (SYNAPTICS_SUPPORTS_AGM(syn->ext_capabilities)((syn->ext_capabilities) & ((1 << 19) | (1 << 11))) && | |||
1221 | synaptics_set_mode(sc, SYNAPTICS_QUE_MODEL0x03, | |||
1222 | SYNAPTICS_CMD_SET_ADV_GESTURE_MODE0xc8)) | |||
1223 | goto err; | |||
1224 | ||||
1225 | return (1); | |||
1226 | ||||
1227 | err: | |||
1228 | pms_reset(sc); | |||
1229 | ||||
1230 | return (0); | |||
1231 | } | |||
1232 | ||||
1233 | int | |||
1234 | pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, | |||
1235 | struct proc *p) | |||
1236 | { | |||
1237 | struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; | |||
1238 | struct wsmousehw *hw; | |||
1239 | int wsmode; | |||
1240 | ||||
1241 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
1242 | switch (cmd) { | |||
1243 | case WSMOUSEIO_GTYPE((unsigned long)0x40000000 | ((sizeof(u_int) & 0x1fff) << 16) | ((('W')) << 8) | ((32))): | |||
1244 | *(u_int *)data = hw->type; | |||
1245 | break; | |||
1246 | case WSMOUSEIO_GCALIBCOORDS((unsigned long)0x40000000 | ((sizeof(struct wsmouse_calibcoords ) & 0x1fff) << 16) | ((('W')) << 8) | ((37))): | |||
1247 | wsmc->minx = hw->x_min; | |||
1248 | wsmc->maxx = hw->x_max; | |||
1249 | wsmc->miny = hw->y_min; | |||
1250 | wsmc->maxy = hw->y_max; | |||
1251 | wsmc->swapxy = 0; | |||
1252 | wsmc->resx = hw->h_res; | |||
1253 | wsmc->resy = hw->v_res; | |||
1254 | break; | |||
1255 | case WSMOUSEIO_SETMODE((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('W')) << 8) | ((38))): | |||
1256 | wsmode = *(u_int *)data; | |||
1257 | if (wsmode != WSMOUSE_COMPAT0 && wsmode != WSMOUSE_NATIVE1) | |||
1258 | return (EINVAL22); | |||
1259 | wsmouse_set_mode(sc->sc_wsmousedev, wsmode); | |||
1260 | break; | |||
1261 | default: | |||
1262 | return (-1); | |||
1263 | } | |||
1264 | return (0); | |||
1265 | } | |||
1266 | ||||
1267 | int | |||
1268 | pms_sync_synaptics(struct pms_softc *sc, int data) | |||
1269 | { | |||
1270 | struct synaptics_softc *syn = sc->synaptics; | |||
1271 | ||||
1272 | switch (sc->inputstate) { | |||
1273 | case 0: | |||
1274 | if ((data & syn->mask) != SYNAPTICS_VALID_NEWABS_FIRST0x80) | |||
1275 | return (-1); | |||
1276 | break; | |||
1277 | case 3: | |||
1278 | if ((data & syn->mask) != SYNAPTICS_VALID_NEWABS_NEXT0xc0) | |||
1279 | return (-1); | |||
1280 | break; | |||
1281 | } | |||
1282 | ||||
1283 | return (0); | |||
1284 | } | |||
1285 | ||||
1286 | void | |||
1287 | pms_proc_synaptics(struct pms_softc *sc) | |||
1288 | { | |||
1289 | struct synaptics_softc *syn = sc->synaptics; | |||
1290 | u_int buttons; | |||
1291 | int x, y, z, w, fingerwidth; | |||
1292 | ||||
1293 | w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) | | |||
1294 | ((sc->packet[3] & 0x04) >> 2); | |||
1295 | z = sc->packet[2]; | |||
1296 | ||||
1297 | if ((syn->capabilities & SYNAPTICS_CAP_EXTENDED(1 << 23)) == 0) { | |||
1298 | /* | |||
1299 | * Emulate W mode for models that don't provide it. Bit 3 | |||
1300 | * of the w-input signals a touch ("finger"), Bit 2 and | |||
1301 | * the "gesture" bits 1-0 can be ignored. | |||
1302 | */ | |||
1303 | if (w & 8) | |||
1304 | w = 4; | |||
1305 | else | |||
1306 | z = w = 0; | |||
1307 | } | |||
1308 | ||||
1309 | ||||
1310 | if (w == 3) { | |||
1311 | if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH(1 << 7)) | |||
1312 | synaptics_sec_proc(sc); | |||
1313 | return; | |||
1314 | } | |||
1315 | ||||
1316 | if ((sc->sc_dev_enable & PMS_DEV_PRIMARY0x01) == 0) | |||
1317 | return; | |||
1318 | ||||
1319 | if (w == 2) | |||
1320 | return; /* EW-mode packets are not expected here. */ | |||
1321 | ||||
1322 | x = ((sc->packet[3] & 0x10) << 8) | ((sc->packet[1] & 0x0f) << 8) | | |||
1323 | sc->packet[4]; | |||
1324 | y = ((sc->packet[3] & 0x20) << 7) | ((sc->packet[1] & 0xf0) << 4) | | |||
1325 | sc->packet[5]; | |||
1326 | ||||
1327 | buttons = ((sc->packet[0] & sc->packet[3]) & 0x01) ? | |||
1328 | WSMOUSE_BUTTON(1)(1 << ((1) - 1)) : 0; | |||
1329 | buttons |= ((sc->packet[0] & sc->packet[3]) & 0x02) ? | |||
1330 | WSMOUSE_BUTTON(3)(1 << ((3) - 1)) : 0; | |||
1331 | ||||
1332 | if (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD(1 << 20)) { | |||
1333 | buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? | |||
1334 | WSMOUSE_BUTTON(1)(1 << ((1) - 1)) : 0; | |||
1335 | } else if (syn->capabilities & SYNAPTICS_CAP_MIDDLE_BUTTON(1 << 18)) { | |||
1336 | buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? | |||
1337 | WSMOUSE_BUTTON(2)(1 << ((2) - 1)) : 0; | |||
1338 | } | |||
1339 | ||||
1340 | if (syn->capabilities & SYNAPTICS_CAP_FOUR_BUTTON(1 << 3)) { | |||
1341 | buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? | |||
1342 | WSMOUSE_BUTTON(4)(1 << ((4) - 1)) : 0; | |||
1343 | buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x02) ? | |||
1344 | WSMOUSE_BUTTON(5)(1 << ((5) - 1)) : 0; | |||
1345 | } else if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model)((syn->ext_model >> 12) & 0x0f) && | |||
1346 | ((sc->packet[0] ^ sc->packet[3]) & 0x02)) { | |||
1347 | if (syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK(1 << 16)) { | |||
1348 | /* | |||
1349 | * Trackstick buttons on this machine are wired to the | |||
1350 | * trackpad as extra buttons, so route the event | |||
1351 | * through the trackstick interface as normal buttons | |||
1352 | */ | |||
1353 | syn->sec_buttons = | |||
1354 | (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(1)(1 << ((1) - 1)) : 0; | |||
1355 | syn->sec_buttons |= | |||
1356 | (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(3)(1 << ((3) - 1)) : 0; | |||
1357 | syn->sec_buttons |= | |||
1358 | (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(2)(1 << ((2) - 1)) : 0; | |||
1359 | wsmouse_buttons( | |||
1360 | sc->sc_sec_wsmousedev, syn->sec_buttons); | |||
1361 | wsmouse_input_sync(sc->sc_sec_wsmousedev); | |||
1362 | return; | |||
1363 | } | |||
1364 | ||||
1365 | buttons |= (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(6)(1 << ((6) - 1)) : 0; | |||
1366 | buttons |= (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(7)(1 << ((7) - 1)) : 0; | |||
1367 | buttons |= (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(8)(1 << ((8) - 1)) : 0; | |||
1368 | buttons |= (sc->packet[5] & 0x02) ? WSMOUSE_BUTTON(9)(1 << ((9) - 1)) : 0; | |||
1369 | buttons |= (sc->packet[4] & 0x04) ? WSMOUSE_BUTTON(10)(1 << ((10) - 1)) : 0; | |||
1370 | buttons |= (sc->packet[5] & 0x04) ? WSMOUSE_BUTTON(11)(1 << ((11) - 1)) : 0; | |||
1371 | buttons |= (sc->packet[4] & 0x08) ? WSMOUSE_BUTTON(12)(1 << ((12) - 1)) : 0; | |||
1372 | buttons |= (sc->packet[5] & 0x08) ? WSMOUSE_BUTTON(13)(1 << ((13) - 1)) : 0; | |||
1373 | x &= ~0x0f; | |||
1374 | y &= ~0x0f; | |||
1375 | } | |||
1376 | ||||
1377 | if (z) { | |||
1378 | fingerwidth = max(w, 4); | |||
1379 | w = (w < 2 ? w + 2 : 1); | |||
1380 | } else { | |||
1381 | fingerwidth = 0; | |||
1382 | w = 0; | |||
1383 | } | |||
1384 | wsmouse_set(sc->sc_wsmousedev, WSMOUSE_TOUCH_WIDTH, fingerwidth, 0); | |||
1385 | WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w)do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (x), (y)); wsmouse_touch((sc->sc_wsmousedev ), (z), (w)); wsmouse_input_sync(sc->sc_wsmousedev); } while (0); | |||
1386 | } | |||
1387 | ||||
1388 | void | |||
1389 | pms_disable_synaptics(struct pms_softc *sc) | |||
1390 | { | |||
1391 | struct synaptics_softc *syn = sc->synaptics; | |||
1392 | ||||
1393 | if (syn->capabilities & SYNAPTICS_CAP_SLEEP(1 << 4)) | |||
1394 | synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE(1 << 3) | | |||
1395 | SYNAPTICS_DISABLE_GESTURE(1 << 2), 0); | |||
1396 | } | |||
1397 | ||||
1398 | int | |||
1399 | alps_sec_proc(struct pms_softc *sc) | |||
1400 | { | |||
1401 | struct alps_softc *alps = sc->alps; | |||
1402 | int dx, dy, pos = 0; | |||
1403 | ||||
1404 | if ((sc->packet[0] & PMS_ALPS_PS2_MASK0xc8) == PMS_ALPS_PS2_VALID0x08) { | |||
1405 | /* | |||
1406 | * We need to keep buttons states because interleaved | |||
1407 | * packets only signalize x/y movements. | |||
1408 | */ | |||
1409 | alps->sec_buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK0x07]; | |||
1410 | } else if ((sc->packet[3] & PMS_ALPS_INTERLEAVED_MASK0xcf) == | |||
1411 | PMS_ALPS_INTERLEAVED_VALID0x0f) { | |||
1412 | sc->inputstate = 3; | |||
1413 | pos = 3; | |||
1414 | } else { | |||
1415 | return (0); | |||
1416 | } | |||
1417 | ||||
1418 | if ((sc->sc_dev_enable & PMS_DEV_SECONDARY0x02) == 0) | |||
1419 | return (1); | |||
1420 | ||||
1421 | dx = (sc->packet[pos] & PMS_PS2_XNEG0x10) ? | |||
1422 | (int)sc->packet[pos + 1] - 256 : sc->packet[pos + 1]; | |||
1423 | dy = (sc->packet[pos] & PMS_PS2_YNEG0x20) ? | |||
1424 | (int)sc->packet[pos + 2] - 256 : sc->packet[pos + 2]; | |||
1425 | ||||
1426 | WSMOUSE_INPUT(sc->sc_sec_wsmousedev, alps->sec_buttons, dx, dy, 0, 0)do { wsmouse_buttons((sc->sc_sec_wsmousedev), (alps->sec_buttons )); wsmouse_motion((sc->sc_sec_wsmousedev), (dx), (dy), (0 ), (0)); wsmouse_input_sync(sc->sc_sec_wsmousedev); } while (0); | |||
1427 | ||||
1428 | return (1); | |||
1429 | } | |||
1430 | ||||
1431 | int | |||
1432 | alps_get_hwinfo(struct pms_softc *sc) | |||
1433 | { | |||
1434 | struct alps_softc *alps = sc->alps; | |||
1435 | u_char resp[3]; | |||
1436 | int i; | |||
1437 | struct wsmousehw *hw; | |||
1438 | ||||
1439 | if (pms_set_resolution(sc, 0) || | |||
1440 | pms_set_scaling(sc, 2) || | |||
1441 | pms_set_scaling(sc, 2) || | |||
1442 | pms_set_scaling(sc, 2) || | |||
1443 | pms_get_status(sc, resp)) { | |||
1444 | DPRINTF("%s: alps: model query error\n", DEVNAME(sc)); | |||
1445 | return (-1); | |||
1446 | } | |||
1447 | ||||
1448 | alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1); | |||
1449 | ||||
1450 | for (i = 0; i < nitems(alps_models)(sizeof((alps_models)) / sizeof((alps_models)[0])); i++) | |||
1451 | if (alps->version == alps_models[i].version) { | |||
1452 | alps->model = alps_models[i].model; | |||
1453 | alps->mask = alps_models[i].mask; | |||
1454 | ||||
1455 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
1456 | hw->type = WSMOUSE_TYPE_ALPS16; | |||
1457 | hw->hw_type = WSMOUSEHW_TOUCHPAD; | |||
1458 | hw->x_min = ALPS_XMIN_BEZEL0; | |||
1459 | hw->y_min = ALPS_YMIN_BEZEL0; | |||
1460 | hw->x_max = ALPS_XMAX_BEZEL1023; | |||
1461 | hw->y_max = ALPS_YMAX_BEZEL767; | |||
1462 | hw->contacts_max = 1; | |||
1463 | ||||
1464 | return (0); | |||
1465 | } | |||
1466 | ||||
1467 | return (-1); | |||
1468 | } | |||
1469 | ||||
1470 | int | |||
1471 | pms_enable_alps(struct pms_softc *sc) | |||
1472 | { | |||
1473 | struct alps_softc *alps = sc->alps; | |||
1474 | struct wsmousedev_attach_args a; | |||
1475 | u_char resp[3]; | |||
1476 | ||||
1477 | if (pms_set_resolution(sc, 0) || | |||
1478 | pms_set_scaling(sc, 1) || | |||
1479 | pms_set_scaling(sc, 1) || | |||
1480 | pms_set_scaling(sc, 1) || | |||
1481 | pms_get_status(sc, resp) || | |||
1482 | resp[0] != PMS_ALPS_MAGIC10 || | |||
1483 | resp[1] != PMS_ALPS_MAGIC20 || | |||
1484 | (resp[2] != PMS_ALPS_MAGIC3_110 && resp[2] != PMS_ALPS_MAGIC3_280 && | |||
1485 | resp[2] != PMS_ALPS_MAGIC3_3100)) | |||
1486 | goto err; | |||
1487 | ||||
1488 | if (sc->alps == NULL((void *)0)) { | |||
1489 | sc->alps = alps = malloc(sizeof(struct alps_softc), | |||
1490 | M_DEVBUF2, M_WAITOK0x0001 | M_ZERO0x0008); | |||
1491 | if (alps == NULL((void *)0)) { | |||
1492 | printf("%s: alps: not enough memory\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1493 | goto err; | |||
1494 | } | |||
1495 | ||||
1496 | if (alps_get_hwinfo(sc)) { | |||
1497 | free(sc->alps, M_DEVBUF2, sizeof(struct alps_softc)); | |||
1498 | sc->alps = NULL((void *)0); | |||
1499 | goto err; | |||
1500 | } | |||
1501 | ||||
1502 | if (wsmouse_configure(sc->sc_wsmousedev, alps_params, | |||
1503 | nitems(alps_params)(sizeof((alps_params)) / sizeof((alps_params)[0])))) { | |||
1504 | free(sc->alps, M_DEVBUF2, sizeof(struct alps_softc)); | |||
1505 | sc->alps = NULL((void *)0); | |||
1506 | printf("%s: setup failed\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
1507 | goto err; | |||
1508 | } | |||
1509 | ||||
1510 | printf("%s: ALPS %s, version 0x%04x\n", DEVNAME(sc)((sc)->sc_dev.dv_xname), | |||
1511 | (alps->model & ALPS_DUALPOINT(1 << 2) ? "Dualpoint" : "Glidepoint"), | |||
1512 | alps->version); | |||
1513 | ||||
1514 | ||||
1515 | if (alps->model & ALPS_DUALPOINT(1 << 2)) { | |||
1516 | a.accessops = &pms_sec_accessops; | |||
1517 | a.accesscookie = sc; | |||
1518 | sc->sc_sec_wsmousedev = config_found((void *)sc, &a,config_found_sm(((void *)sc), (&a), (wsmousedevprint), (( void *)0)) | |||
1519 | wsmousedevprint)config_found_sm(((void *)sc), (&a), (wsmousedevprint), (( void *)0)); | |||
1520 | } | |||
1521 | } | |||
1522 | ||||
1523 | if (alps->model == 0) | |||
1524 | goto err; | |||
1525 | ||||
1526 | if ((alps->model & ALPS_PASSTHROUGH(1 << 3)) && | |||
1527 | (pms_set_scaling(sc, 2) || | |||
1528 | pms_set_scaling(sc, 2) || | |||
1529 | pms_set_scaling(sc, 2) || | |||
1530 | pms_dev_disable(sc))) { | |||
1531 | DPRINTF("%s: alps: passthrough on error\n", DEVNAME(sc)); | |||
1532 | goto err; | |||
1533 | } | |||
1534 | ||||
1535 | if (pms_dev_disable(sc) || | |||
1536 | pms_dev_disable(sc) || | |||
1537 | pms_set_rate(sc, 0x0a)) { | |||
1538 | DPRINTF("%s: alps: tapping error\n", DEVNAME(sc)); | |||
1539 | goto err; | |||
1540 | } | |||
1541 | ||||
1542 | if (pms_dev_disable(sc) || | |||
1543 | pms_dev_disable(sc) || | |||
1544 | pms_dev_disable(sc) || | |||
1545 | pms_dev_disable(sc) || | |||
1546 | pms_dev_enable(sc)) { | |||
1547 | DPRINTF("%s: alps: absolute mode error\n", DEVNAME(sc)); | |||
1548 | goto err; | |||
1549 | } | |||
1550 | ||||
1551 | if ((alps->model & ALPS_PASSTHROUGH(1 << 3)) && | |||
1552 | (pms_set_scaling(sc, 1) || | |||
1553 | pms_set_scaling(sc, 1) || | |||
1554 | pms_set_scaling(sc, 1) || | |||
1555 | pms_dev_disable(sc))) { | |||
1556 | DPRINTF("%s: alps: passthrough off error\n", DEVNAME(sc)); | |||
1557 | goto err; | |||
1558 | } | |||
1559 | ||||
1560 | alps->sec_buttons = 0; | |||
1561 | ||||
1562 | return (1); | |||
1563 | ||||
1564 | err: | |||
1565 | pms_reset(sc); | |||
1566 | ||||
1567 | return (0); | |||
1568 | } | |||
1569 | ||||
1570 | int | |||
1571 | pms_ioctl_alps(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, | |||
1572 | struct proc *p) | |||
1573 | { | |||
1574 | struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; | |||
1575 | int wsmode; | |||
1576 | struct wsmousehw *hw; | |||
1577 | ||||
1578 | switch (cmd) { | |||
1579 | case WSMOUSEIO_GTYPE((unsigned long)0x40000000 | ((sizeof(u_int) & 0x1fff) << 16) | ((('W')) << 8) | ((32))): | |||
1580 | *(u_int *)data = WSMOUSE_TYPE_ALPS16; | |||
1581 | break; | |||
1582 | case WSMOUSEIO_GCALIBCOORDS((unsigned long)0x40000000 | ((sizeof(struct wsmouse_calibcoords ) & 0x1fff) << 16) | ((('W')) << 8) | ((37))): | |||
1583 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
1584 | wsmc->minx = hw->x_min; | |||
1585 | wsmc->maxx = hw->x_max; | |||
1586 | wsmc->miny = hw->y_min; | |||
1587 | wsmc->maxy = hw->y_max; | |||
1588 | wsmc->swapxy = 0; | |||
1589 | break; | |||
1590 | case WSMOUSEIO_SETMODE((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('W')) << 8) | ((38))): | |||
1591 | wsmode = *(u_int *)data; | |||
1592 | if (wsmode != WSMOUSE_COMPAT0 && wsmode != WSMOUSE_NATIVE1) | |||
1593 | return (EINVAL22); | |||
1594 | wsmouse_set_mode(sc->sc_wsmousedev, wsmode); | |||
1595 | break; | |||
1596 | default: | |||
1597 | return (-1); | |||
1598 | } | |||
1599 | return (0); | |||
1600 | } | |||
1601 | ||||
1602 | int | |||
1603 | pms_sync_alps(struct pms_softc *sc, int data) | |||
1604 | { | |||
1605 | struct alps_softc *alps = sc->alps; | |||
1606 | ||||
1607 | if ((alps->model & ALPS_DUALPOINT(1 << 2)) && | |||
1608 | (sc->packet[0] & PMS_ALPS_PS2_MASK0xc8) == PMS_ALPS_PS2_VALID0x08) { | |||
1609 | if (sc->inputstate == 2) | |||
1610 | sc->inputstate += 3; | |||
1611 | return (0); | |||
1612 | } | |||
1613 | ||||
1614 | switch (sc->inputstate) { | |||
1615 | case 0: | |||
1616 | if ((data & alps->mask) != alps->mask) | |||
1617 | return (-1); | |||
1618 | break; | |||
1619 | case 1: | |||
1620 | case 2: | |||
1621 | case 3: | |||
1622 | if ((data & PMS_ALPS_MASK0x80) != PMS_ALPS_VALID0x00) | |||
1623 | return (-1); | |||
1624 | break; | |||
1625 | case 4: | |||
1626 | case 5: | |||
1627 | if ((alps->model & ALPS_INTERLEAVED(1 << 4)) == 0 && | |||
1628 | (data & PMS_ALPS_MASK0x80) != PMS_ALPS_VALID0x00) | |||
1629 | return (-1); | |||
1630 | break; | |||
1631 | } | |||
1632 | ||||
1633 | return (0); | |||
1634 | } | |||
1635 | ||||
1636 | void | |||
1637 | pms_proc_alps(struct pms_softc *sc) | |||
1638 | { | |||
1639 | struct alps_softc *alps = sc->alps; | |||
1640 | int x, y, z, dx, dy; | |||
1641 | u_int buttons, gesture; | |||
1642 | ||||
1643 | if ((alps->model & ALPS_DUALPOINT(1 << 2)) && alps_sec_proc(sc)) | |||
1644 | return; | |||
1645 | ||||
1646 | x = sc->packet[1] | ((sc->packet[2] & 0x78) << 4); | |||
1647 | y = sc->packet[4] | ((sc->packet[3] & 0x70) << 3); | |||
1648 | z = sc->packet[5]; | |||
1649 | ||||
1650 | buttons = ((sc->packet[3] & 1) ? WSMOUSE_BUTTON(1)(1 << ((1) - 1)) : 0) | | |||
1651 | ((sc->packet[3] & 2) ? WSMOUSE_BUTTON(3)(1 << ((3) - 1)) : 0) | | |||
1652 | ((sc->packet[3] & 4) ? WSMOUSE_BUTTON(2)(1 << ((2) - 1)) : 0); | |||
1653 | ||||
1654 | if ((sc->sc_dev_enable & PMS_DEV_SECONDARY0x02) && z == ALPS_Z_MAGIC127) { | |||
1655 | dx = (x > ALPS_XSEC_BEZEL768 / 2) ? (x - ALPS_XSEC_BEZEL768) : x; | |||
1656 | dy = (y > ALPS_YSEC_BEZEL512 / 2) ? (y - ALPS_YSEC_BEZEL512) : y; | |||
1657 | ||||
1658 | WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0)do { wsmouse_buttons((sc->sc_sec_wsmousedev), (buttons)); wsmouse_motion ((sc->sc_sec_wsmousedev), (dx), (dy), (0), (0)); wsmouse_input_sync (sc->sc_sec_wsmousedev); } while (0); | |||
1659 | ||||
1660 | return; | |||
1661 | } | |||
1662 | ||||
1663 | if ((sc->sc_dev_enable & PMS_DEV_PRIMARY0x01) == 0) | |||
1664 | return; | |||
1665 | ||||
1666 | /* | |||
1667 | * XXX The Y-axis is in the oposit direction compared to | |||
1668 | * Synaptics touchpads and PS/2 mouses. | |||
1669 | * It's why we need to translate the y value here for both | |||
1670 | * NATIVE and COMPAT modes. | |||
1671 | */ | |||
1672 | y = ALPS_YMAX_BEZEL767 - y + ALPS_YMIN_BEZEL0; | |||
1673 | ||||
1674 | if (alps->gesture == ALPS_TAP0x01) { | |||
1675 | /* Report a touch with the tap coordinates. */ | |||
1676 | WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (alps->old_x), (alps->old_y)); wsmouse_touch((sc->sc_wsmousedev), (40), (0)); wsmouse_input_sync (sc->sc_wsmousedev); } while (0) | |||
1677 | alps->old_x, alps->old_y, ALPS_PRESSURE, 0)do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (alps->old_x), (alps->old_y)); wsmouse_touch((sc->sc_wsmousedev), (40), (0)); wsmouse_input_sync (sc->sc_wsmousedev); } while (0); | |||
1678 | if (z > 0) { | |||
1679 | /* | |||
1680 | * The hardware doesn't send a null pressure | |||
1681 | * event when dragging starts. | |||
1682 | */ | |||
1683 | WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (alps->old_x), (alps->old_y)); wsmouse_touch((sc->sc_wsmousedev), (0), (0)); wsmouse_input_sync (sc->sc_wsmousedev); } while (0) | |||
1684 | alps->old_x, alps->old_y, 0, 0)do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (alps->old_x), (alps->old_y)); wsmouse_touch((sc->sc_wsmousedev), (0), (0)); wsmouse_input_sync (sc->sc_wsmousedev); } while (0); | |||
1685 | } | |||
1686 | } | |||
1687 | ||||
1688 | gesture = sc->packet[2] & 0x03; | |||
1689 | if (gesture != ALPS_TAP0x01) | |||
1690 | WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, 0)do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (x), (y)); wsmouse_touch((sc->sc_wsmousedev ), (z), (0)); wsmouse_input_sync(sc->sc_wsmousedev); } while (0); | |||
1691 | ||||
1692 | if (alps->gesture != ALPS_DRAG0x03 || gesture != ALPS_TAP0x01) | |||
1693 | alps->gesture = gesture; | |||
1694 | ||||
1695 | alps->old_x = x; | |||
1696 | alps->old_y = y; | |||
1697 | } | |||
1698 | ||||
1699 | int | |||
1700 | elantech_set_absolute_mode_v1(struct pms_softc *sc) | |||
1701 | { | |||
1702 | int i; | |||
1703 | u_char resp[3]; | |||
1704 | ||||
1705 | /* Enable absolute mode. Magic numbers from Linux driver. */ | |||
1706 | if (pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG0x11) || | |||
1707 | pms_spec_cmd(sc, 0x10) || | |||
1708 | pms_spec_cmd(sc, 0x16) || | |||
1709 | pms_set_scaling(sc, 1) || | |||
1710 | pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG0x11) || | |||
1711 | pms_spec_cmd(sc, 0x11) || | |||
1712 | pms_spec_cmd(sc, 0x8f) || | |||
1713 | pms_set_scaling(sc, 1)) | |||
1714 | return (-1); | |||
1715 | ||||
1716 | /* Read back reg 0x10 to ensure hardware is ready. */ | |||
1717 | for (i = 0; i < 5; i++) { | |||
1718 | if (pms_spec_cmd(sc, ELANTECH_CMD_READ_REG0x10) || | |||
1719 | pms_spec_cmd(sc, 0x10) || | |||
1720 | pms_get_status(sc, resp) == 0) | |||
1721 | break; | |||
1722 | delay(2000)(*delay_func)(2000); | |||
1723 | } | |||
1724 | if (i
| |||
1725 | return (-1); | |||
1726 | ||||
1727 | if ((resp[0] & ELANTECH_ABSOLUTE_MODE0x04) == 0) | |||
| ||||
1728 | return (-1); | |||
1729 | ||||
1730 | return (0); | |||
1731 | } | |||
1732 | ||||
1733 | int | |||
1734 | elantech_set_absolute_mode_v2(struct pms_softc *sc) | |||
1735 | { | |||
1736 | int i; | |||
1737 | u_char resp[3]; | |||
1738 | u_char reg10 = (sc->elantech->fw_version == 0x20030 ? 0x54 : 0xc4); | |||
1739 | ||||
1740 | /* Enable absolute mode. Magic numbers from Linux driver. */ | |||
1741 | if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1742 | elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG0x11) || | |||
1743 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1744 | elantech_ps2_cmd(sc, 0x10) || | |||
1745 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1746 | elantech_ps2_cmd(sc, reg10) || | |||
1747 | pms_set_scaling(sc, 1) || | |||
1748 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1749 | elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG0x11) || | |||
1750 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1751 | elantech_ps2_cmd(sc, 0x11) || | |||
1752 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1753 | elantech_ps2_cmd(sc, 0x88) || | |||
1754 | pms_set_scaling(sc, 1)) | |||
1755 | return (-1); | |||
1756 | ||||
1757 | /* Read back reg 0x10 to ensure hardware is ready. */ | |||
1758 | for (i = 0; i < 5; i++) { | |||
1759 | if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1760 | elantech_ps2_cmd(sc, ELANTECH_CMD_READ_REG0x10) || | |||
1761 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1762 | elantech_ps2_cmd(sc, 0x10) || | |||
1763 | pms_get_status(sc, resp) == 0) | |||
1764 | break; | |||
1765 | delay(2000)(*delay_func)(2000); | |||
1766 | } | |||
1767 | if (i == 5) | |||
1768 | return (-1); | |||
1769 | ||||
1770 | return (0); | |||
1771 | } | |||
1772 | ||||
1773 | int | |||
1774 | elantech_set_absolute_mode_v3(struct pms_softc *sc) | |||
1775 | { | |||
1776 | int i; | |||
1777 | u_char resp[3]; | |||
1778 | ||||
1779 | /* Enable absolute mode. Magic numbers from Linux driver. */ | |||
1780 | if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1781 | elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG0x00) || | |||
1782 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1783 | elantech_ps2_cmd(sc, 0x10) || | |||
1784 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1785 | elantech_ps2_cmd(sc, 0x0b) || | |||
1786 | pms_set_scaling(sc, 1)) | |||
1787 | return (-1); | |||
1788 | ||||
1789 | /* Read back reg 0x10 to ensure hardware is ready. */ | |||
1790 | for (i = 0; i < 5; i++) { | |||
1791 | if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1792 | elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG0x00) || | |||
1793 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1794 | elantech_ps2_cmd(sc, 0x10) || | |||
1795 | pms_get_status(sc, resp) == 0) | |||
1796 | break; | |||
1797 | delay(2000)(*delay_func)(2000); | |||
1798 | } | |||
1799 | if (i == 5) | |||
1800 | return (-1); | |||
1801 | ||||
1802 | return (0); | |||
1803 | } | |||
1804 | ||||
1805 | int | |||
1806 | elantech_set_absolute_mode_v4(struct pms_softc *sc) | |||
1807 | { | |||
1808 | /* Enable absolute mode. Magic numbers from Linux driver. */ | |||
1809 | if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1810 | elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG0x00) || | |||
1811 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1812 | elantech_ps2_cmd(sc, 0x07) || | |||
1813 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1814 | elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG0x00) || | |||
1815 | elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND0xf8) || | |||
1816 | elantech_ps2_cmd(sc, 0x01) || | |||
1817 | pms_set_scaling(sc, 1)) | |||
1818 | return (-1); | |||
1819 | ||||
1820 | /* v4 has no register 0x10 to read response from */ | |||
1821 | ||||
1822 | return (0); | |||
1823 | } | |||
1824 | ||||
1825 | int | |||
1826 | elantech_get_hwinfo_v1(struct pms_softc *sc) | |||
1827 | { | |||
1828 | struct elantech_softc *elantech = sc->elantech; | |||
1829 | struct wsmousehw *hw; | |||
1830 | int fw_version; | |||
1831 | u_char capabilities[3]; | |||
1832 | ||||
1833 | if (synaptics_query(sc, ELANTECH_QUE_FW_VER1, &fw_version)) | |||
1834 | return (-1); | |||
1835 | ||||
1836 | if (fw_version < 0x20030 || fw_version == 0x20600) { | |||
1837 | if (fw_version < 0x20000) | |||
1838 | elantech->flags |= ELANTECH_F_HW_V1_OLD0x08; | |||
1839 | } else | |||
1840 | return (-1); | |||
1841 | ||||
1842 | elantech->fw_version = fw_version; | |||
1843 | ||||
1844 | if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES2) || | |||
1845 | pms_get_status(sc, capabilities)) | |||
1846 | return (-1); | |||
1847 | ||||
1848 | if (capabilities[0] & ELANTECH_CAP_HAS_ROCKER4) | |||
1849 | elantech->flags |= ELANTECH_F_HAS_ROCKER0x02; | |||
1850 | ||||
1851 | if (elantech_set_absolute_mode_v1(sc)) | |||
1852 | return (-1); | |||
1853 | ||||
1854 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
1855 | hw->type = WSMOUSE_TYPE_ELANTECH18; | |||
1856 | hw->hw_type = WSMOUSEHW_TOUCHPAD; | |||
1857 | hw->x_min = ELANTECH_V1_X_MIN(0 + 32); | |||
1858 | hw->x_max = ELANTECH_V1_X_MAX(576 - 32); | |||
1859 | hw->y_min = ELANTECH_V1_Y_MIN(0 + 32); | |||
1860 | hw->y_max = ELANTECH_V1_Y_MAX(384 - 32); | |||
1861 | ||||
1862 | return (0); | |||
1863 | } | |||
1864 | ||||
1865 | int | |||
1866 | elantech_get_hwinfo_v2(struct pms_softc *sc) | |||
1867 | { | |||
1868 | struct elantech_softc *elantech = sc->elantech; | |||
1869 | struct wsmousehw *hw; | |||
1870 | int fw_version, ic_ver; | |||
1871 | u_char capabilities[3]; | |||
1872 | int i, fixed_dpi; | |||
1873 | u_char resp[3]; | |||
1874 | ||||
1875 | if (synaptics_query(sc, ELANTECH_QUE_FW_VER1, &fw_version)) | |||
1876 | return (-1); | |||
1877 | ||||
1878 | ic_ver = (fw_version & 0x0f0000) >> 16; | |||
1879 | if (ic_ver != 2 && ic_ver != 4) | |||
1880 | return (-1); | |||
1881 | ||||
1882 | elantech->fw_version = fw_version; | |||
1883 | if (fw_version >= 0x20800) | |||
1884 | elantech->flags |= ELANTECH_F_REPORTS_PRESSURE0x01; | |||
1885 | ||||
1886 | if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES2) || | |||
1887 | pms_get_status(sc, capabilities)) | |||
1888 | return (-1); | |||
1889 | ||||
1890 | if (elantech_set_absolute_mode_v2(sc)) | |||
1891 | return (-1); | |||
1892 | ||||
1893 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
1894 | hw->type = WSMOUSE_TYPE_ELANTECH18; | |||
1895 | hw->hw_type = WSMOUSEHW_TOUCHPAD; | |||
1896 | ||||
1897 | if (fw_version == 0x20800 || fw_version == 0x20b00 || | |||
1898 | fw_version == 0x20030) { | |||
1899 | hw->x_max = ELANTECH_V2_X_MAX1152; | |||
1900 | hw->y_max = ELANTECH_V2_Y_MAX768; | |||
1901 | } else { | |||
1902 | if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID0) || | |||
1903 | pms_get_status(sc, resp)) | |||
1904 | return (-1); | |||
1905 | fixed_dpi = resp[1] & 0x10; | |||
1906 | i = (fw_version > 0x20800 && fw_version < 0x20900) ? 1 : 2; | |||
1907 | if ((fw_version >> 16) == 0x14 && fixed_dpi) { | |||
1908 | if (pms_spec_cmd(sc, ELANTECH_QUE_SAMPLE3) || | |||
1909 | pms_get_status(sc, resp)) | |||
1910 | return (-1); | |||
1911 | hw->x_max = (capabilities[1] - i) * resp[1] / 2; | |||
1912 | hw->y_max = (capabilities[2] - i) * resp[2] / 2; | |||
1913 | } else if (fw_version == 0x040216) { | |||
1914 | hw->x_max = 819; | |||
1915 | hw->y_max = 405; | |||
1916 | } else if (fw_version == 0x040219 || fw_version == 0x040215) { | |||
1917 | hw->x_max = 900; | |||
1918 | hw->y_max = 500; | |||
1919 | } else { | |||
1920 | hw->x_max = (capabilities[1] - i) * 64; | |||
1921 | hw->y_max = (capabilities[2] - i) * 64; | |||
1922 | } | |||
1923 | } | |||
1924 | ||||
1925 | return (0); | |||
1926 | } | |||
1927 | ||||
1928 | int | |||
1929 | elantech_get_hwinfo_v3(struct pms_softc *sc) | |||
1930 | { | |||
1931 | struct elantech_softc *elantech = sc->elantech; | |||
1932 | struct wsmousehw *hw; | |||
1933 | int fw_version; | |||
1934 | u_char resp[3]; | |||
1935 | ||||
1936 | if (synaptics_query(sc, ELANTECH_QUE_FW_VER1, &fw_version)) | |||
1937 | return (-1); | |||
1938 | ||||
1939 | if (((fw_version & 0x0f0000) >> 16) != 5) | |||
1940 | return (-1); | |||
1941 | ||||
1942 | elantech->fw_version = fw_version; | |||
1943 | elantech->flags |= ELANTECH_F_REPORTS_PRESSURE0x01; | |||
1944 | ||||
1945 | if ((fw_version & 0x4000) == 0x4000) | |||
1946 | elantech->flags |= ELANTECH_F_CRC_ENABLED0x10; | |||
1947 | ||||
1948 | if (elantech_set_absolute_mode_v3(sc)) | |||
1949 | return (-1); | |||
1950 | ||||
1951 | if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID0) || | |||
1952 | pms_get_status(sc, resp)) | |||
1953 | return (-1); | |||
1954 | ||||
1955 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
1956 | hw->x_max = elantech->max_x = (resp[0] & 0x0f) << 8 | resp[1]; | |||
1957 | hw->y_max = elantech->max_y = (resp[0] & 0xf0) << 4 | resp[2]; | |||
1958 | ||||
1959 | hw->type = WSMOUSE_TYPE_ELANTECH18; | |||
1960 | hw->hw_type = WSMOUSEHW_TOUCHPAD; | |||
1961 | ||||
1962 | return (0); | |||
1963 | } | |||
1964 | ||||
1965 | int | |||
1966 | elantech_get_hwinfo_v4(struct pms_softc *sc) | |||
1967 | { | |||
1968 | struct elantech_softc *elantech = sc->elantech; | |||
1969 | struct wsmousehw *hw; | |||
1970 | int fw_version; | |||
1971 | u_char capabilities[3]; | |||
1972 | u_char resp[3]; | |||
1973 | ||||
1974 | if (synaptics_query(sc, ELANTECH_QUE_FW_VER1, &fw_version)) | |||
1975 | return (-1); | |||
1976 | ||||
1977 | if ((fw_version & 0x0f0000) >> 16 < 6) | |||
1978 | return (-1); | |||
1979 | ||||
1980 | elantech->fw_version = fw_version; | |||
1981 | elantech->flags |= ELANTECH_F_REPORTS_PRESSURE0x01; | |||
1982 | ||||
1983 | if ((fw_version & 0x4000) == 0x4000) | |||
1984 | elantech->flags |= ELANTECH_F_CRC_ENABLED0x10; | |||
1985 | ||||
1986 | if (elantech_set_absolute_mode_v4(sc)) | |||
1987 | return (-1); | |||
1988 | ||||
1989 | if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES2) || | |||
1990 | pms_get_status(sc, capabilities)) | |||
1991 | return (-1); | |||
1992 | ||||
1993 | if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID0) || | |||
1994 | pms_get_status(sc, resp)) | |||
1995 | return (-1); | |||
1996 | ||||
1997 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
1998 | hw->x_max = (resp[0] & 0x0f) << 8 | resp[1]; | |||
1999 | hw->y_max = (resp[0] & 0xf0) << 4 | resp[2]; | |||
2000 | ||||
2001 | if ((capabilities[1] < 2) || (capabilities[1] > hw->x_max)) | |||
2002 | return (-1); | |||
2003 | ||||
2004 | if (capabilities[0] & ELANTECH_CAP_TRACKPOINT0x80) | |||
2005 | elantech->flags |= ELANTECH_F_TRACKPOINT0x20; | |||
2006 | ||||
2007 | hw->type = WSMOUSE_TYPE_ELANTECH18; | |||
2008 | hw->hw_type = (ELANTECH_IS_CLICKPAD(sc)(((sc)->elantech->fw_version & 0x1000) != 0) | |||
2009 | ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD); | |||
2010 | hw->mt_slots = ELANTECH_MAX_FINGERS5; | |||
2011 | ||||
2012 | elantech->width = hw->x_max / (capabilities[1] - 1); | |||
2013 | ||||
2014 | return (0); | |||
2015 | } | |||
2016 | ||||
2017 | int | |||
2018 | elantech_ps2_cmd(struct pms_softc *sc, u_char command) | |||
2019 | { | |||
2020 | u_char cmd[1]; | |||
2021 | ||||
2022 | cmd[0] = command; | |||
2023 | return (pms_cmd(sc, cmd, 1, NULL((void *)0), 0)); | |||
2024 | } | |||
2025 | ||||
2026 | int | |||
2027 | elantech_knock(struct pms_softc *sc) | |||
2028 | { | |||
2029 | u_char resp[3]; | |||
2030 | ||||
2031 | if (pms_dev_disable(sc) || | |||
2032 | pms_set_scaling(sc, 1) || | |||
2033 | pms_set_scaling(sc, 1) || | |||
2034 | pms_set_scaling(sc, 1) || | |||
2035 | pms_get_status(sc, resp) || | |||
2036 | resp[0] != PMS_ELANTECH_MAGIC10x3c || | |||
2037 | resp[1] != PMS_ELANTECH_MAGIC20x03 || | |||
2038 | (resp[2] != PMS_ELANTECH_MAGIC3_10xc8 && | |||
2039 | resp[2] != PMS_ELANTECH_MAGIC3_20x00)) | |||
2040 | return (-1); | |||
2041 | ||||
2042 | return (0); | |||
2043 | } | |||
2044 | ||||
2045 | int | |||
2046 | pms_enable_elantech_v1(struct pms_softc *sc) | |||
2047 | { | |||
2048 | struct elantech_softc *elantech = sc->elantech; | |||
2049 | int i; | |||
2050 | ||||
2051 | if (elantech_knock(sc)) | |||
| ||||
2052 | goto err; | |||
2053 | ||||
2054 | if (sc->elantech == NULL((void *)0)) { | |||
2055 | sc->elantech = elantech = malloc(sizeof(struct elantech_softc), | |||
2056 | M_DEVBUF2, M_WAITOK0x0001 | M_ZERO0x0008); | |||
2057 | if (elantech == NULL((void *)0)) { | |||
2058 | printf("%s: elantech: not enough memory\n", | |||
2059 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
2060 | goto err; | |||
2061 | } | |||
2062 | ||||
2063 | if (elantech_get_hwinfo_v1(sc)) { | |||
2064 | free(sc->elantech, M_DEVBUF2, | |||
2065 | sizeof(struct elantech_softc)); | |||
2066 | sc->elantech = NULL((void *)0); | |||
2067 | goto err; | |||
2068 | } | |||
2069 | if (wsmouse_configure(sc->sc_wsmousedev, NULL((void *)0), 0)) { | |||
2070 | free(sc->elantech, M_DEVBUF2, | |||
2071 | sizeof(struct elantech_softc)); | |||
2072 | sc->elantech = NULL((void *)0); | |||
2073 | printf("%s: elantech: setup failed\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
2074 | goto err; | |||
2075 | } | |||
2076 | ||||
2077 | printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n", | |||
2078 | DEVNAME(sc)((sc)->sc_dev.dv_xname), 1, sc->elantech->fw_version); | |||
2079 | } else if (elantech_set_absolute_mode_v1(sc)) | |||
2080 | goto err; | |||
2081 | ||||
2082 | for (i = 0; i < nitems(sc->elantech->parity)(sizeof((sc->elantech->parity)) / sizeof((sc->elantech ->parity)[0])); i++) | |||
2083 | sc->elantech->parity[i] = sc->elantech->parity[i & (i - 1)] ^ 1; | |||
2084 | ||||
2085 | return (1); | |||
2086 | ||||
2087 | err: | |||
2088 | pms_reset(sc); | |||
2089 | ||||
2090 | return (0); | |||
2091 | } | |||
2092 | ||||
2093 | int | |||
2094 | pms_enable_elantech_v2(struct pms_softc *sc) | |||
2095 | { | |||
2096 | struct elantech_softc *elantech = sc->elantech; | |||
2097 | ||||
2098 | if (elantech_knock(sc)) | |||
2099 | goto err; | |||
2100 | ||||
2101 | if (sc->elantech == NULL((void *)0)) { | |||
2102 | sc->elantech = elantech = malloc(sizeof(struct elantech_softc), | |||
2103 | M_DEVBUF2, M_WAITOK0x0001 | M_ZERO0x0008); | |||
2104 | if (elantech == NULL((void *)0)) { | |||
2105 | printf("%s: elantech: not enough memory\n", | |||
2106 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
2107 | goto err; | |||
2108 | } | |||
2109 | ||||
2110 | if (elantech_get_hwinfo_v2(sc)) { | |||
2111 | free(sc->elantech, M_DEVBUF2, | |||
2112 | sizeof(struct elantech_softc)); | |||
2113 | sc->elantech = NULL((void *)0); | |||
2114 | goto err; | |||
2115 | } | |||
2116 | if (wsmouse_configure(sc->sc_wsmousedev, NULL((void *)0), 0)) { | |||
2117 | free(sc->elantech, M_DEVBUF2, | |||
2118 | sizeof(struct elantech_softc)); | |||
2119 | sc->elantech = NULL((void *)0); | |||
2120 | printf("%s: elantech: setup failed\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
2121 | goto err; | |||
2122 | } | |||
2123 | ||||
2124 | printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n", | |||
2125 | DEVNAME(sc)((sc)->sc_dev.dv_xname), 2, sc->elantech->fw_version); | |||
2126 | } else if (elantech_set_absolute_mode_v2(sc)) | |||
2127 | goto err; | |||
2128 | ||||
2129 | return (1); | |||
2130 | ||||
2131 | err: | |||
2132 | pms_reset(sc); | |||
2133 | ||||
2134 | return (0); | |||
2135 | } | |||
2136 | ||||
2137 | int | |||
2138 | pms_enable_elantech_v3(struct pms_softc *sc) | |||
2139 | { | |||
2140 | struct elantech_softc *elantech = sc->elantech; | |||
2141 | ||||
2142 | if (elantech_knock(sc)) | |||
2143 | goto err; | |||
2144 | ||||
2145 | if (sc->elantech == NULL((void *)0)) { | |||
2146 | sc->elantech = elantech = malloc(sizeof(struct elantech_softc), | |||
2147 | M_DEVBUF2, M_WAITOK0x0001 | M_ZERO0x0008); | |||
2148 | if (elantech == NULL((void *)0)) { | |||
2149 | printf("%s: elantech: not enough memory\n", | |||
2150 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
2151 | goto err; | |||
2152 | } | |||
2153 | ||||
2154 | if (elantech_get_hwinfo_v3(sc)) { | |||
2155 | free(sc->elantech, M_DEVBUF2, | |||
2156 | sizeof(struct elantech_softc)); | |||
2157 | sc->elantech = NULL((void *)0); | |||
2158 | goto err; | |||
2159 | } | |||
2160 | if (wsmouse_configure(sc->sc_wsmousedev, NULL((void *)0), 0)) { | |||
2161 | free(sc->elantech, M_DEVBUF2, | |||
2162 | sizeof(struct elantech_softc)); | |||
2163 | sc->elantech = NULL((void *)0); | |||
2164 | printf("%s: elantech: setup failed\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
2165 | goto err; | |||
2166 | } | |||
2167 | ||||
2168 | printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n", | |||
2169 | DEVNAME(sc)((sc)->sc_dev.dv_xname), 3, sc->elantech->fw_version); | |||
2170 | } else if (elantech_set_absolute_mode_v3(sc)) | |||
2171 | goto err; | |||
2172 | ||||
2173 | return (1); | |||
2174 | ||||
2175 | err: | |||
2176 | pms_reset(sc); | |||
2177 | ||||
2178 | return (0); | |||
2179 | } | |||
2180 | ||||
2181 | int | |||
2182 | pms_enable_elantech_v4(struct pms_softc *sc) | |||
2183 | { | |||
2184 | struct elantech_softc *elantech = sc->elantech; | |||
2185 | struct wsmousedev_attach_args a; | |||
2186 | ||||
2187 | if (elantech_knock(sc)) | |||
2188 | goto err; | |||
2189 | ||||
2190 | if (sc->elantech == NULL((void *)0)) { | |||
2191 | sc->elantech = elantech = malloc(sizeof(struct elantech_softc), | |||
2192 | M_DEVBUF2, M_WAITOK0x0001 | M_ZERO0x0008); | |||
2193 | if (elantech == NULL((void *)0)) { | |||
2194 | printf("%s: elantech: not enough memory\n", | |||
2195 | DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
2196 | goto err; | |||
2197 | } | |||
2198 | ||||
2199 | if (elantech_get_hwinfo_v4(sc)) { | |||
2200 | free(sc->elantech, M_DEVBUF2, | |||
2201 | sizeof(struct elantech_softc)); | |||
2202 | sc->elantech = NULL((void *)0); | |||
2203 | goto err; | |||
2204 | } | |||
2205 | if (wsmouse_configure(sc->sc_wsmousedev, NULL((void *)0), 0)) { | |||
2206 | free(sc->elantech, M_DEVBUF2, | |||
2207 | sizeof(struct elantech_softc)); | |||
2208 | sc->elantech = NULL((void *)0); | |||
2209 | printf("%s: elantech: setup failed\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); | |||
2210 | goto err; | |||
2211 | } | |||
2212 | ||||
2213 | printf("%s: Elantech %s, version 4, firmware 0x%x\n", | |||
2214 | DEVNAME(sc)((sc)->sc_dev.dv_xname), (ELANTECH_IS_CLICKPAD(sc)(((sc)->elantech->fw_version & 0x1000) != 0) ? "Clickpad" | |||
2215 | : "Touchpad"), sc->elantech->fw_version); | |||
2216 | ||||
2217 | if (sc->elantech->flags & ELANTECH_F_TRACKPOINT0x20) { | |||
2218 | a.accessops = &pms_sec_accessops; | |||
2219 | a.accesscookie = sc; | |||
2220 | sc->sc_sec_wsmousedev = config_found((void *) sc, &a,config_found_sm(((void *) sc), (&a), (wsmousedevprint), ( (void *)0)) | |||
2221 | wsmousedevprint)config_found_sm(((void *) sc), (&a), (wsmousedevprint), ( (void *)0)); | |||
2222 | } | |||
2223 | ||||
2224 | } else if (elantech_set_absolute_mode_v4(sc)) | |||
2225 | goto err; | |||
2226 | ||||
2227 | return (1); | |||
2228 | ||||
2229 | err: | |||
2230 | pms_reset(sc); | |||
2231 | ||||
2232 | return (0); | |||
2233 | } | |||
2234 | ||||
2235 | int | |||
2236 | pms_ioctl_elantech(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, | |||
2237 | struct proc *p) | |||
2238 | { | |||
2239 | struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; | |||
2240 | struct wsmousehw *hw; | |||
2241 | int wsmode; | |||
2242 | ||||
2243 | switch (cmd) { | |||
2244 | case WSMOUSEIO_GTYPE((unsigned long)0x40000000 | ((sizeof(u_int) & 0x1fff) << 16) | ((('W')) << 8) | ((32))): | |||
2245 | *(u_int *)data = WSMOUSE_TYPE_ELANTECH18; | |||
2246 | break; | |||
2247 | case WSMOUSEIO_GCALIBCOORDS((unsigned long)0x40000000 | ((sizeof(struct wsmouse_calibcoords ) & 0x1fff) << 16) | ((('W')) << 8) | ((37))): | |||
2248 | hw = wsmouse_get_hw(sc->sc_wsmousedev); | |||
2249 | wsmc->minx = hw->x_min; | |||
2250 | wsmc->maxx = hw->x_max; | |||
2251 | wsmc->miny = hw->y_min; | |||
2252 | wsmc->maxy = hw->y_max; | |||
2253 | wsmc->swapxy = 0; | |||
2254 | wsmc->resx = hw->h_res; | |||
2255 | wsmc->resy = hw->v_res; | |||
2256 | break; | |||
2257 | case WSMOUSEIO_SETMODE((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('W')) << 8) | ((38))): | |||
2258 | wsmode = *(u_int *)data; | |||
2259 | if (wsmode != WSMOUSE_COMPAT0 && wsmode != WSMOUSE_NATIVE1) | |||
2260 | return (EINVAL22); | |||
2261 | wsmouse_set_mode(sc->sc_wsmousedev, wsmode); | |||
2262 | break; | |||
2263 | default: | |||
2264 | return (-1); | |||
2265 | } | |||
2266 | return (0); | |||
2267 | } | |||
2268 | ||||
2269 | int | |||
2270 | pms_sync_elantech_v1(struct pms_softc *sc, int data) | |||
2271 | { | |||
2272 | struct elantech_softc *elantech = sc->elantech; | |||
2273 | u_char p; | |||
2274 | ||||
2275 | switch (sc->inputstate) { | |||
2276 | case 0: | |||
2277 | if (elantech->flags & ELANTECH_F_HW_V1_OLD0x08) { | |||
2278 | elantech->p1 = (data & 0x20) >> 5; | |||
2279 | elantech->p2 = (data & 0x10) >> 4; | |||
2280 | } else { | |||
2281 | elantech->p1 = (data & 0x10) >> 4; | |||
2282 | elantech->p2 = (data & 0x20) >> 5; | |||
2283 | } | |||
2284 | elantech->p3 = (data & 0x04) >> 2; | |||
2285 | return (0); | |||
2286 | case 1: | |||
2287 | p = elantech->p1; | |||
2288 | break; | |||
2289 | case 2: | |||
2290 | p = elantech->p2; | |||
2291 | break; | |||
2292 | case 3: | |||
2293 | p = elantech->p3; | |||
2294 | break; | |||
2295 | default: | |||
2296 | return (-1); | |||
2297 | } | |||
2298 | ||||
2299 | if (data < 0 || data >= nitems(elantech->parity)(sizeof((elantech->parity)) / sizeof((elantech->parity) [0])) || | |||
2300 | /* | |||
2301 | * FW 0x20022 sends inverted parity bits on cold boot, returning | |||
2302 | * to normal after suspend & resume, so the parity check is | |||
2303 | * disabled for this one. | |||
2304 | */ | |||
2305 | (elantech->fw_version != 0x20022 && elantech->parity[data] != p)) | |||
2306 | return (-1); | |||
2307 | ||||
2308 | return (0); | |||
2309 | } | |||
2310 | ||||
2311 | int | |||
2312 | pms_sync_elantech_v2(struct pms_softc *sc, int data) | |||
2313 | { | |||
2314 | struct elantech_softc *elantech = sc->elantech; | |||
2315 | ||||
2316 | /* Variants reporting pressure always have the same constant bits. */ | |||
2317 | if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE0x01) { | |||
2318 | if (sc->inputstate == 0 && (data & 0x0c) != 0x04) | |||
2319 | return (-1); | |||
2320 | if (sc->inputstate == 3 && (data & 0x0f) != 0x02) | |||
2321 | return (-1); | |||
2322 | return (0); | |||
2323 | } | |||
2324 | ||||
2325 | /* For variants not reporting pressure, 1 and 3 finger touch packets | |||
2326 | * have different constant bits than 2 finger touch packets. */ | |||
2327 | switch (sc->inputstate) { | |||
2328 | case 0: | |||
2329 | if ((data & 0xc0) == 0x80) { | |||
2330 | if ((data & 0x0c) != 0x0c) | |||
2331 | return (-1); | |||
2332 | elantech->flags |= ELANTECH_F_2FINGER_PACKET0x04; | |||
2333 | } else { | |||
2334 | if ((data & 0x3c) != 0x3c) | |||
2335 | return (-1); | |||
2336 | elantech->flags &= ~ELANTECH_F_2FINGER_PACKET0x04; | |||
2337 | } | |||
2338 | break; | |||
2339 | case 1: | |||
2340 | case 4: | |||
2341 | if (elantech->flags & ELANTECH_F_2FINGER_PACKET0x04) | |||
2342 | break; | |||
2343 | if ((data & 0xf0) != 0x00) | |||
2344 | return (-1); | |||
2345 | break; | |||
2346 | case 3: | |||
2347 | if (elantech->flags & ELANTECH_F_2FINGER_PACKET0x04) { | |||
2348 | if ((data & 0x0e) != 0x08) | |||
2349 | return (-1); | |||
2350 | } else { | |||
2351 | if ((data & 0x3e) != 0x38) | |||
2352 | return (-1); | |||
2353 | } | |||
2354 | break; | |||
2355 | default: | |||
2356 | break; | |||
2357 | } | |||
2358 | ||||
2359 | return (0); | |||
2360 | } | |||
2361 | ||||
2362 | int | |||
2363 | pms_sync_elantech_v3(struct pms_softc *sc, int data) | |||
2364 | { | |||
2365 | struct elantech_softc *elantech = sc->elantech; | |||
2366 | ||||
2367 | switch (sc->inputstate) { | |||
2368 | case 0: | |||
2369 | if (elantech->flags & ELANTECH_F_CRC_ENABLED0x10) | |||
2370 | break; | |||
2371 | if ((data & 0x0c) != 0x04 && (data & 0x0c) != 0x0c) | |||
2372 | return (-1); | |||
2373 | break; | |||
2374 | case 3: | |||
2375 | if (elantech->flags & ELANTECH_F_CRC_ENABLED0x10) { | |||
2376 | if ((data & 0x09) != 0x08 && (data & 0x09) != 0x09) | |||
2377 | return (-1); | |||
2378 | } else { | |||
2379 | if ((data & 0xcf) != 0x02 && (data & 0xce) != 0x0c) | |||
2380 | return (-1); | |||
2381 | } | |||
2382 | break; | |||
2383 | } | |||
2384 | ||||
2385 | return (0); | |||
2386 | } | |||
2387 | ||||
2388 | /* Extract the type bits from packet[3]. */ | |||
2389 | static inline int | |||
2390 | elantech_packet_type(struct elantech_softc *elantech, u_char b) | |||
2391 | { | |||
2392 | /* | |||
2393 | * This looks dubious, but in the "crc-enabled" format bit 2 may | |||
2394 | * be set even in MOTION packets. | |||
2395 | */ | |||
2396 | if ((elantech->flags & ELANTECH_F_TRACKPOINT0x20) && ((b & 0x0f) == 0x06)) | |||
2397 | return (ELANTECH_PKT_TRACKPOINT0x06); | |||
2398 | else | |||
2399 | return (b & 0x03); | |||
2400 | } | |||
2401 | ||||
2402 | int | |||
2403 | pms_sync_elantech_v4(struct pms_softc *sc, int data) | |||
2404 | { | |||
2405 | if (sc->inputstate == 0) | |||
2406 | return ((data & 0x08) == 0 ? 0 : -1); | |||
2407 | ||||
2408 | if (sc->inputstate == 3) { | |||
2409 | switch (elantech_packet_type(sc->elantech, data)) { | |||
2410 | case ELANTECH_V4_PKT_STATUS0: | |||
2411 | case ELANTECH_V4_PKT_HEAD0x01: | |||
2412 | case ELANTECH_V4_PKT_MOTION0x02: | |||
2413 | if (sc->elantech->flags & ELANTECH_F_CRC_ENABLED0x10) | |||
2414 | return ((data & 0x08) == 0 ? 0 : -1); | |||
2415 | else | |||
2416 | return ((data & 0x1c) == 0x10 ? 0 : -1); | |||
2417 | case ELANTECH_PKT_TRACKPOINT0x06: | |||
2418 | return ((sc->packet[0] & 0xc8) == 0 | |||
2419 | && sc->packet[1] == ((data & 0x10) << 3) | |||
2420 | && sc->packet[2] == ((data & 0x20) << 2) | |||
2421 | && (data ^ (sc->packet[0] & 0x30)) == 0x36 | |||
2422 | ? 0 : -1); | |||
2423 | } | |||
2424 | return (-1); | |||
2425 | } | |||
2426 | return (0); | |||
2427 | } | |||
2428 | ||||
2429 | void | |||
2430 | pms_proc_elantech_v1(struct pms_softc *sc) | |||
2431 | { | |||
2432 | struct elantech_softc *elantech = sc->elantech; | |||
2433 | int x, y, w, z; | |||
2434 | u_int buttons; | |||
2435 | ||||
2436 | buttons = butmap[sc->packet[0] & 3]; | |||
2437 | ||||
2438 | if (elantech->flags & ELANTECH_F_HAS_ROCKER0x02) { | |||
2439 | if (sc->packet[0] & 0x40) /* up */ | |||
2440 | buttons |= WSMOUSE_BUTTON(4)(1 << ((4) - 1)); | |||
2441 | if (sc->packet[0] & 0x80) /* down */ | |||
2442 | buttons |= WSMOUSE_BUTTON(5)(1 << ((5) - 1)); | |||
2443 | } | |||
2444 | ||||
2445 | if (elantech->flags & ELANTECH_F_HW_V1_OLD0x08) | |||
2446 | w = ((sc->packet[1] & 0x80) >> 7) + | |||
2447 | ((sc->packet[1] & 0x30) >> 4); | |||
2448 | else | |||
2449 | w = (sc->packet[0] & 0xc0) >> 6; | |||
2450 | ||||
2451 | /* | |||
2452 | * Firmwares 0x20022 and 0x20600 have a bug, position data in the | |||
2453 | * first two reports for single-touch contacts may be corrupt. | |||
2454 | */ | |||
2455 | if (elantech->fw_version == 0x20022 || | |||
2456 | elantech->fw_version == 0x20600) { | |||
2457 | if (w == 1) { | |||
2458 | if (elantech->initial_pkt < 2) { | |||
2459 | elantech->initial_pkt++; | |||
2460 | return; | |||
2461 | } | |||
2462 | } else if (elantech->initial_pkt) { | |||
2463 | elantech->initial_pkt = 0; | |||
2464 | } | |||
2465 | } | |||
2466 | ||||
2467 | /* Hardware version 1 doesn't report pressure. */ | |||
2468 | if (w) { | |||
2469 | x = ((sc->packet[1] & 0x0c) << 6) | sc->packet[2]; | |||
2470 | y = ((sc->packet[1] & 0x03) << 8) | sc->packet[3]; | |||
2471 | z = SYNAPTICS_PRESSURE30; | |||
2472 | } else { | |||
2473 | x = y = z = 0; | |||
2474 | } | |||
2475 | ||||
2476 | WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w)do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (x), (y)); wsmouse_touch((sc->sc_wsmousedev ), (z), (w)); wsmouse_input_sync(sc->sc_wsmousedev); } while (0); | |||
2477 | } | |||
2478 | ||||
2479 | void | |||
2480 | pms_proc_elantech_v2(struct pms_softc *sc) | |||
2481 | { | |||
2482 | const u_char debounce_pkt[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff }; | |||
2483 | struct elantech_softc *elantech = sc->elantech; | |||
2484 | int x, y, w, z; | |||
2485 | u_int buttons; | |||
2486 | ||||
2487 | /* | |||
2488 | * The hardware sends this packet when in debounce state. | |||
2489 | * The packet should be ignored. | |||
2490 | */ | |||
2491 | if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt))__builtin_memcmp((sc->packet), (debounce_pkt), (sizeof(debounce_pkt )))) | |||
2492 | return; | |||
2493 | ||||
2494 | buttons = butmap[sc->packet[0] & 3]; | |||
2495 | ||||
2496 | w = (sc->packet[0] & 0xc0) >> 6; | |||
2497 | if (w == 1 || w == 3) { | |||
2498 | x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2]; | |||
2499 | y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5]; | |||
2500 | if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE0x01) | |||
2501 | z = ((sc->packet[1] & 0xf0) | | |||
2502 | (sc->packet[4] & 0xf0) >> 4); | |||
2503 | else | |||
2504 | z = SYNAPTICS_PRESSURE30; | |||
2505 | } else if (w == 2) { | |||
2506 | x = (((sc->packet[0] & 0x10) << 4) | sc->packet[1]) << 2; | |||
2507 | y = (((sc->packet[0] & 0x20) << 3) | sc->packet[2]) << 2; | |||
2508 | z = SYNAPTICS_PRESSURE30; | |||
2509 | } else { | |||
2510 | x = y = z = 0; | |||
2511 | } | |||
2512 | ||||
2513 | WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w)do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (x), (y)); wsmouse_touch((sc->sc_wsmousedev ), (z), (w)); wsmouse_input_sync(sc->sc_wsmousedev); } while (0); | |||
2514 | } | |||
2515 | ||||
2516 | void | |||
2517 | pms_proc_elantech_v3(struct pms_softc *sc) | |||
2518 | { | |||
2519 | const u_char debounce_pkt[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff }; | |||
2520 | struct elantech_softc *elantech = sc->elantech; | |||
2521 | int x, y, w, z; | |||
2522 | u_int buttons; | |||
2523 | ||||
2524 | buttons = butmap[sc->packet[0] & 3]; | |||
2525 | ||||
2526 | x = ((sc->packet[1] & 0x0f) << 8 | sc->packet[2]); | |||
2527 | y = ((sc->packet[4] & 0x0f) << 8 | sc->packet[5]); | |||
2528 | z = 0; | |||
2529 | w = (sc->packet[0] & 0xc0) >> 6; | |||
2530 | if (w == 2) { | |||
2531 | /* | |||
2532 | * Two-finger touch causes two packets -- a head packet | |||
2533 | * and a tail packet. We report a single event and ignore | |||
2534 | * the tail packet. | |||
2535 | */ | |||
2536 | if (elantech->flags & ELANTECH_F_CRC_ENABLED0x10) { | |||
2537 | if ((sc->packet[3] & 0x09) != 0x08) | |||
2538 | return; | |||
2539 | } else { | |||
2540 | /* The hardware sends this packet when in debounce state. | |||
2541 | * The packet should be ignored. */ | |||
2542 | if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt))__builtin_memcmp((sc->packet), (debounce_pkt), (sizeof(debounce_pkt )))) | |||
2543 | return; | |||
2544 | if ((sc->packet[0] & 0x0c) != 0x04 && | |||
2545 | (sc->packet[3] & 0xcf) != 0x02) { | |||
2546 | /* not the head packet -- ignore */ | |||
2547 | return; | |||
2548 | } | |||
2549 | } | |||
2550 | } | |||
2551 | ||||
2552 | /* Prevent jumping cursor if pad isn't touched or reports garbage. */ | |||
2553 | if (w == 0 || | |||
2554 | ((x == 0 || y == 0 || x == elantech->max_x || y == elantech->max_y) | |||
2555 | && (x != elantech->old_x || y != elantech->old_y))) { | |||
2556 | x = elantech->old_x; | |||
2557 | y = elantech->old_y; | |||
2558 | } | |||
2559 | ||||
2560 | if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE0x01) | |||
2561 | z = (sc->packet[1] & 0xf0) | ((sc->packet[4] & 0xf0) >> 4); | |||
2562 | else if (w) | |||
2563 | z = SYNAPTICS_PRESSURE30; | |||
2564 | ||||
2565 | WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w)do { wsmouse_buttons((sc->sc_wsmousedev), (buttons)); wsmouse_position ((sc->sc_wsmousedev), (x), (y)); wsmouse_touch((sc->sc_wsmousedev ), (z), (w)); wsmouse_input_sync(sc->sc_wsmousedev); } while (0); | |||
2566 | elantech->old_x = x; | |||
2567 | elantech->old_y = y; | |||
2568 | } | |||
2569 | ||||
2570 | void | |||
2571 | pms_proc_elantech_v4(struct pms_softc *sc) | |||
2572 | { | |||
2573 | struct elantech_softc *elantech = sc->elantech; | |||
2574 | struct device *sc_wsmousedev = sc->sc_wsmousedev; | |||
2575 | int id, weight, n, x, y, z; | |||
2576 | u_int buttons, slots; | |||
2577 | ||||
2578 | switch (elantech_packet_type(elantech, sc->packet[3])) { | |||
2579 | case ELANTECH_V4_PKT_STATUS0: | |||
2580 | slots = elantech->mt_slots; | |||
2581 | elantech->mt_slots = sc->packet[1] & 0x1f; | |||
2582 | slots &= ~elantech->mt_slots; | |||
2583 | for (id = 0; slots; id++, slots >>= 1) { | |||
2584 | if (slots & 1) | |||
2585 | wsmouse_mtstate(sc_wsmousedev, id, 0, 0, 0); | |||
2586 | } | |||
2587 | break; | |||
2588 | ||||
2589 | case ELANTECH_V4_PKT_HEAD0x01: | |||
2590 | id = ((sc->packet[3] & 0xe0) >> 5) - 1; | |||
2591 | if (id > -1 && id < ELANTECH_MAX_FINGERS5) { | |||
2592 | x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2]; | |||
2593 | y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5]; | |||
2594 | z = (sc->packet[1] & 0xf0) | |||
2595 | | ((sc->packet[4] & 0xf0) >> 4); | |||
2596 | wsmouse_mtstate(sc_wsmousedev, id, x, y, z); | |||
2597 | } | |||
2598 | break; | |||
2599 | ||||
2600 | case ELANTECH_V4_PKT_MOTION0x02: | |||
2601 | weight = (sc->packet[0] & 0x10) ? ELANTECH_V4_WEIGHT_VALUE5 : 1; | |||
2602 | for (n = 0; n < 6; n += 3) { | |||
2603 | id = ((sc->packet[n] & 0xe0) >> 5) - 1; | |||
2604 | if (id < 0 || id >= ELANTECH_MAX_FINGERS5) | |||
2605 | continue; | |||
2606 | x = weight * (signed char)sc->packet[n + 1]; | |||
2607 | y = weight * (signed char)sc->packet[n + 2]; | |||
2608 | z = WSMOUSE_DEFAULT_PRESSURE45; | |||
2609 | wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_X, x, id); | |||
2610 | wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_Y, y, id); | |||
2611 | wsmouse_set(sc_wsmousedev, WSMOUSE_MT_PRESSURE, z, id); | |||
2612 | } | |||
2613 | break; | |||
2614 | ||||
2615 | case ELANTECH_PKT_TRACKPOINT0x06: | |||
2616 | if (sc->sc_dev_enable & PMS_DEV_SECONDARY0x02) { | |||
2617 | /* | |||
2618 | * This firmware misreport coordinates for trackpoint | |||
2619 | * occasionally. Discard packets outside of [-127, 127] range | |||
2620 | * to prevent cursor jumps. | |||
2621 | */ | |||
2622 | if (sc->packet[4] == 0x80 || sc->packet[5] == 0x80 || | |||
2623 | sc->packet[1] >> 7 == sc->packet[4] >> 7 || | |||
2624 | sc->packet[2] >> 7 == sc->packet[5] >> 7) | |||
2625 | return; | |||
2626 | ||||
2627 | x = sc->packet[4] - 0x100 + (sc->packet[1] << 1); | |||
2628 | y = sc->packet[5] - 0x100 + (sc->packet[2] << 1); | |||
2629 | buttons = butmap[sc->packet[0] & 7]; | |||
2630 | WSMOUSE_INPUT(sc->sc_sec_wsmousedev,do { wsmouse_buttons((sc->sc_sec_wsmousedev), (buttons)); wsmouse_motion ((sc->sc_sec_wsmousedev), (x), (y), (0), (0)); wsmouse_input_sync (sc->sc_sec_wsmousedev); } while (0) | |||
2631 | buttons, x, y, 0, 0)do { wsmouse_buttons((sc->sc_sec_wsmousedev), (buttons)); wsmouse_motion ((sc->sc_sec_wsmousedev), (x), (y), (0), (0)); wsmouse_input_sync (sc->sc_sec_wsmousedev); } while (0); | |||
2632 | } | |||
2633 | return; | |||
2634 | ||||
2635 | default: | |||
2636 | printf("%s: unknown packet type 0x%x\n", DEVNAME(sc)((sc)->sc_dev.dv_xname), | |||
2637 | sc->packet[3] & 0x1f); | |||
2638 | return; | |||
2639 | } | |||
2640 | ||||
2641 | buttons = butmap[sc->packet[0] & 3]; | |||
2642 | wsmouse_buttons(sc_wsmousedev, buttons); | |||
2643 | ||||
2644 | wsmouse_input_sync(sc_wsmousedev); | |||
2645 | } |