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