wsmouse.c revision 1.14 1 /* $NetBSD: wsmouse.c,v 1.14 2001/10/13 13:36:01 augustss 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.14 2001/10/13 13:36:01 augustss 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 != NULL) {
462 /* Grab the mouse out of the greedy hands of the mux. */
463 error = wsmouse_rem_mux(unit, sc->sc_mux);
464 if (error)
465 return (error);
466 }
467 #endif
468
469 if (sc->sc_events.io) /* and that it's not in use */
470 return (EBUSY);
471
472 sc->sc_events.io = p;
473 wsevent_init(&sc->sc_events); /* may cause sleep */
474
475 sc->sc_ready = 1; /* start accepting events */
476 sc->sc_x = INVALID_X;
477 sc->sc_y = INVALID_Y;
478 sc->sc_z = INVALID_Z;
479
480 /* enable the device, and punt if that's not possible */
481 error = (*sc->sc_accessops->enable)(sc->sc_accesscookie);
482 if (error) {
483 sc->sc_ready = 0; /* stop accepting events */
484 wsevent_fini(&sc->sc_events);
485 sc->sc_events.io = NULL;
486 return (error);
487 }
488
489 return (0);
490 #else
491 return (ENXIO);
492 #endif /* NWSMOUSE > 0 */
493 }
494
495 int
496 wsmouseclose(dev, flags, mode, p)
497 dev_t dev;
498 int flags, mode;
499 struct proc *p;
500 {
501 #if NWSMOUSE > 0
502 return (wsmousedoclose(wsmouse_cd.cd_devs[minor(dev)],
503 flags, mode, p));
504 #else
505 return (ENXIO);
506 #endif /* NWSMOUSE > 0 */
507 }
508
509 #if NWSMOUSE > 0
510 int
511 wsmousedoclose(dv, flags, mode, p)
512 struct device *dv;
513 int flags, mode;
514 struct proc *p;
515 {
516 struct wsmouse_softc *sc = (struct wsmouse_softc *)dv;
517
518 if ((flags & (FREAD | FWRITE)) == FWRITE)
519 return (0); /* see wsmouseopen() */
520
521 (*sc->sc_accessops->disable)(sc->sc_accesscookie);
522
523 sc->sc_ready = 0; /* stop accepting events */
524 wsevent_fini(&sc->sc_events);
525 sc->sc_events.io = NULL;
526 return (0);
527 }
528 #endif /* NWSMOUSE > 0 */
529
530 int
531 wsmouseread(dev, uio, flags)
532 dev_t dev;
533 struct uio *uio;
534 int flags;
535 {
536 #if NWSMOUSE > 0
537 struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
538 int error;
539
540 if (sc->sc_dying)
541 return (EIO);
542
543 sc->sc_refcnt++;
544 error = wsevent_read(&sc->sc_events, uio, flags);
545 if (--sc->sc_refcnt < 0) {
546 wakeup(sc);
547 error = EIO;
548 }
549 return (error);
550 #else
551 return (ENXIO);
552 #endif /* NWSMOUSE > 0 */
553 }
554
555 int
556 wsmouseioctl(dev, cmd, data, flag, p)
557 dev_t dev;
558 u_long cmd;
559 caddr_t data;
560 int flag;
561 struct proc *p;
562 {
563 #if NWSMOUSE > 0
564 return (wsmousedoioctl(wsmouse_cd.cd_devs[minor(dev)],
565 cmd, data, flag, p));
566 #else
567 return (ENXIO);
568 #endif /* NWSMOUSE > 0 */
569 }
570
571 #if NWSMOUSE > 0
572 /* A wrapper around the ioctl() workhorse to make reference counting easy. */
573 int
574 wsmousedoioctl(dv, cmd, data, flag, p)
575 struct device *dv;
576 u_long cmd;
577 caddr_t data;
578 int flag;
579 struct proc *p;
580 {
581 struct wsmouse_softc *sc = (struct wsmouse_softc *)dv;
582 int error;
583
584 sc->sc_refcnt++;
585 error = wsmouse_do_ioctl(sc, cmd, data, flag, p);
586 if (--sc->sc_refcnt < 0)
587 wakeup(sc);
588 return (error);
589 }
590
591 int
592 wsmouse_do_ioctl(sc, cmd, data, flag, p)
593 struct wsmouse_softc *sc;
594 u_long cmd;
595 caddr_t data;
596 int flag;
597 struct proc *p;
598 {
599 int error;
600
601 if (sc->sc_dying)
602 return (EIO);
603
604 /*
605 * Try the generic ioctls that the wsmouse interface supports.
606 */
607 switch (cmd) {
608 case FIONBIO: /* we will remove this someday (soon???) */
609 return (0);
610
611 case FIOASYNC:
612 sc->sc_events.async = *(int *)data != 0;
613 return (0);
614
615 case TIOCSPGRP:
616 if (*(int *)data != sc->sc_events.io->p_pgid)
617 return (EPERM);
618 return (0);
619 }
620
621 /*
622 * Try the mouse driver for WSMOUSEIO ioctls. It returns -1
623 * if it didn't recognize the request.
624 */
625 error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd,
626 data, flag, p);
627 return (error != -1 ? error : ENOTTY);
628 }
629 #endif /* NWSMOUSE > 0 */
630
631 int
632 wsmousepoll(dev, events, p)
633 dev_t dev;
634 int events;
635 struct proc *p;
636 {
637 #if NWSMOUSE > 0
638 struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
639
640 return (wsevent_poll(&sc->sc_events, events, p));
641 #else
642 return (0);
643 #endif /* NWSMOUSE > 0 */
644 }
645
646 #if NWSMUX > 0
647 int
648 wsmouse_add_mux(unit, muxsc)
649 int unit;
650 struct wsmux_softc *muxsc;
651 {
652 struct wsmouse_softc *sc;
653
654 if (unit < 0 || unit >= wsmouse_cd.cd_ndevs ||
655 (sc = wsmouse_cd.cd_devs[unit]) == NULL)
656 return (ENXIO);
657
658 if (sc->sc_mux || sc->sc_events.io)
659 return (EBUSY);
660
661 return (wsmux_attach_sc(muxsc, WSMUX_KBD, &sc->sc_dv, &sc->sc_events,
662 &sc->sc_mux, &wsmouse_muxops));
663 }
664
665 int
666 wsmouse_rem_mux(unit, muxsc)
667 int unit;
668 struct wsmux_softc *muxsc;
669 {
670 struct wsmouse_softc *sc;
671
672 if (unit < 0 || unit >= wsmouse_cd.cd_ndevs ||
673 (sc = wsmouse_cd.cd_devs[unit]) == NULL)
674 return (ENXIO);
675
676 return (wsmux_detach_sc(muxsc, &sc->sc_dv));
677 }
678
679 #endif
680