wsmouse.c revision 1.13 1 /* $NetBSD: wsmouse.c,v 1.13 2001/02/13 01:14:45 bjh21 Exp $ */
2
3 /*
4 * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Christopher G. Demetriou
17 * for the NetBSD Project.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: wsmouse.c,v 1.13 2001/02/13 01:14:45 bjh21 Exp $");
35
36 /*
37 * Copyright (c) 1992, 1993
38 * The Regents of the University of California. All rights reserved.
39 *
40 * This software was developed by the Computer Systems Engineering group
41 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
42 * contributed to Berkeley.
43 *
44 * All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Lawrence Berkeley Laboratory.
48 *
49 * Redistribution and use in source and binary forms, with or without
50 * modification, are permitted provided that the following conditions
51 * are met:
52 * 1. Redistributions of source code must retain the above copyright
53 * notice, this list of conditions and the following disclaimer.
54 * 2. Redistributions in binary form must reproduce the above copyright
55 * notice, this list of conditions and the following disclaimer in the
56 * documentation and/or other materials provided with the distribution.
57 * 3. All advertising materials mentioning features or use of this software
58 * must display the following acknowledgement:
59 * This product includes software developed by the University of
60 * California, Berkeley and its contributors.
61 * 4. Neither the name of the University nor the names of its contributors
62 * may be used to endorse or promote products derived from this software
63 * without specific prior written permission.
64 *
65 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
66 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
67 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
68 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
69 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
70 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
71 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
72 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
73 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
74 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
75 * SUCH DAMAGE.
76 *
77 * @(#)ms.c 8.1 (Berkeley) 6/11/93
78 */
79
80 /*
81 * Mouse driver.
82 */
83
84 #include <sys/param.h>
85 #include <sys/conf.h>
86 #include <sys/ioctl.h>
87 #include <sys/fcntl.h>
88 #include <sys/kernel.h>
89 #include <sys/proc.h>
90 #include <sys/syslog.h>
91 #include <sys/systm.h>
92 #include <sys/tty.h>
93 #include <sys/signalvar.h>
94 #include <sys/device.h>
95 #include <sys/vnode.h>
96
97 #include <dev/wscons/wsconsio.h>
98 #include <dev/wscons/wsmousevar.h>
99 #include <dev/wscons/wseventvar.h>
100
101 #include "wsmouse.h"
102 #include "wsmux.h"
103 #include "wsdisplay.h"
104 #include "wskbd.h"
105
106 #if NWSMUX > 0
107 #include <dev/wscons/wsmuxvar.h>
108 #endif
109
110 #define INVALID_X INT_MAX
111 #define INVALID_Y INT_MAX
112 #define INVALID_Z INT_MAX
113
114 struct wsmouse_softc {
115 struct device sc_dv;
116
117 const struct wsmouse_accessops *sc_accessops;
118 void *sc_accesscookie;
119
120 int sc_ready; /* accepting events */
121 struct wseventvar sc_events; /* event queue state */
122
123 u_int sc_mb; /* mouse button state */
124 u_int sc_ub; /* user button state */
125 int sc_dx; /* delta-x */
126 int sc_dy; /* delta-y */
127 int sc_dz; /* delta-z */
128 int sc_x; /* absolute-x */
129 int sc_y; /* absolute-y */
130 int sc_z; /* absolute-z */
131
132 int sc_refcnt;
133 u_char sc_dying; /* device is being detached */
134
135 #if NWSMUX > 0
136 struct wsmux_softc *sc_mux;
137 #endif
138 };
139
140 int wsmouse_match __P((struct device *, struct cfdata *, void *));
141 void wsmouse_attach __P((struct device *, struct device *, void *));
142 int wsmouse_detach __P((struct device *, int));
143 int wsmouse_activate __P((struct device *, enum devact));
144
145 int wsmouse_do_ioctl __P((struct wsmouse_softc *, u_long, caddr_t,
146 int, struct proc *));
147
148 int wsmousedoclose __P((struct device *, int, int, struct proc *));
149 int wsmousedoioctl __P((struct device *, u_long, caddr_t, int,
150 struct proc *));
151
152 struct cfattach wsmouse_ca = {
153 sizeof (struct wsmouse_softc), wsmouse_match, wsmouse_attach,
154 wsmouse_detach, wsmouse_activate
155 };
156
157 #if NWSMOUSE > 0
158 extern struct cfdriver wsmouse_cd;
159 #endif /* NWSMOUSE > 0 */
160
161 cdev_decl(wsmouse);
162
163 #if NWSMUX > 0
164 struct wsmuxops wsmouse_muxops = {
165 wsmouseopen, wsmousedoclose, wsmousedoioctl, 0, 0
166 };
167 #endif
168
169 /*
170 * Print function (for parent devices).
171 */
172 int
173 wsmousedevprint(aux, pnp)
174 void *aux;
175 const char *pnp;
176 {
177
178 if (pnp)
179 printf("wsmouse at %s", pnp);
180 return (UNCONF);
181 }
182
183 int
184 wsmouse_match(parent, match, aux)
185 struct device *parent;
186 struct cfdata *match;
187 void *aux;
188 {
189 return (1);
190 }
191
192 void
193 wsmouse_attach(parent, self, aux)
194 struct device *parent, *self;
195 void *aux;
196 {
197 struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
198 struct wsmousedev_attach_args *ap = aux;
199 #if NWSMUX > 0
200 int mux;
201 #endif
202
203 sc->sc_accessops = ap->accessops;
204 sc->sc_accesscookie = ap->accesscookie;
205 sc->sc_ready = 0; /* sanity */
206
207 #if NWSMUX > 0
208 mux = sc->sc_dv.dv_cfdata->wsmousedevcf_mux;
209 if (mux != WSMOUSEDEVCF_MUX_DEFAULT) {
210 wsmux_attach(mux, WSMUX_MOUSE, &sc->sc_dv, &sc->sc_events,
211 &sc->sc_mux, &wsmouse_muxops);
212 printf(" mux %d", mux);
213 }
214 #endif
215
216 printf("\n");
217 }
218
219 int
220 wsmouse_activate(self, act)
221 struct device *self;
222 enum devact act;
223 {
224 /* XXX should we do something more? */
225 return (0);
226 }
227
228 /*
229 * Detach a mouse. To keep track of users of the softc we keep
230 * a reference count that's incremented while inside, e.g., read.
231 * If the mouse is active and the reference count is > 0 (0 is the
232 * normal state) we post an event and then wait for the process
233 * that had the reference to wake us up again. Then we blow away the
234 * vnode and return (which will deallocate the softc).
235 */
236 int
237 wsmouse_detach(self, flags)
238 struct device *self;
239 int flags;
240 {
241 struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
242 struct wseventvar *evar;
243 int maj, mn;
244 int s;
245 #if NWSMUX > 0
246 int mux;
247 #endif
248
249 sc->sc_dying = 1;
250
251 #if NWSMUX > 0
252 mux = sc->sc_dv.dv_cfdata->wsmousedevcf_mux;
253 if (mux != WSMOUSEDEVCF_MUX_DEFAULT)
254 wsmux_detach(mux, &sc->sc_dv);
255 #endif
256
257 evar = &sc->sc_events;
258 if (evar->io) {
259 s = spltty();
260 if (--sc->sc_refcnt >= 0) {
261 /* Wake everyone by generating a dummy event. */
262 if (++evar->put >= WSEVENT_QSIZE)
263 evar->put = 0;
264 WSEVENT_WAKEUP(evar);
265 /* Wait for processes to go away. */
266 if (tsleep(sc, PZERO, "wsmdet", hz * 60))
267 printf("wsmouse_detach: %s didn't detach\n",
268 sc->sc_dv.dv_xname);
269 }
270 splx(s);
271 }
272
273 /* locate the major number */
274 for (maj = 0; maj < nchrdev; maj++)
275 if (cdevsw[maj].d_open == wsmouseopen)
276 break;
277
278 /* Nuke the vnodes for any open instances (calls close). */
279 mn = self->dv_unit;
280 vdevgone(maj, mn, mn, VCHR);
281
282 return (0);
283 }
284
285 void
286 wsmouse_input(wsmousedev, btns, x, y, z, flags)
287 struct device *wsmousedev;
288 u_int btns; /* 0 is up */
289 int x, y, z;
290 u_int flags;
291 {
292 struct wsmouse_softc *sc = (struct wsmouse_softc *)wsmousedev;
293 struct wscons_event *ev;
294 struct wseventvar *evar;
295 int mb, ub, d, get, put, any;
296
297 /*
298 * Discard input if not ready.
299 */
300 if (sc->sc_ready == 0)
301 return;
302
303 #if NWSMUX > 0
304 if (sc->sc_mux)
305 evar = &sc->sc_mux->sc_events;
306 else
307 #endif
308 evar = &sc->sc_events;
309
310 sc->sc_mb = btns;
311 if (!(flags & WSMOUSE_INPUT_ABSOLUTE_X))
312 sc->sc_dx += x;
313 if (!(flags & WSMOUSE_INPUT_ABSOLUTE_Y))
314 sc->sc_dy += y;
315 if (!(flags & WSMOUSE_INPUT_ABSOLUTE_Z))
316 sc->sc_dz += z;
317
318 /*
319 * We have at least one event (mouse button, delta-X, or
320 * delta-Y; possibly all three, and possibly three separate
321 * button events). Deliver these events until we are out
322 * of changes or out of room. As events get delivered,
323 * mark them `unchanged'.
324 */
325 ub = sc->sc_ub;
326 any = 0;
327 get = evar->get;
328 put = evar->put;
329 ev = &evar->q[put];
330
331 /* NEXT prepares to put the next event, backing off if necessary */
332 #define NEXT \
333 if ((++put) % WSEVENT_QSIZE == get) { \
334 put--; \
335 goto out; \
336 }
337 /* ADVANCE completes the `put' of the event */
338 #define ADVANCE \
339 ev++; \
340 if (put >= WSEVENT_QSIZE) { \
341 put = 0; \
342 ev = &evar->q[0]; \
343 } \
344 any = 1
345 /* TIMESTAMP sets `time' field of the event to the current time */
346 #define TIMESTAMP \
347 do { \
348 int s; \
349 s = splhigh(); \
350 TIMEVAL_TO_TIMESPEC(&time, &ev->time); \
351 splx(s); \
352 } while (0)
353
354 if (flags & WSMOUSE_INPUT_ABSOLUTE_X) {
355 if (sc->sc_x != x) {
356 NEXT;
357 ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_X;
358 ev->value = x;
359 TIMESTAMP;
360 ADVANCE;
361 sc->sc_x = x;
362 }
363 } else {
364 if (sc->sc_dx) {
365 NEXT;
366 ev->type = WSCONS_EVENT_MOUSE_DELTA_X;
367 ev->value = sc->sc_dx;
368 TIMESTAMP;
369 ADVANCE;
370 sc->sc_dx = 0;
371 }
372 }
373 if (flags & WSMOUSE_INPUT_ABSOLUTE_Y) {
374 if (sc->sc_y != y) {
375 NEXT;
376 ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_Y;
377 ev->value = y;
378 TIMESTAMP;
379 ADVANCE;
380 sc->sc_y = y;
381 }
382 } else {
383 if (sc->sc_dy) {
384 NEXT;
385 ev->type = WSCONS_EVENT_MOUSE_DELTA_Y;
386 ev->value = sc->sc_dy;
387 TIMESTAMP;
388 ADVANCE;
389 sc->sc_dy = 0;
390 }
391 }
392 if (flags & WSMOUSE_INPUT_ABSOLUTE_Z) {
393 if (sc->sc_z != z) {
394 NEXT;
395 ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_Z;
396 ev->value = z;
397 TIMESTAMP;
398 ADVANCE;
399 sc->sc_z = z;
400 }
401 } else {
402 if (sc->sc_dz) {
403 NEXT;
404 ev->type = WSCONS_EVENT_MOUSE_DELTA_Z;
405 ev->value = sc->sc_dz;
406 TIMESTAMP;
407 ADVANCE;
408 sc->sc_dz = 0;
409 }
410 }
411
412 mb = sc->sc_mb;
413 while ((d = mb ^ ub) != 0) {
414 /*
415 * Mouse button change. Find the first change and drop
416 * it into the event queue.
417 */
418 NEXT;
419 ev->value = ffs(d) - 1;
420
421 KASSERT(ev->value >= 0);
422
423 d = 1 << ev->value;
424 ev->type =
425 (mb & d) ? WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
426 TIMESTAMP;
427 ADVANCE;
428 ub ^= d;
429 }
430 out:
431 if (any) {
432 sc->sc_ub = ub;
433 evar->put = put;
434 WSEVENT_WAKEUP(evar);
435 }
436 }
437
438 int
439 wsmouseopen(dev, flags, mode, p)
440 dev_t dev;
441 int flags, mode;
442 struct proc *p;
443 {
444 #if NWSMOUSE > 0
445 struct wsmouse_softc *sc;
446 int error, unit;
447
448 unit = minor(dev);
449 if (unit >= wsmouse_cd.cd_ndevs || /* make sure it was attached */
450 (sc = wsmouse_cd.cd_devs[unit]) == NULL)
451 return (ENXIO);
452
453 if (sc->sc_dying)
454 return (EIO);
455
456 if ((flags & (FREAD | FWRITE)) == FWRITE)
457 return (0); /* always allow open for write
458 so ioctl() is possible. */
459
460 #if NWSMUX > 0
461 if (sc->sc_mux)
462 return (EBUSY);
463 #endif
464
465 if (sc->sc_events.io) /* and that it's not in use */
466 return (EBUSY);
467
468 sc->sc_events.io = p;
469 wsevent_init(&sc->sc_events); /* may cause sleep */
470
471 sc->sc_ready = 1; /* start accepting events */
472 sc->sc_x = INVALID_X;
473 sc->sc_y = INVALID_Y;
474 sc->sc_z = INVALID_Z;
475
476 /* enable the device, and punt if that's not possible */
477 error = (*sc->sc_accessops->enable)(sc->sc_accesscookie);
478 if (error) {
479 sc->sc_ready = 0; /* stop accepting events */
480 wsevent_fini(&sc->sc_events);
481 sc->sc_events.io = NULL;
482 return (error);
483 }
484
485 return (0);
486 #else
487 return (ENXIO);
488 #endif /* NWSMOUSE > 0 */
489 }
490
491 int
492 wsmouseclose(dev, flags, mode, p)
493 dev_t dev;
494 int flags, mode;
495 struct proc *p;
496 {
497 #if NWSMOUSE > 0
498 return (wsmousedoclose(wsmouse_cd.cd_devs[minor(dev)],
499 flags, mode, p));
500 #else
501 return (ENXIO);
502 #endif /* NWSMOUSE > 0 */
503 }
504
505 #if NWSMOUSE > 0
506 int
507 wsmousedoclose(dv, flags, mode, p)
508 struct device *dv;
509 int flags, mode;
510 struct proc *p;
511 {
512 struct wsmouse_softc *sc = (struct wsmouse_softc *)dv;
513
514 if ((flags & (FREAD | FWRITE)) == FWRITE)
515 return (0); /* see wsmouseopen() */
516
517 (*sc->sc_accessops->disable)(sc->sc_accesscookie);
518
519 sc->sc_ready = 0; /* stop accepting events */
520 wsevent_fini(&sc->sc_events);
521 sc->sc_events.io = NULL;
522 return (0);
523 }
524 #endif /* NWSMOUSE > 0 */
525
526 int
527 wsmouseread(dev, uio, flags)
528 dev_t dev;
529 struct uio *uio;
530 int flags;
531 {
532 #if NWSMOUSE > 0
533 struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
534 int error;
535
536 if (sc->sc_dying)
537 return (EIO);
538
539 sc->sc_refcnt++;
540 error = wsevent_read(&sc->sc_events, uio, flags);
541 if (--sc->sc_refcnt < 0) {
542 wakeup(sc);
543 error = EIO;
544 }
545 return (error);
546 #else
547 return (ENXIO);
548 #endif /* NWSMOUSE > 0 */
549 }
550
551 int
552 wsmouseioctl(dev, cmd, data, flag, p)
553 dev_t dev;
554 u_long cmd;
555 caddr_t data;
556 int flag;
557 struct proc *p;
558 {
559 #if NWSMOUSE > 0
560 return (wsmousedoioctl(wsmouse_cd.cd_devs[minor(dev)],
561 cmd, data, flag, p));
562 #else
563 return (ENXIO);
564 #endif /* NWSMOUSE > 0 */
565 }
566
567 #if NWSMOUSE > 0
568 /* A wrapper around the ioctl() workhorse to make reference counting easy. */
569 int
570 wsmousedoioctl(dv, cmd, data, flag, p)
571 struct device *dv;
572 u_long cmd;
573 caddr_t data;
574 int flag;
575 struct proc *p;
576 {
577 struct wsmouse_softc *sc = (struct wsmouse_softc *)dv;
578 int error;
579
580 sc->sc_refcnt++;
581 error = wsmouse_do_ioctl(sc, cmd, data, flag, p);
582 if (--sc->sc_refcnt < 0)
583 wakeup(sc);
584 return (error);
585 }
586
587 int
588 wsmouse_do_ioctl(sc, cmd, data, flag, p)
589 struct wsmouse_softc *sc;
590 u_long cmd;
591 caddr_t data;
592 int flag;
593 struct proc *p;
594 {
595 int error;
596
597 if (sc->sc_dying)
598 return (EIO);
599
600 /*
601 * Try the generic ioctls that the wsmouse interface supports.
602 */
603 switch (cmd) {
604 case FIONBIO: /* we will remove this someday (soon???) */
605 return (0);
606
607 case FIOASYNC:
608 sc->sc_events.async = *(int *)data != 0;
609 return (0);
610
611 case TIOCSPGRP:
612 if (*(int *)data != sc->sc_events.io->p_pgid)
613 return (EPERM);
614 return (0);
615 }
616
617 /*
618 * Try the mouse driver for WSMOUSEIO ioctls. It returns -1
619 * if it didn't recognize the request.
620 */
621 error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd,
622 data, flag, p);
623 return (error != -1 ? error : ENOTTY);
624 }
625 #endif /* NWSMOUSE > 0 */
626
627 int
628 wsmousepoll(dev, events, p)
629 dev_t dev;
630 int events;
631 struct proc *p;
632 {
633 #if NWSMOUSE > 0
634 struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
635
636 return (wsevent_poll(&sc->sc_events, events, p));
637 #else
638 return (0);
639 #endif /* NWSMOUSE > 0 */
640 }
641
642 #if NWSMUX > 0
643 int
644 wsmouse_add_mux(unit, muxsc)
645 int unit;
646 struct wsmux_softc *muxsc;
647 {
648 struct wsmouse_softc *sc;
649
650 if (unit < 0 || unit >= wsmouse_cd.cd_ndevs ||
651 (sc = wsmouse_cd.cd_devs[unit]) == NULL)
652 return (ENXIO);
653
654 if (sc->sc_mux || sc->sc_events.io)
655 return (EBUSY);
656
657 return (wsmux_attach_sc(muxsc, WSMUX_KBD, &sc->sc_dv, &sc->sc_events,
658 &sc->sc_mux, &wsmouse_muxops));
659 }
660
661 int
662 wsmouse_rem_mux(unit, muxsc)
663 int unit;
664 struct wsmux_softc *muxsc;
665 {
666 struct wsmouse_softc *sc;
667
668 if (unit < 0 || unit >= wsmouse_cd.cd_ndevs ||
669 (sc = wsmouse_cd.cd_devs[unit]) == NULL)
670 return (ENXIO);
671
672 return (wsmux_detach_sc(muxsc, &sc->sc_dv));
673 }
674
675 #endif
676