1 | /* $NetBSD: pms.c,v 1.35 2011/09/09 14:29:47 jakllsch Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2004 Kentaro Kurahone. |
5 | * Copyright (c) 2004 Ales Krenek. |
6 | * Copyright (c) 1994 Charles M. Hannum. |
7 | * Copyright (c) 1992, 1993 Erik Forsberg. |
8 | * All rights reserved. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED |
17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
19 | * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
22 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
23 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
24 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include <sys/cdefs.h> |
29 | __KERNEL_RCSID(0, "$NetBSD: pms.c,v 1.35 2011/09/09 14:29:47 jakllsch Exp $" ); |
30 | |
31 | #include "opt_pms.h" |
32 | |
33 | #include <sys/param.h> |
34 | #include <sys/systm.h> |
35 | #include <sys/device.h> |
36 | #include <sys/ioctl.h> |
37 | #include <sys/kernel.h> |
38 | #include <sys/kthread.h> |
39 | |
40 | #include <sys/bus.h> |
41 | |
42 | #include <dev/pckbport/pckbportvar.h> |
43 | #ifdef PMS_SYNAPTICS_TOUCHPAD |
44 | #include <dev/pckbport/synapticsvar.h> |
45 | #endif |
46 | #ifdef PMS_ELANTECH_TOUCHPAD |
47 | #include <dev/pckbport/elantechvar.h> |
48 | #endif |
49 | |
50 | #include <dev/pckbport/pmsreg.h> |
51 | #include <dev/pckbport/pmsvar.h> |
52 | |
53 | |
54 | #include <dev/wscons/wsconsio.h> |
55 | #include <dev/wscons/wsmousevar.h> |
56 | |
57 | #ifdef PMSDEBUG |
58 | int pmsdebug = 1; |
59 | #define DPRINTF(x) if (pmsdebug) printf x |
60 | #else |
61 | #define DPRINTF(x) |
62 | #endif |
63 | |
64 | static const enum pms_type tries[] = { |
65 | PMS_SCROLL5, PMS_SCROLL3, PMS_STANDARD, PMS_UNKNOWN |
66 | }; |
67 | |
68 | static const struct pms_protocol pms_protocols[] = { |
69 | { { 0, 0, 0 }, 0, "unknown protocol" }, |
70 | { { 0, 0, 0 }, 0, "no scroll wheel (3 buttons)" }, |
71 | { { 200, 100, 80 }, 3, "scroll wheel (3 buttons)" }, |
72 | { { 200, 200, 80 }, 4, "scroll wheel (5 buttons)" }, |
73 | { { 0, 0, 0 }, 0, "synaptics" }, |
74 | { { 0, 0, 0 }, 0, "elantech" } |
75 | }; |
76 | |
77 | |
78 | static int pmsprobe(device_t, cfdata_t, void *); |
79 | static void pmsattach(device_t, device_t, void *); |
80 | static void pmsinput(void *, int); |
81 | |
82 | CFATTACH_DECL_NEW(pms, sizeof(struct pms_softc), |
83 | pmsprobe, pmsattach, NULL, NULL); |
84 | |
85 | static int pms_protocol(pckbport_tag_t, pckbport_slot_t); |
86 | static void do_enable(struct pms_softc *); |
87 | static void do_disable(struct pms_softc *); |
88 | static void pms_reset_thread(void*); |
89 | static int pms_enable(void *); |
90 | static int pms_ioctl(void *, u_long, void *, int, struct lwp *); |
91 | static void pms_disable(void *); |
92 | |
93 | static bool pms_suspend(device_t, const pmf_qual_t *); |
94 | static bool pms_resume(device_t, const pmf_qual_t *); |
95 | |
96 | static const struct wsmouse_accessops pms_accessops = { |
97 | pms_enable, |
98 | pms_ioctl, |
99 | pms_disable, |
100 | }; |
101 | |
102 | static int |
103 | pms_protocol(pckbport_tag_t tag, pckbport_slot_t slot) |
104 | { |
105 | u_char cmd[2], resp[1]; |
106 | int i, j, res; |
107 | const struct pms_protocol *p; |
108 | |
109 | for (j = 0; j < sizeof(tries) / sizeof(tries[0]); ++j) { |
110 | p = &pms_protocols[tries[j]]; |
111 | if (!p->rates[0]) |
112 | break; |
113 | cmd[0] = PMS_SET_SAMPLE; |
114 | for (i = 0; i < 3; i++) { |
115 | cmd[1] = p->rates[i]; |
116 | res = pckbport_enqueue_cmd(tag, slot, cmd, 2, 0, 1, 0); |
117 | if (res) |
118 | return PMS_STANDARD; |
119 | } |
120 | |
121 | cmd[0] = PMS_SEND_DEV_ID; |
122 | res = pckbport_enqueue_cmd(tag, slot, cmd, 1, 1, 1, resp); |
123 | if (res) |
124 | return PMS_UNKNOWN; |
125 | if (resp[0] == p->response) { |
126 | DPRINTF(("pms_protocol: found mouse protocol %d\n" , |
127 | tries[j])); |
128 | return tries[j]; |
129 | } |
130 | } |
131 | DPRINTF(("pms_protocol: standard PS/2 protocol (no scroll wheel)\n" )); |
132 | return PMS_STANDARD; |
133 | } |
134 | |
135 | int |
136 | pmsprobe(device_t parent, cfdata_t match, void *aux) |
137 | { |
138 | struct pckbport_attach_args *pa = aux; |
139 | u_char cmd[1], resp[2]; |
140 | int res; |
141 | |
142 | if (pa->pa_slot != PCKBPORT_AUX_SLOT) |
143 | return 0; |
144 | |
145 | /* Flush any garbage. */ |
146 | pckbport_flush(pa->pa_tag, pa->pa_slot); |
147 | |
148 | /* reset the device */ |
149 | cmd[0] = PMS_RESET; |
150 | res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); |
151 | if (res) { |
152 | aprint_debug("pmsprobe: reset error %d\n" , res); |
153 | return 0; |
154 | } |
155 | if (resp[0] != PMS_RSTDONE) { |
156 | printf("pmsprobe: reset response 0x%x\n" , resp[0]); |
157 | return 0; |
158 | } |
159 | |
160 | /* get type number (0 = mouse) */ |
161 | if (resp[1] != 0) { |
162 | aprint_debug("pmsprobe: type 0x%x\n" , resp[1]); |
163 | return 0; |
164 | } |
165 | |
166 | return 10; |
167 | } |
168 | |
169 | static void |
170 | pmsattach(device_t parent, device_t self, void *aux) |
171 | { |
172 | struct pms_softc *sc = device_private(self); |
173 | struct pckbport_attach_args *pa = aux; |
174 | struct wsmousedev_attach_args a; |
175 | u_char cmd[2], resp[2]; |
176 | int res; |
177 | |
178 | sc->sc_dev = self; |
179 | sc->sc_kbctag = pa->pa_tag; |
180 | sc->sc_kbcslot = pa->pa_slot; |
181 | |
182 | aprint_naive("\n" ); |
183 | aprint_normal("\n" ); |
184 | |
185 | /* Flush any garbage. */ |
186 | pckbport_flush(pa->pa_tag, pa->pa_slot); |
187 | |
188 | /* reset the device */ |
189 | cmd[0] = PMS_RESET; |
190 | res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); |
191 | if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) { |
192 | aprint_debug("pmsattach: reset error\n" ); |
193 | return; |
194 | } |
195 | sc->inputstate = 0; |
196 | sc->buttons = 0; |
197 | sc->protocol = PMS_UNKNOWN; |
198 | |
199 | #ifdef PMS_SYNAPTICS_TOUCHPAD |
200 | /* Probe for synaptics touchpad. */ |
201 | if (pms_synaptics_probe_init(sc) == 0) { |
202 | sc->protocol = PMS_SYNAPTICS; |
203 | } else |
204 | #endif |
205 | #ifdef PMS_ELANTECH_TOUCHPAD |
206 | if (pms_elantech_probe_init(sc) == 0) { |
207 | sc->protocol = PMS_ELANTECH; |
208 | } else |
209 | #endif |
210 | /* Install generic handler. */ |
211 | pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot, |
212 | pmsinput, sc, device_xname(sc->sc_dev)); |
213 | |
214 | a.accessops = &pms_accessops; |
215 | a.accesscookie = sc; |
216 | |
217 | /* |
218 | * Attach the wsmouse, saving a handle to it. |
219 | * Note that we don't need to check this pointer against NULL |
220 | * here or in pmsintr, because if this fails pms_enable() will |
221 | * never be called, so pmsinput() will never be called. |
222 | */ |
223 | sc->sc_wsmousedev = config_found_ia(self, "wsmousedev" , &a, wsmousedevprint); |
224 | |
225 | /* no interrupts until enabled */ |
226 | cmd[0] = PMS_DEV_DISABLE; |
227 | res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 0, NULL, 0); |
228 | if (res) |
229 | aprint_error("pmsattach: disable error\n" ); |
230 | pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0); |
231 | |
232 | kthread_create(PRI_NONE, 0, NULL, pms_reset_thread, sc, |
233 | &sc->sc_event_thread, "%s" , device_xname(sc->sc_dev)); |
234 | |
235 | if (!pmf_device_register(self, pms_suspend, pms_resume)) |
236 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
237 | } |
238 | |
239 | static void |
240 | do_enable(struct pms_softc *sc) |
241 | { |
242 | u_char cmd[2]; |
243 | int res; |
244 | |
245 | sc->inputstate = 0; |
246 | sc->buttons = 0; |
247 | |
248 | pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1); |
249 | |
250 | #ifdef PMS_SYNAPTICS_TOUCHPAD |
251 | if (sc->protocol == PMS_SYNAPTICS) |
252 | pms_synaptics_enable(sc); |
253 | #endif |
254 | #ifdef PMS_ELANTECH_TOUCHPAD |
255 | if (sc->protocol == PMS_ELANTECH) |
256 | pms_elantech_enable(sc); |
257 | #endif |
258 | |
259 | cmd[0] = PMS_DEV_ENABLE; |
260 | res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, |
261 | 1, 0, 1, 0); |
262 | if (res) |
263 | aprint_error("pms_enable: command error %d\n" , res); |
264 | |
265 | if (sc->protocol == PMS_UNKNOWN) |
266 | sc->protocol = pms_protocol(sc->sc_kbctag, sc->sc_kbcslot); |
267 | DPRINTF(("pms_enable: using %s protocol\n" , |
268 | pms_protocols[sc->protocol].name)); |
269 | #if 0 |
270 | { |
271 | u_char scmd[2]; |
272 | |
273 | scmd[0] = PMS_SET_RES; |
274 | scmd[1] = 3; /* 8 counts/mm */ |
275 | res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, |
276 | 2, 0, 1, 0); |
277 | if (res) |
278 | printf("pms_enable: setup error1 (%d)\n" , res); |
279 | |
280 | scmd[0] = PMS_SET_SCALE21; |
281 | res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, |
282 | 1, 0, 1, 0); |
283 | if (res) |
284 | printf("pms_enable: setup error2 (%d)\n" , res); |
285 | |
286 | scmd[0] = PMS_SET_SAMPLE; |
287 | scmd[1] = 100; /* 100 samples/sec */ |
288 | res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, |
289 | 2, 0, 1, 0); |
290 | if (res) |
291 | printf("pms_enable: setup error3 (%d)\n" , res); |
292 | } |
293 | #endif |
294 | } |
295 | |
296 | static void |
297 | do_disable(struct pms_softc *sc) |
298 | { |
299 | u_char cmd[1]; |
300 | int res; |
301 | |
302 | cmd[0] = PMS_DEV_DISABLE; |
303 | res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, |
304 | 1, 0, 1, 0); |
305 | if (res) |
306 | aprint_error("pms_disable: command error\n" ); |
307 | |
308 | pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0); |
309 | } |
310 | |
311 | static int |
312 | pms_enable(void *v) |
313 | { |
314 | struct pms_softc *sc = v; |
315 | int s; |
316 | |
317 | if (sc->sc_enabled) |
318 | return EBUSY; |
319 | |
320 | do_enable(sc); |
321 | |
322 | s = spltty(); |
323 | sc->sc_enabled = 1; |
324 | splx(s); |
325 | |
326 | return 0; |
327 | } |
328 | |
329 | static void |
330 | pms_disable(void *v) |
331 | { |
332 | struct pms_softc *sc = v; |
333 | int s; |
334 | |
335 | do_disable(sc); |
336 | |
337 | s = spltty(); |
338 | sc->sc_enabled = 0; |
339 | splx(s); |
340 | } |
341 | |
342 | static bool |
343 | pms_suspend(device_t dv, const pmf_qual_t *qual) |
344 | { |
345 | struct pms_softc *sc = device_private(dv); |
346 | |
347 | if (sc->sc_enabled) |
348 | do_disable(sc); |
349 | |
350 | return true; |
351 | } |
352 | |
353 | static bool |
354 | pms_resume(device_t dv, const pmf_qual_t *qual) |
355 | { |
356 | struct pms_softc *sc = device_private(dv); |
357 | |
358 | #ifdef PMS_SYNAPTICS_TOUCHPAD |
359 | if (sc->protocol == PMS_SYNAPTICS) { |
360 | pms_synaptics_resume(sc); |
361 | if (sc->sc_enabled) { |
362 | do_enable(sc); |
363 | } |
364 | } else |
365 | #endif |
366 | #ifdef PMS_ELANTECH_TOUCHPAD |
367 | if (sc->protocol == PMS_ELANTECH) { |
368 | pms_elantech_resume(sc); |
369 | if (sc->sc_enabled) { |
370 | do_enable(sc); |
371 | } |
372 | } else |
373 | #endif |
374 | if (sc->sc_enabled) { |
375 | /* recheck protocol & init mouse */ |
376 | sc->protocol = PMS_UNKNOWN; |
377 | do_enable(sc); /* only if we were suspended */ |
378 | } |
379 | |
380 | return true; |
381 | } |
382 | |
383 | static int |
384 | pms_ioctl(void *v, u_long cmd, void *data, int flag, |
385 | struct lwp *l) |
386 | { |
387 | struct pms_softc *sc = v; |
388 | u_char kbcmd[2]; |
389 | int i; |
390 | |
391 | switch (cmd) { |
392 | case WSMOUSEIO_GTYPE: |
393 | *(u_int *)data = WSMOUSE_TYPE_PS2; |
394 | break; |
395 | |
396 | case WSMOUSEIO_SRES: |
397 | i = (*(u_int *)data - 12) / 25; |
398 | |
399 | if (i < 0) |
400 | i = 0; |
401 | |
402 | if (i > 3) |
403 | i = 3; |
404 | |
405 | kbcmd[0] = PMS_SET_RES; |
406 | kbcmd[1] = i; |
407 | i = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, kbcmd, |
408 | 2, 0, 1, 0); |
409 | |
410 | if (i) |
411 | printf("pms_ioctl: SET_RES command error\n" ); |
412 | break; |
413 | |
414 | default: |
415 | return EPASSTHROUGH; |
416 | } |
417 | return 0; |
418 | } |
419 | |
420 | static void |
421 | pms_reset_thread(void *arg) |
422 | { |
423 | struct pms_softc *sc = arg; |
424 | u_char cmd[1], resp[2]; |
425 | int res; |
426 | int save_protocol; |
427 | |
428 | for (;;) { |
429 | tsleep(&sc->sc_enabled, PWAIT, "pmsreset" , 0); |
430 | #ifdef PMSDEBUG |
431 | if (pmsdebug) |
432 | #endif |
433 | #if defined(PMSDEBUG) || defined(DIAGNOSTIC) |
434 | aprint_debug_dev(sc->sc_dev, |
435 | "resetting mouse interface\n" ); |
436 | #endif |
437 | save_protocol = sc->protocol; |
438 | pms_disable(sc); |
439 | cmd[0] = PMS_RESET; |
440 | res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, |
441 | 1, 2, 1, resp); |
442 | if (res) { |
443 | DPRINTF(("%s: reset error %d\n" , |
444 | device_xname(sc->sc_dev), res)); |
445 | } |
446 | |
447 | /* For the synaptics and elantech case, leave the protocol alone. */ |
448 | if (sc->protocol != PMS_SYNAPTICS && sc->protocol != PMS_ELANTECH) |
449 | sc->protocol = PMS_UNKNOWN; |
450 | |
451 | pms_enable(sc); |
452 | if (sc->protocol != save_protocol) { |
453 | #if defined(PMSDEBUG) || defined(DIAGNOSTIC) |
454 | aprint_verbose_dev(sc->sc_dev, |
455 | "protocol change, sleeping and retrying\n" ); |
456 | #endif |
457 | pms_disable(sc); |
458 | cmd[0] = PMS_RESET; |
459 | res = pckbport_enqueue_cmd(sc->sc_kbctag, |
460 | sc->sc_kbcslot, cmd, 1, 2, 1, resp); |
461 | if (res) { |
462 | DPRINTF(("%s: reset error %d\n" , |
463 | device_xname(sc->sc_dev), res)); |
464 | } |
465 | tsleep(pms_reset_thread, PWAIT, "pmsreset" , hz); |
466 | cmd[0] = PMS_RESET; |
467 | res = pckbport_enqueue_cmd(sc->sc_kbctag, |
468 | sc->sc_kbcslot, cmd, 1, 2, 1, resp); |
469 | if (res) { |
470 | DPRINTF(("%s: reset error %d\n" , |
471 | device_xname(sc->sc_dev), res)); |
472 | } |
473 | sc->protocol = PMS_UNKNOWN; /* reprobe protocol */ |
474 | pms_enable(sc); |
475 | #if defined(PMSDEBUG) || defined(DIAGNOSTIC) |
476 | if (sc->protocol != save_protocol) { |
477 | printf("%s: protocol changed.\n" , |
478 | device_xname(sc->sc_dev)); |
479 | } |
480 | #endif |
481 | } |
482 | } |
483 | } |
484 | |
485 | /* Masks for the first byte of a packet */ |
486 | #define PMS_LBUTMASK 0x01 |
487 | #define PMS_RBUTMASK 0x02 |
488 | #define PMS_MBUTMASK 0x04 |
489 | #define PMS_4BUTMASK 0x10 |
490 | #define PMS_5BUTMASK 0x20 |
491 | |
492 | static void |
493 | pmsinput(void *vsc, int data) |
494 | { |
495 | struct pms_softc *sc = vsc; |
496 | u_int changed; |
497 | int dx, dy, dz = 0; |
498 | int newbuttons = 0; |
499 | |
500 | if (!sc->sc_enabled) { |
501 | /* Interrupts are not expected. Discard the byte. */ |
502 | return; |
503 | } |
504 | |
505 | getmicrouptime(&sc->current); |
506 | |
507 | if (sc->inputstate > 0) { |
508 | struct timeval diff; |
509 | |
510 | timersub(&sc->current, &sc->last, &diff); |
511 | /* |
512 | * Empirically, the delay should be about 1700us on a standard |
513 | * PS/2 port. I have seen delays as large as 4500us (rarely) |
514 | * in regular use. When using a confused mouse, I generally |
515 | * see delays at least as large as 30,000us. -seebs |
516 | * |
517 | * The thinkpad trackball returns at 22-23ms. So we use |
518 | * >= 40ms. In the future, I'll implement adaptable timeout |
519 | * by increasing the timeout if the mouse reset happens |
520 | * too frequently -christos |
521 | */ |
522 | if (diff.tv_sec > 0 || diff.tv_usec >= 40000) { |
523 | DPRINTF(("pms_input: unusual delay (%ld.%06ld s), " |
524 | "scheduling reset\n" , |
525 | (long)diff.tv_sec, (long)diff.tv_usec)); |
526 | sc->inputstate = 0; |
527 | sc->sc_enabled = 0; |
528 | wakeup(&sc->sc_enabled); |
529 | return; |
530 | } |
531 | } |
532 | sc->last = sc->current; |
533 | |
534 | if (sc->inputstate == 0) { |
535 | /* |
536 | * Some devices (seen on trackballs anytime, and on |
537 | * some mice shortly after reset) output garbage bytes |
538 | * between packets. Just ignore them. |
539 | */ |
540 | if ((data & 0xc0) != 0) |
541 | return; /* not in sync yet, discard input */ |
542 | } |
543 | |
544 | sc->packet[sc->inputstate++] = data & 0xff; |
545 | switch (sc->inputstate) { |
546 | case 0: |
547 | /* no useful processing can be done yet */ |
548 | break; |
549 | |
550 | case 1: |
551 | /* |
552 | * Why should we test for bit 0x8 and insist on it here? |
553 | * The old (psm.c and psm_intelli.c) drivers didn't do |
554 | * it, and there are devices where it does harm (that's |
555 | * why it is not used if using PMS_STANDARD protocol). |
556 | * Anyway, it does not to cause any harm to accept packets |
557 | * without this bit. |
558 | */ |
559 | #if 0 |
560 | if (sc->protocol == PMS_STANDARD) |
561 | break; |
562 | if (!(sc->packet[0] & 0x8)) { |
563 | DPRINTF(("pmsinput: 0x8 not set in first byte " |
564 | "[0x%02x], resetting\n" , sc->packet[0])); |
565 | sc->inputstate = 0; |
566 | sc->sc_enabled = 0; |
567 | wakeup(&sc->sc_enabled); |
568 | return; |
569 | } |
570 | #endif |
571 | break; |
572 | |
573 | case 2: |
574 | break; |
575 | |
576 | case 4: |
577 | /* Case 4 is a superset of case 3. This is *not* an accident. */ |
578 | if (sc->protocol == PMS_SCROLL3) { |
579 | dz = sc->packet[3]; |
580 | if (dz >= 128) |
581 | dz -= 256; |
582 | if (dz == -128) |
583 | dz = -127; |
584 | } else if (sc->protocol == PMS_SCROLL5) { |
585 | dz = sc->packet[3] & 0xf; |
586 | if (dz >= 8) |
587 | dz -= 16; |
588 | if (sc->packet[3] & PMS_4BUTMASK) |
589 | newbuttons |= 0x8; |
590 | if (sc->packet[3] & PMS_5BUTMASK) |
591 | newbuttons |= 0x10; |
592 | } else { |
593 | DPRINTF(("pmsinput: why am I looking at this byte?\n" )); |
594 | dz = 0; |
595 | } |
596 | /* FALLTHROUGH */ |
597 | case 3: |
598 | /* |
599 | * This is only an endpoint for scroll protocols with 4 |
600 | * bytes, or the standard protocol with 3. |
601 | */ |
602 | if (sc->protocol != PMS_STANDARD && sc->inputstate == 3) |
603 | break; |
604 | |
605 | newbuttons |= ((sc->packet[0] & PMS_LBUTMASK) ? 0x1 : 0) | |
606 | ((sc->packet[0] & PMS_MBUTMASK) ? 0x2 : 0) | |
607 | ((sc->packet[0] & PMS_RBUTMASK) ? 0x4 : 0); |
608 | |
609 | dx = sc->packet[1]; |
610 | if (dx >= 128) |
611 | dx -= 256; |
612 | if (dx == -128) |
613 | dx = -127; |
614 | |
615 | dy = sc->packet[2]; |
616 | if (dy >= 128) |
617 | dy -= 256; |
618 | if (dy == -128) |
619 | dy = -127; |
620 | |
621 | sc->inputstate = 0; |
622 | changed = (sc->buttons ^ newbuttons); |
623 | sc->buttons = newbuttons; |
624 | |
625 | #ifdef PMSDEBUG |
626 | if (sc->protocol == PMS_STANDARD) { |
627 | DPRINTF(("pms: packet: 0x%02x%02x%02x\n" , |
628 | sc->packet[0], sc->packet[1], sc->packet[2])); |
629 | } else { |
630 | DPRINTF(("pms: packet: 0x%02x%02x%02x%02x\n" , |
631 | sc->packet[0], sc->packet[1], sc->packet[2], |
632 | sc->packet[3])); |
633 | } |
634 | #endif |
635 | if (dx || dy || dz || changed) { |
636 | #ifdef PMSDEBUG |
637 | DPRINTF(("pms: x %+03d y %+03d z %+03d " |
638 | "buttons 0x%02x\n" , dx, dy, dz, sc->buttons)); |
639 | #endif |
640 | wsmouse_input(sc->sc_wsmousedev, |
641 | sc->buttons, dx, dy, dz, 0, |
642 | WSMOUSE_INPUT_DELTA); |
643 | } |
644 | memset(sc->packet, 0, 4); |
645 | break; |
646 | |
647 | /* If we get here, we have problems. */ |
648 | default: |
649 | printf("pmsinput: very confused. resetting.\n" ); |
650 | sc->inputstate = 0; |
651 | sc->sc_enabled = 0; |
652 | wakeup(&sc->sc_enabled); |
653 | return; |
654 | } |
655 | } |
656 | |
657 | int |
658 | pms_sliced_command(pckbport_tag_t tag, pckbport_slot_t slot, u_char scmd) |
659 | { |
660 | u_char cmd[2]; |
661 | int i, err, ret = 0; |
662 | |
663 | cmd[0] = PMS_SET_SCALE11; |
664 | ret = pckbport_poll_cmd(tag, slot, cmd, 1, 0, NULL, 0); |
665 | |
666 | /* |
667 | * Need to send 4 Set Resolution commands, with the argument |
668 | * encoded in the bottom most 2 bits. |
669 | */ |
670 | for (i = 6; i >= 0; i -= 2) { |
671 | cmd[0] = PMS_SET_RES; |
672 | cmd[1] = (scmd >> i) & 3; |
673 | err = pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0); |
674 | if (ret == 0 && err != 0) { |
675 | ret = err; |
676 | } |
677 | } |
678 | |
679 | return ret; |
680 | } |
681 | |