ms.c revision 1.39.18.1 1 /* $NetBSD: ms.c,v 1.39.18.1 2017/04/27 05:36:31 pgoyette Exp $ */
2
3 /*
4 * based on:
5 *
6 * Copyright (c) 1992, 1993
7 * The Regents of the University of California. All rights reserved.
8 *
9 * This software was developed by the Computer Systems Engineering group
10 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
11 * contributed to Berkeley.
12 *
13 * All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Lawrence Berkeley Laboratory.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
20 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * 3. Neither the name of the University nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 * @(#)ms.c 8.1 (Berkeley) 6/11/93
43 *
44 * Header: ms.c,v 1.5 92/11/26 01:28:47 torek Exp (LBL)
45 */
46
47 #include <sys/cdefs.h>
48 __KERNEL_RCSID(0, "$NetBSD: ms.c,v 1.39.18.1 2017/04/27 05:36:31 pgoyette Exp $");
49
50 /*
51 * Mouse driver.
52 *
53 * wscons aware. Attaches two wsmouse devices, one for each port.
54 * Also still exports its own device entry points so it is possible
55 * to open this and read firm_events.
56 * The events go only to one place at a time:
57 * - When somebody has opened a ms device directly wsmouse cannot be activated.
58 * (when wsmouse is opened it calls ms_enable to activate)
59 * - When feeding events to wsmouse open of ms device will fail.
60 */
61
62 #include "wsmouse.h"
63
64 #include <sys/param.h>
65 #include <sys/device.h>
66 #include <sys/ioctl.h>
67 #include <sys/kernel.h>
68 #include <sys/proc.h>
69 #include <sys/syslog.h>
70 #include <sys/systm.h>
71 #include <sys/callout.h>
72 #include <sys/tty.h>
73 #include <sys/signalvar.h>
74 #include <sys/conf.h>
75
76 #include <amiga/dev/event_var.h>
77 #include <amiga/dev/vuid_event.h>
78
79 #include <amiga/amiga/custom.h>
80 #include <amiga/amiga/cia.h>
81 #include <amiga/amiga/device.h>
82
83 #if NWSMOUSE > 0
84 #include <dev/wscons/wsmousevar.h>
85 #include <dev/wscons/wsconsio.h>
86 #endif
87
88 void msattach(device_t, device_t, void *);
89 int msmatch(device_t, cfdata_t, void *);
90
91 /* per-port state */
92 struct ms_port {
93 int ms_portno; /* which hardware port, for msintr() */
94
95 struct callout ms_intr_ch;
96
97 u_char ms_horc; /* horizontal counter on last scan */
98 u_char ms_verc; /* vertical counter on last scan */
99 char ms_mb; /* mouse button state */
100 char ms_ub; /* user button state */
101 int ms_dx; /* delta-x */
102 int ms_dy; /* delta-y */
103 volatile int ms_ready; /* event queue is ready */
104 struct evvar ms_events; /* event queue state */
105 #if NWSMOUSE > 0
106 device_t ms_wsmousedev; /* wsmouse device */
107 int ms_wsenabled; /* feeding events to wscons */
108 #endif
109 };
110
111 #define MS_NPORTS 2
112
113 struct ms_softc {
114 struct ms_port sc_ports[MS_NPORTS];
115 };
116
117 CFATTACH_DECL_NEW(ms, sizeof(struct ms_softc),
118 msmatch, msattach, NULL, NULL);
119
120 void msintr(void *);
121 void ms_enable(struct ms_port *);
122 void ms_disable(struct ms_port *);
123
124 extern struct cfdriver ms_cd;
125
126 dev_type_open(msopen);
127 dev_type_close(msclose);
128 dev_type_read(msread);
129 dev_type_ioctl(msioctl);
130 dev_type_poll(mspoll);
131 dev_type_kqfilter(mskqfilter);
132
133 const struct cdevsw ms_cdevsw = {
134 .d_open = msopen,
135 .d_close = msclose,
136 .d_read = msread,
137 .d_write = nowrite,
138 .d_ioctl = msioctl,
139 .d_stop = nostop,
140 .d_tty = notty,
141 .d_poll = mspoll,
142 .d_mmap = nommap,
143 .d_kqfilter = mskqfilter,
144 .d_discard = nodiscard,
145 .d_flag = 0
146 };
147
148 #define MS_UNIT(d) ((minor(d) & ~0x1) >> 1)
149 #define MS_PORT(d) (minor(d) & 0x1)
150
151 #if NWSMOUSE > 0
152 /*
153 * Callbacks for wscons.
154 */
155 static int ms_wscons_enable(void *);
156 static int ms_wscons_ioctl(void *, u_long, void *, int, struct lwp *);
157 static void ms_wscons_disable(void *);
158
159 static struct wsmouse_accessops ms_wscons_accessops = {
160 ms_wscons_enable,
161 ms_wscons_ioctl,
162 ms_wscons_disable
163 };
164 #endif
165
166 /*
167 * Given a dev_t, return a pointer to the port's hardware state.
168 * Assumes the unit to be valid, so do *not* use this in msopen().
169 */
170 static struct ms_port *
171 ms_dev2msport(dev_t dev)
172 {
173 device_t self;
174 struct ms_softc *sc;
175 struct ms_port *msp;
176
177 msp = NULL;
178
179 self = device_lookup_acquire(&ms_cd, MS_UNIT(dev));
180 if (self == NULL)
181 return msp;;
182 sc = device_private(self);
183 if (sc != NULL)
184 msp = sc->sc_ports[MS_PORT(dev)];
185
186 device_release(self);
187 return msp;
188 }
189
190 int
191 msmatch(device_t parent, cfdata_t cf, void *aux)
192 {
193 static int ms_matched = 0;
194
195 /* Allow only one instance. */
196 if (!matchname((char *)aux, "ms") || ms_matched)
197 return 0;
198
199 ms_matched = 1;
200 return 1;
201 }
202
203 void
204 msattach(device_t parent, device_t self, void *aux)
205 {
206 #if NWSMOUSE > 0
207 struct wsmousedev_attach_args waa;
208 #endif
209 struct ms_softc *sc = device_private(self);
210 int i;
211
212 printf("\n");
213 for (i = 0; i < MS_NPORTS; i++) {
214 sc->sc_ports[i].ms_portno = i;
215 callout_init(&sc->sc_ports[i].ms_intr_ch, 0);
216 #if NWSMOUSE > 0
217 waa.accessops = &ms_wscons_accessops;
218 waa.accesscookie = &sc->sc_ports[i];
219
220 sc->sc_ports[i].ms_wsenabled = 0;
221 sc->sc_ports[i].ms_wsmousedev =
222 config_found(self, &waa, wsmousedevprint);
223 #endif
224 }
225 }
226
227 /*
228 * Amiga mice are hooked up to one of the two "game" ports, where
229 * the main mouse is usually on the first port, and port 2 can
230 * be used by a joystick. Nevertheless, we support two mouse
231 * devices, /dev/mouse0 and /dev/mouse1 (with a link of /dev/mouse to
232 * the device that represents the port of the mouse in use).
233 */
234
235 /*
236 * enable scanner, called when someone opens the port.
237 */
238 void
239 ms_enable(struct ms_port *ms)
240 {
241
242 /*
243 * use this as flag to the "interrupt" to tell it when to
244 * shut off (when it's reset to 0).
245 */
246 ms->ms_ready = 1;
247
248 callout_reset(&ms->ms_intr_ch, 2, msintr, ms);
249 }
250
251 /*
252 * disable scanner. Just set ms_ready to 0, and after the next
253 * timeout taken, no further timeouts will be initiated.
254 */
255 void
256 ms_disable(struct ms_port *ms)
257 {
258 int s;
259
260 s = splhigh ();
261 ms->ms_ready = 0;
262 /*
263 * sync with the interrupt
264 */
265 tsleep(ms, PZERO - 1, "mouse-disable", 0);
266 splx(s);
267 }
268
269
270 /*
271 * we're emulating a mousesystems serial mouse here..
272 */
273 void
274 msintr(void *arg)
275 {
276 static const char to_one[] = { 1, 2, 2, 4, 4, 4, 4 };
277 static const int to_id[] = { MS_RIGHT, MS_MIDDLE, 0, MS_LEFT };
278 struct ms_port *ms = arg;
279 struct firm_event *fe;
280 int mb, ub, d, get, put, any, port;
281 u_char pra, *horc, *verc;
282 u_short pot, count;
283 short dx, dy;
284
285 port = ms->ms_portno;
286
287 horc = ((u_char *) &count) + 1;
288 verc = (u_char *) &count;
289
290 /*
291 * first read the three buttons.
292 */
293 pot = custom.potgor;
294 pra = ciaa.pra;
295 pot >>= port == 0 ? 8 : 12; /* contains right and middle button */
296 pra >>= port == 0 ? 6 : 7; /* contains left button */
297 mb = (pot & 4) / 4 + (pot & 1) * 2 + (pra & 1) * 4;
298 mb ^= 0x07;
299
300 /*
301 * read current values of counter registers
302 */
303 if (port == 0)
304 count = custom.joy0dat;
305 else
306 count = custom.joy1dat;
307
308 /*
309 * take care of wraparound
310 */
311 dx = *horc - ms->ms_horc;
312 if (dx < -127)
313 dx += 255;
314 else if (dx > 127)
315 dx -= 255;
316 dy = *verc - ms->ms_verc;
317 if (dy < -127)
318 dy += 255;
319 else if (dy > 127)
320 dy -= 255;
321
322 /*
323 * remember current values for next scan
324 */
325 ms->ms_horc = *horc;
326 ms->ms_verc = *verc;
327
328 ms->ms_dx = dx;
329 ms->ms_dy = dy;
330 ms->ms_mb = mb;
331
332 #if NWSMOUSE > 0
333 /*
334 * If we have attached wsmouse and we are not opened
335 * directly then pass events to wscons.
336 */
337 if (ms->ms_wsmousedev && ms->ms_wsenabled)
338 {
339 int buttons = 0;
340
341 if (mb & 4)
342 buttons |= 1;
343 if (mb & 2)
344 buttons |= 2;
345 if (mb & 1)
346 buttons |= 4;
347
348 wsmouse_input(ms->ms_wsmousedev,
349 buttons,
350 dx, -dy, 0, 0,
351 WSMOUSE_INPUT_DELTA);
352
353 } else
354 #endif
355 if (dx || dy || ms->ms_ub != ms->ms_mb) {
356 /*
357 * We have at least one event (mouse button, delta-X, or
358 * delta-Y; possibly all three, and possibly three separate
359 * button events). Deliver these events until we are out of
360 * changes or out of room. As events get delivered, mark them
361 * `unchanged'.
362 */
363 any = 0;
364 get = ms->ms_events.ev_get;
365 put = ms->ms_events.ev_put;
366 fe = &ms->ms_events.ev_q[put];
367
368 mb = ms->ms_mb;
369 ub = ms->ms_ub;
370 while ((d = mb ^ ub) != 0) {
371 /*
372 * Mouse button change. Convert up to three changes
373 * to the `first' change, and drop it into the event
374 * queue.
375 */
376 if ((++put) % EV_QSIZE == get) {
377 put--;
378 goto out;
379 }
380
381 d = to_one[d - 1]; /* from 1..7 to {1,2,4} */
382 fe->id = to_id[d - 1]; /* from {1,2,4} to ID */
383 fe->value = mb & d ? VKEY_DOWN : VKEY_UP;
384 firm_gettime(fe);
385 fe++;
386
387 if (put >= EV_QSIZE) {
388 put = 0;
389 fe = &ms->ms_events.ev_q[0];
390 }
391 any = 1;
392
393 ub ^= d;
394 }
395 if (ms->ms_dx) {
396 if ((++put) % EV_QSIZE == get) {
397 put--;
398 goto out;
399 }
400
401 fe->id = LOC_X_DELTA;
402 fe->value = ms->ms_dx;
403 firm_gettime(fe);
404 fe++;
405
406 if (put >= EV_QSIZE) {
407 put = 0;
408 fe = &ms->ms_events.ev_q[0];
409 }
410 any = 1;
411
412 ms->ms_dx = 0;
413 }
414 if (ms->ms_dy) {
415 if ((++put) % EV_QSIZE == get) {
416 put--;
417 goto out;
418 }
419
420 fe->id = LOC_Y_DELTA;
421 fe->value = ms->ms_dy;
422 firm_gettime(fe);
423 fe++;
424
425 if (put >= EV_QSIZE) {
426 put = 0;
427 fe = &ms->ms_events.ev_q[0];
428 }
429 any = 1;
430
431 ms->ms_dy = 0;
432 }
433 out:
434 if (any) {
435 ms->ms_ub = ub;
436 ms->ms_events.ev_put = put;
437 EV_WAKEUP(&ms->ms_events);
438 }
439 }
440
441 /*
442 * reschedule handler, or if terminating,
443 * handshake with ms_disable
444 */
445 if (ms->ms_ready)
446 callout_reset(&ms->ms_intr_ch, 2, msintr, ms);
447 else
448 wakeup(ms);
449 }
450
451 int
452 msopen(dev_t dev, int flags, int mode, struct lwp *l)
453 {
454 device_t self;
455 struct ms_softc *sc;
456 struct ms_port *ms;
457 int unit, port;
458
459 unit = MS_UNIT(dev);
460
461 self = device_lookup_acquire(&ms_cd, unit);
462 if (self == NULL)
463 return ENXIO;
464 sc = device_private(self);
465 if (sc == NULL) {
466 device_release(self);
467 return(EXDEV);
468 }
469
470 port = MS_PORT(dev);
471 ms = &sc->sc_ports[port];
472
473 if (ms->ms_events.ev_io) {
474 device_release(self);
475 return(EBUSY);
476 }
477
478 #if NWSMOUSE > 0
479 /* don't allow opening when sending events to wsmouse */
480 if (ms->ms_wsenabled) {
481 device_release(self);
482 return EBUSY;
483 }
484 #endif
485 /* initialize potgo bits for mouse mode */
486 custom.potgo = custom.potgor | (0xf00 << (port * 4));
487
488 ms->ms_events.ev_io = l->l_proc;
489 ev_init(&ms->ms_events); /* may cause sleep */
490 ms_enable(ms);
491
492 device_release(self);
493 return(0);
494 }
495
496 int
497 msclose(dev_t dev, int flags, int mode, struct lwp *l)
498 {
499 struct ms_port *ms;
500
501 ms = ms_dev2msport(dev);
502
503 ms_disable(ms);
504 ev_fini(&ms->ms_events);
505 ms->ms_events.ev_io = NULL;
506 return(0);
507 }
508
509 int
510 msread(dev_t dev, struct uio *uio, int flags)
511 {
512 struct ms_port *ms;
513
514 ms = ms_dev2msport(dev);
515
516 return(ev_read(&ms->ms_events, uio, flags));
517 }
518
519 int
520 msioctl(dev_t dev, u_long cmd, register void *data, int flag,
521 struct lwp *l)
522 {
523 struct ms_port *ms;
524
525 ms = ms_dev2msport(dev);
526
527 switch (cmd) {
528 case FIONBIO: /* we will remove this someday (soon???) */
529 return(0);
530 case FIOASYNC:
531 ms->ms_events.ev_async = *(int *)data != 0;
532 return(0);
533 case FIOSETOWN:
534 if (-*(int *)data != ms->ms_events.ev_io->p_pgid
535 && *(int *)data != ms->ms_events.ev_io->p_pid)
536 return(EPERM);
537 return(0);
538 case TIOCSPGRP:
539 if (*(int *)data != ms->ms_events.ev_io->p_pgid)
540 return(EPERM);
541 return(0);
542 case VUIDGFORMAT: /* we only do firm_events */
543 *(int *)data = VUID_FIRM_EVENT;
544 return(0);
545 case VUIDSFORMAT:
546 if (*(int *)data != VUID_FIRM_EVENT)
547 return(EINVAL);
548 return(0);
549 }
550 return(ENOTTY);
551 }
552
553 int
554 mspoll(dev_t dev, int events, struct lwp *l)
555 {
556 struct ms_port *ms;
557
558 ms = ms_dev2msport(dev);
559
560 return(ev_poll(&ms->ms_events, events, l));
561 }
562
563 int
564 mskqfilter(dev_t dev, struct knote *kn)
565 {
566 struct ms_port *ms;
567
568 ms = ms_dev2msport(dev);
569
570 return (ev_kqfilter(&ms->ms_events, kn));
571 }
572
573 #if NWSMOUSE > 0
574
575 static int
576 ms_wscons_ioctl(void *cookie, u_long cmd, void *data, int flag,
577 struct lwp *l)
578 {
579 switch(cmd) {
580 case WSMOUSEIO_GTYPE:
581 *(u_int*)data = WSMOUSE_TYPE_AMIGA;
582 return (0);
583 }
584
585 return -1;
586 }
587
588 static int
589 ms_wscons_enable(void *cookie)
590 {
591 struct ms_port *port = cookie;
592
593 /* somebody reading events from us directly? */
594 if (port->ms_events.ev_io)
595 return EBUSY;
596
597 port->ms_wsenabled = 1;
598 ms_enable(port);
599
600 return 0;
601 }
602
603 static void
604 ms_wscons_disable(void *cookie)
605 {
606 struct ms_port *port = cookie;
607
608 if (port->ms_wsenabled)
609 ms_disable(port);
610 port->ms_wsenabled = 0;
611 }
612
613 #endif
614
615