pms.c revision 1.17 1 /* $NetBSD: pms.c,v 1.17 2007/03/04 06:02:27 christos 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 "opt_pms.h"
29
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: pms.c,v 1.17 2007/03/04 06:02:27 christos Exp $");
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 <machine/bus.h>
41
42 #include <dev/pckbport/pckbportvar.h>
43 #ifdef PMS_SYNAPTICS_TOUCHPAD
44 #include <dev/pckbport/synapticsvar.h>
45 #endif
46
47 #include <dev/pckbport/pmsreg.h>
48 #include <dev/pckbport/pmsvar.h>
49
50
51 #include <dev/wscons/wsconsio.h>
52 #include <dev/wscons/wsmousevar.h>
53
54 #ifdef PMSDEBUG
55 int pmsdebug = 1;
56 #define DPRINTF(x) if (pmsdebug) printf x
57 #else
58 #define DPRINTF(x)
59 #endif
60
61 const enum pms_type tries[] = {
62 PMS_SCROLL5, PMS_SCROLL3, PMS_STANDARD, PMS_UNKNOWN
63 };
64
65 const struct pms_protocol pms_protocols[] = {
66 { { 0, 0, 0 }, 0, "unknown protocol" },
67 { { 0, 0, 0 }, 0, "no scroll wheel (3 buttons)" },
68 { { 200, 100, 80 }, 3, "scroll wheel (3 buttons)" },
69 { { 200, 200, 80 }, 4, "scroll wheel (5 buttons)" },
70 { { 0, 0, 0 }, 0, "synaptics" }
71 };
72
73
74 int pmsprobe(struct device *, struct cfdata *, void *);
75 void pmsattach(struct device *, struct device *, void *);
76 void pmsinput(void *, int);
77
78 CFATTACH_DECL(pms, sizeof(struct pms_softc),
79 pmsprobe, pmsattach, NULL, NULL);
80
81 static int pms_protocol(pckbport_tag_t, pckbport_slot_t);
82 static void do_enable(struct pms_softc *);
83 static void do_disable(struct pms_softc *);
84 static void pms_reset_thread(void*);
85 static void pms_spawn_reset_thread(void*);
86 int pms_enable(void *);
87 int pms_ioctl(void *, u_long, void *, int, struct lwp *);
88 void pms_disable(void *);
89 #ifndef PMS_DISABLE_POWERHOOK
90 void pms_power(int, void *);
91 #endif /* !PMS_DISABLE_POWERHOOK */
92
93 const struct wsmouse_accessops pms_accessops = {
94 pms_enable,
95 pms_ioctl,
96 pms_disable,
97 };
98
99 static int
100 pms_protocol(pckbport_tag_t tag, pckbport_slot_t slot)
101 {
102 u_char cmd[2], resp[1];
103 int i, j, res;
104 const struct pms_protocol *p;
105
106 for (j = 0; j < sizeof(tries) / sizeof(tries[0]); ++j) {
107 p = &pms_protocols[tries[j]];
108 if (!p->rates[0])
109 break;
110 cmd[0] = PMS_SET_SAMPLE;
111 for (i = 0; i < 3; i++) {
112 cmd[1] = p->rates[i];
113 res = pckbport_enqueue_cmd(tag, slot, cmd, 2, 0, 1, 0);
114 if (res)
115 return PMS_STANDARD;
116 }
117
118 cmd[0] = PMS_SEND_DEV_ID;
119 res = pckbport_enqueue_cmd(tag, slot, cmd, 1, 1, 1, resp);
120 if (res)
121 return PMS_UNKNOWN;
122 if (resp[0] == p->response) {
123 DPRINTF(("pms_protocol: found mouse protocol %d\n",
124 tries[j]));
125 return tries[j];
126 }
127 }
128 DPRINTF(("pms_protocol: standard PS/2 protocol (no scroll wheel)\n"));
129 return PMS_STANDARD;
130 }
131
132 int
133 pmsprobe(struct device *parent, struct cfdata *match,
134 void *aux)
135 {
136 struct pckbport_attach_args *pa = aux;
137 u_char cmd[1], resp[2];
138 int res;
139
140 if (pa->pa_slot != PCKBPORT_AUX_SLOT)
141 return 0;
142
143 /* Flush any garbage. */
144 pckbport_flush(pa->pa_tag, pa->pa_slot);
145
146 /* reset the device */
147 cmd[0] = PMS_RESET;
148 res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
149 if (res) {
150 #ifdef DEBUG
151 printf("pmsprobe: reset error %d\n", res);
152 #endif
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 #ifdef DEBUG
163 printf("pmsprobe: type 0x%x\n", resp[1]);
164 #endif
165 return 0;
166 }
167
168 return 10;
169 }
170
171 void
172 pmsattach(struct device *parent, struct device *self, void *aux)
173 {
174 struct pms_softc *sc = device_private(self);
175 struct pckbport_attach_args *pa = aux;
176 struct wsmousedev_attach_args a;
177 u_char cmd[2], resp[2];
178 int res;
179
180 sc->sc_kbctag = pa->pa_tag;
181 sc->sc_kbcslot = pa->pa_slot;
182
183 printf("\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 #ifdef DEBUG
192 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
193 printf("pmsattach: reset error\n");
194 return;
195 }
196 #endif
197 sc->inputstate = 0;
198 sc->buttons = 0;
199 sc->protocol = PMS_UNKNOWN;
200
201 #ifdef PMS_SYNAPTICS_TOUCHPAD
202 /* Probe for synaptics touchpad. */
203 if (pms_synaptics_probe_init(sc) == 0) {
204 sc->protocol = PMS_SYNAPTICS;
205 } else
206 #endif
207 /* Install generic handler. */
208 pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
209 pmsinput, sc, sc->sc_dev.dv_xname);
210
211 a.accessops = &pms_accessops;
212 a.accesscookie = sc;
213
214 /*
215 * Attach the wsmouse, saving a handle to it.
216 * Note that we don't need to check this pointer against NULL
217 * here or in pmsintr, because if this fails pms_enable() will
218 * never be called, so pmsinput() will never be called.
219 */
220 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
221
222 /* no interrupts until enabled */
223 cmd[0] = PMS_DEV_DISABLE;
224 res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 0, 0, 0);
225 if (res)
226 printf("pmsattach: disable error\n");
227 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
228
229 kthread_create(pms_spawn_reset_thread, sc);
230
231 #ifndef PMS_DISABLE_POWERHOOK
232 sc->sc_powerhook = powerhook_establish(self->dv_xname, pms_power, sc);
233 sc->sc_suspended = 0;
234 #endif /* !PMS_DISABLE_POWERHOOK */
235 }
236
237 static void
238 do_enable(struct pms_softc *sc)
239 {
240 u_char cmd[2];
241 int res;
242
243 sc->inputstate = 0;
244 sc->buttons = 0;
245
246 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1);
247
248 #ifdef PMS_SYNAPTICS_TOUCHPAD
249 if (sc->protocol == PMS_SYNAPTICS)
250 pms_synaptics_enable(sc);
251 #endif
252
253 cmd[0] = PMS_DEV_ENABLE;
254 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
255 1, 0, 1, 0);
256 if (res)
257 printf("pms_enable: command error %d\n", res);
258
259 if (sc->protocol == PMS_UNKNOWN)
260 sc->protocol = pms_protocol(sc->sc_kbctag, sc->sc_kbcslot);
261 DPRINTF(("pms_enable: using %s protocol\n",
262 pms_protocols[sc->protocol].name));
263 #if 0
264 {
265 u_char scmd[2];
266
267 scmd[0] = PMS_SET_RES;
268 scmd[1] = 3; /* 8 counts/mm */
269 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
270 2, 0, 1, 0);
271 if (res)
272 printf("pms_enable: setup error1 (%d)\n", res);
273
274 scmd[0] = PMS_SET_SCALE21;
275 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
276 1, 0, 1, 0);
277 if (res)
278 printf("pms_enable: setup error2 (%d)\n", res);
279
280 scmd[0] = PMS_SET_SAMPLE;
281 scmd[1] = 100; /* 100 samples/sec */
282 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
283 2, 0, 1, 0);
284 if (res)
285 printf("pms_enable: setup error3 (%d)\n", res);
286 }
287 #endif
288 }
289
290 static void
291 do_disable(struct pms_softc *sc)
292 {
293 u_char cmd[1];
294 int res;
295
296 cmd[0] = PMS_DEV_DISABLE;
297 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
298 1, 0, 1, 0);
299 if (res)
300 printf("pms_disable: command error\n");
301
302 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
303 }
304
305 int
306 pms_enable(void *v)
307 {
308 struct pms_softc *sc = v;
309 int s;
310
311 if (sc->sc_enabled)
312 return EBUSY;
313
314 do_enable(sc);
315
316 s = spltty();
317 sc->sc_enabled = 1;
318 splx(s);
319
320 return 0;
321 }
322
323 void
324 pms_disable(void *v)
325 {
326 struct pms_softc *sc = v;
327 int s;
328
329 do_disable(sc);
330
331 s = spltty();
332 sc->sc_enabled = 0;
333 splx(s);
334 }
335
336 #ifndef PMS_DISABLE_POWERHOOK
337 void
338 pms_power(int why, void *v)
339 {
340 struct pms_softc *sc = v;
341
342 switch (why) {
343 case PWR_STANDBY:
344 break;
345 case PWR_SUSPEND:
346 if (sc->sc_enabled) {
347 do_disable(sc);
348 sc->sc_suspended = 1;
349 }
350 break;
351 case PWR_RESUME:
352 #ifdef PMS_SYNAPTICS_TOUCHPAD
353 if (sc->protocol == PMS_SYNAPTICS) {
354 pms_synaptics_resume(sc);
355 sc->sc_suspended = 0;
356 do_enable(sc);
357 }
358 #endif
359 if (sc->sc_enabled && sc->sc_suspended) {
360 /* recheck protocol & init mouse */
361 sc->protocol = PMS_UNKNOWN;
362 sc->sc_suspended = 0;
363 do_enable(sc); /* only if we were suspended */
364 }
365 break;
366 case PWR_SOFTSUSPEND:
367 case PWR_SOFTSTANDBY:
368 case PWR_SOFTRESUME:
369 break;
370 }
371 }
372 #endif /* !PMS_DISABLE_POWERHOOK */
373
374 int
375 pms_ioctl(void *v, u_long cmd, void *data, int flag,
376 struct lwp *l)
377 {
378 struct pms_softc *sc = v;
379 u_char kbcmd[2];
380 int i;
381
382 switch (cmd) {
383 case WSMOUSEIO_GTYPE:
384 *(u_int *)data = WSMOUSE_TYPE_PS2;
385 break;
386
387 case WSMOUSEIO_SRES:
388 i = (*(u_int *)data - 12) / 25;
389
390 if (i < 0)
391 i = 0;
392
393 if (i > 3)
394 i = 3;
395
396 kbcmd[0] = PMS_SET_RES;
397 kbcmd[1] = i;
398 i = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, kbcmd,
399 2, 0, 1, 0);
400
401 if (i)
402 printf("pms_ioctl: SET_RES command error\n");
403 break;
404
405 default:
406 return EPASSTHROUGH;
407 }
408 return 0;
409 }
410
411 static void
412 pms_spawn_reset_thread(void *arg)
413 {
414 struct pms_softc *sc = arg;
415
416 kthread_create1(pms_reset_thread, sc, &sc->sc_event_thread,
417 sc->sc_dev.dv_xname);
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 printf("%s: resetting mouse interface\n",
435 sc->sc_dev.dv_xname);
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", sc->sc_dev.dv_xname,
444 res));
445 }
446
447 #ifdef PMS_SYNAPTICS_TOUCHPAD
448 /* For the synaptics case, leave the protocol alone. */
449 if (sc->protocol != PMS_SYNAPTICS)
450 #endif
451 sc->protocol = PMS_UNKNOWN;
452
453 pms_enable(sc);
454 if (sc->protocol != save_protocol) {
455 #if defined(PMSDEBUG) || defined(DIAGNOSTIC)
456 printf("%s: protocol change, sleeping and retrying\n",
457 sc->sc_dev.dv_xname);
458 #endif
459 pms_disable(sc);
460 cmd[0] = PMS_RESET;
461 res = pckbport_enqueue_cmd(sc->sc_kbctag,
462 sc->sc_kbcslot, cmd, 1, 2, 1, resp);
463 if (res) {
464 DPRINTF(("%s: reset error %d\n",
465 sc->sc_dev.dv_xname, res));
466 }
467 tsleep(pms_reset_thread, PWAIT, "pmsreset", hz);
468 cmd[0] = PMS_RESET;
469 res = pckbport_enqueue_cmd(sc->sc_kbctag,
470 sc->sc_kbcslot, cmd, 1, 2, 1, resp);
471 if (res) {
472 DPRINTF(("%s: reset error %d\n",
473 sc->sc_dev.dv_xname, res));
474 }
475 sc->protocol = PMS_UNKNOWN; /* reprobe protocol */
476 pms_enable(sc);
477 #if defined(PMSDEBUG) || defined(DIAGNOSTIC)
478 if (sc->protocol != save_protocol) {
479 printf("%s: protocol changed.\n",
480 sc->sc_dev.dv_xname);
481 }
482 #endif
483 }
484 }
485 }
486
487 /* Masks for the first byte of a packet */
488 #define PMS_LBUTMASK 0x01
489 #define PMS_RBUTMASK 0x02
490 #define PMS_MBUTMASK 0x04
491 #define PMS_4BUTMASK 0x10
492 #define PMS_5BUTMASK 0x20
493
494 void
495 pmsinput(void *vsc, int data)
496 {
497 struct pms_softc *sc = vsc;
498 u_int changed;
499 int dx, dy, dz = 0;
500 int newbuttons = 0;
501
502 if (!sc->sc_enabled) {
503 /* Interrupts are not expected. Discard the byte. */
504 return;
505 }
506
507 getmicrouptime(&sc->current);
508
509 if (sc->inputstate > 0) {
510 struct timeval diff;
511
512 timersub(&sc->current, &sc->last, &diff);
513 /*
514 * Empirically, the delay should be about 1700us on a standard
515 * PS/2 port. I have seen delays as large as 4500us (rarely)
516 * in regular use. When using a confused mouse, I generally
517 * see delays at least as large as 30,000us. -seebs
518 *
519 * The thinkpad trackball returns at 22-23ms. So we use
520 * >= 40ms. In the future, I'll implement adaptable timeout
521 * by increasing the timeout if the mouse reset happens
522 * too frequently -christos
523 */
524 if (diff.tv_sec > 0 || diff.tv_usec >= 40000) {
525 DPRINTF(("pms_input: unusual delay (%ld.%06ld s), "
526 "scheduling reset\n",
527 (long)diff.tv_sec, (long)diff.tv_usec));
528 sc->inputstate = 0;
529 sc->sc_enabled = 0;
530 wakeup(&sc->sc_enabled);
531 return;
532 }
533 }
534 sc->last = sc->current;
535
536 if (sc->inputstate == 0) {
537 /*
538 * Some devices (seen on trackballs anytime, and on
539 * some mice shortly after reset) output garbage bytes
540 * between packets. Just ignore them.
541 */
542 if ((data & 0xc0) != 0)
543 return; /* not in sync yet, discard input */
544 }
545
546 sc->packet[sc->inputstate++] = data & 0xff;
547 switch (sc->inputstate) {
548 case 0:
549 /* no useful processing can be done yet */
550 break;
551
552 case 1:
553 /*
554 * Why should we test for bit 0x8 and insist on it here?
555 * The old (psm.c and psm_intelli.c) drivers didn't do
556 * it, and there are devices where it does harm (that's
557 * why it is not used if using PMS_STANDARD protocol).
558 * Anyway, it does not to cause any harm to accept packets
559 * without this bit.
560 */
561 #if 0
562 if (sc->protocol == PMS_STANDARD)
563 break;
564 if (!(sc->packet[0] & 0x8)) {
565 DPRINTF(("pmsinput: 0x8 not set in first byte "
566 "[0x%02x], resetting\n", sc->packet[0]));
567 sc->inputstate = 0;
568 sc->sc_enabled = 0;
569 wakeup(&sc->sc_enabled);
570 return;
571 }
572 #endif
573 break;
574
575 case 2:
576 break;
577
578 case 4:
579 /* Case 4 is a superset of case 3. This is *not* an accident. */
580 if (sc->protocol == PMS_SCROLL3) {
581 dz = sc->packet[3];
582 if (dz >= 128)
583 dz -= 256;
584 if (dz == -128)
585 dz = -127;
586 } else if (sc->protocol == PMS_SCROLL5) {
587 dz = sc->packet[3] & 0xf;
588 if (dz >= 8)
589 dz -= 16;
590 if (sc->packet[3] & PMS_4BUTMASK)
591 newbuttons |= 0x8;
592 if (sc->packet[3] & PMS_5BUTMASK)
593 newbuttons |= 0x10;
594 } else {
595 DPRINTF(("pmsinput: why am I looking at this byte?\n"));
596 dz = 0;
597 }
598 /* FALLTHROUGH */
599 case 3:
600 /*
601 * This is only an endpoint for scroll protocols with 4
602 * bytes, or the standard protocol with 3.
603 */
604 if (sc->protocol != PMS_STANDARD && sc->inputstate == 3)
605 break;
606
607 newbuttons |= ((sc->packet[0] & PMS_LBUTMASK) ? 0x1 : 0) |
608 ((sc->packet[0] & PMS_MBUTMASK) ? 0x2 : 0) |
609 ((sc->packet[0] & PMS_RBUTMASK) ? 0x4 : 0);
610
611 dx = sc->packet[1];
612 if (dx >= 128)
613 dx -= 256;
614 if (dx == -128)
615 dx = -127;
616
617 dy = sc->packet[2];
618 if (dy >= 128)
619 dy -= 256;
620 if (dy == -128)
621 dy = -127;
622
623 sc->inputstate = 0;
624 changed = (sc->buttons ^ newbuttons);
625 sc->buttons = newbuttons;
626
627 #ifdef PMSDEBUG
628 if (sc->protocol == PMS_STANDARD) {
629 DPRINTF(("pms: packet: 0x%02x%02x%02x\n",
630 sc->packet[0], sc->packet[1], sc->packet[2]));
631 } else {
632 DPRINTF(("pms: packet: 0x%02x%02x%02x%02x\n",
633 sc->packet[0], sc->packet[1], sc->packet[2],
634 sc->packet[3]));
635 }
636 #endif
637 if (dx || dy || dz || changed) {
638 #ifdef PMSDEBUG
639 DPRINTF(("pms: x %+03d y %+03d z %+03d "
640 "buttons 0x%02x\n", dx, dy, dz, sc->buttons));
641 #endif
642 wsmouse_input(sc->sc_wsmousedev,
643 sc->buttons, dx, dy, dz, 0,
644 WSMOUSE_INPUT_DELTA);
645 }
646 memset(sc->packet, 0, 4);
647 break;
648
649 /* If we get here, we have problems. */
650 default:
651 printf("pmsinput: very confused. resetting.\n");
652 sc->inputstate = 0;
653 sc->sc_enabled = 0;
654 wakeup(&sc->sc_enabled);
655 return;
656 }
657 }
658