ms.c revision 1.36.22.1 1 /* $NetBSD: ms.c,v 1.36.22.1 2012/11/20 03:00:58 tls 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.36.22.1 2012/11/20 03:00:58 tls 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 msopen, msclose, msread, nowrite, msioctl,
135 nostop, notty, mspoll, nommap, mskqfilter,
136 };
137
138 #define MS_UNIT(d) ((minor(d) & ~0x1) >> 1)
139 #define MS_PORT(d) (minor(d) & 0x1)
140
141 /*
142 * Given a dev_t, return a pointer to the port's hardware state.
143 * Assumes the unit to be valid, so do *not* use this in msopen().
144 */
145 #define MS_DEV2MSPORT(d) \
146 (&(((struct ms_softc *)getsoftc(ms_cd, MS_UNIT(d)))->sc_ports[MS_PORT(d)]))
147
148 #if NWSMOUSE > 0
149 /*
150 * Callbacks for wscons.
151 */
152 static int ms_wscons_enable(void *);
153 static int ms_wscons_ioctl(void *, u_long, void *, int, struct lwp *);
154 static void ms_wscons_disable(void *);
155
156 static struct wsmouse_accessops ms_wscons_accessops = {
157 ms_wscons_enable,
158 ms_wscons_ioctl,
159 ms_wscons_disable
160 };
161 #endif
162
163 int
164 msmatch(device_t parent, cfdata_t cf, void *aux)
165 {
166 static int ms_matched = 0;
167
168 /* Allow only one instance. */
169 if (!matchname((char *)aux, "ms") || ms_matched)
170 return 0;
171
172 ms_matched = 1;
173 return 1;
174 }
175
176 void
177 msattach(device_t parent, device_t self, void *aux)
178 {
179 #if NWSMOUSE > 0
180 struct wsmousedev_attach_args waa;
181 #endif
182 struct ms_softc *sc = device_private(self);
183 int i;
184
185 printf("\n");
186 for (i = 0; i < MS_NPORTS; i++) {
187 sc->sc_ports[i].ms_portno = i;
188 callout_init(&sc->sc_ports[i].ms_intr_ch, 0);
189 #if NWSMOUSE > 0
190 waa.accessops = &ms_wscons_accessops;
191 waa.accesscookie = &sc->sc_ports[i];
192
193 sc->sc_ports[i].ms_wsenabled = 0;
194 sc->sc_ports[i].ms_wsmousedev =
195 config_found(self, &waa, wsmousedevprint);
196 #endif
197 }
198 }
199
200 /*
201 * Amiga mice are hooked up to one of the two "game" ports, where
202 * the main mouse is usually on the first port, and port 2 can
203 * be used by a joystick. Nevertheless, we support two mouse
204 * devices, /dev/mouse0 and /dev/mouse1 (with a link of /dev/mouse to
205 * the device that represents the port of the mouse in use).
206 */
207
208 /*
209 * enable scanner, called when someone opens the port.
210 */
211 void
212 ms_enable(struct ms_port *ms)
213 {
214
215 /*
216 * use this as flag to the "interrupt" to tell it when to
217 * shut off (when it's reset to 0).
218 */
219 ms->ms_ready = 1;
220
221 callout_reset(&ms->ms_intr_ch, 2, msintr, ms);
222 }
223
224 /*
225 * disable scanner. Just set ms_ready to 0, and after the next
226 * timeout taken, no further timeouts will be initiated.
227 */
228 void
229 ms_disable(struct ms_port *ms)
230 {
231 int s;
232
233 s = splhigh ();
234 ms->ms_ready = 0;
235 /*
236 * sync with the interrupt
237 */
238 tsleep(ms, PZERO - 1, "mouse-disable", 0);
239 splx(s);
240 }
241
242
243 /*
244 * we're emulating a mousesystems serial mouse here..
245 */
246 void
247 msintr(void *arg)
248 {
249 static const char to_one[] = { 1, 2, 2, 4, 4, 4, 4 };
250 static const int to_id[] = { MS_RIGHT, MS_MIDDLE, 0, MS_LEFT };
251 struct ms_port *ms = arg;
252 struct firm_event *fe;
253 int mb, ub, d, get, put, any, port;
254 u_char pra, *horc, *verc;
255 u_short pot, count;
256 short dx, dy;
257
258 port = ms->ms_portno;
259
260 horc = ((u_char *) &count) + 1;
261 verc = (u_char *) &count;
262
263 /*
264 * first read the three buttons.
265 */
266 pot = custom.potgor;
267 pra = ciaa.pra;
268 pot >>= port == 0 ? 8 : 12; /* contains right and middle button */
269 pra >>= port == 0 ? 6 : 7; /* contains left button */
270 mb = (pot & 4) / 4 + (pot & 1) * 2 + (pra & 1) * 4;
271 mb ^= 0x07;
272
273 /*
274 * read current values of counter registers
275 */
276 if (port == 0)
277 count = custom.joy0dat;
278 else
279 count = custom.joy1dat;
280
281 /*
282 * take care of wraparound
283 */
284 dx = *horc - ms->ms_horc;
285 if (dx < -127)
286 dx += 255;
287 else if (dx > 127)
288 dx -= 255;
289 dy = *verc - ms->ms_verc;
290 if (dy < -127)
291 dy += 255;
292 else if (dy > 127)
293 dy -= 255;
294
295 /*
296 * remember current values for next scan
297 */
298 ms->ms_horc = *horc;
299 ms->ms_verc = *verc;
300
301 ms->ms_dx = dx;
302 ms->ms_dy = dy;
303 ms->ms_mb = mb;
304
305 #if NWSMOUSE > 0
306 /*
307 * If we have attached wsmouse and we are not opened
308 * directly then pass events to wscons.
309 */
310 if (ms->ms_wsmousedev && ms->ms_wsenabled)
311 {
312 int buttons = 0;
313
314 if (mb & 4)
315 buttons |= 1;
316 if (mb & 2)
317 buttons |= 2;
318 if (mb & 1)
319 buttons |= 4;
320
321 wsmouse_input(ms->ms_wsmousedev,
322 buttons,
323 dx, -dy, 0, 0,
324 WSMOUSE_INPUT_DELTA);
325
326 } else
327 #endif
328 if (dx || dy || ms->ms_ub != ms->ms_mb) {
329 /*
330 * We have at least one event (mouse button, delta-X, or
331 * delta-Y; possibly all three, and possibly three separate
332 * button events). Deliver these events until we are out of
333 * changes or out of room. As events get delivered, mark them
334 * `unchanged'.
335 */
336 any = 0;
337 get = ms->ms_events.ev_get;
338 put = ms->ms_events.ev_put;
339 fe = &ms->ms_events.ev_q[put];
340
341 mb = ms->ms_mb;
342 ub = ms->ms_ub;
343 while ((d = mb ^ ub) != 0) {
344 /*
345 * Mouse button change. Convert up to three changes
346 * to the `first' change, and drop it into the event
347 * queue.
348 */
349 if ((++put) % EV_QSIZE == get) {
350 put--;
351 goto out;
352 }
353
354 d = to_one[d - 1]; /* from 1..7 to {1,2,4} */
355 fe->id = to_id[d - 1]; /* from {1,2,4} to ID */
356 fe->value = mb & d ? VKEY_DOWN : VKEY_UP;
357 firm_gettime(fe);
358 fe++;
359
360 if (put >= EV_QSIZE) {
361 put = 0;
362 fe = &ms->ms_events.ev_q[0];
363 }
364 any = 1;
365
366 ub ^= d;
367 }
368 if (ms->ms_dx) {
369 if ((++put) % EV_QSIZE == get) {
370 put--;
371 goto out;
372 }
373
374 fe->id = LOC_X_DELTA;
375 fe->value = ms->ms_dx;
376 firm_gettime(fe);
377 fe++;
378
379 if (put >= EV_QSIZE) {
380 put = 0;
381 fe = &ms->ms_events.ev_q[0];
382 }
383 any = 1;
384
385 ms->ms_dx = 0;
386 }
387 if (ms->ms_dy) {
388 if ((++put) % EV_QSIZE == get) {
389 put--;
390 goto out;
391 }
392
393 fe->id = LOC_Y_DELTA;
394 fe->value = ms->ms_dy;
395 firm_gettime(fe);
396 fe++;
397
398 if (put >= EV_QSIZE) {
399 put = 0;
400 fe = &ms->ms_events.ev_q[0];
401 }
402 any = 1;
403
404 ms->ms_dy = 0;
405 }
406 out:
407 if (any) {
408 ms->ms_ub = ub;
409 ms->ms_events.ev_put = put;
410 EV_WAKEUP(&ms->ms_events);
411 }
412 }
413
414 /*
415 * reschedule handler, or if terminating,
416 * handshake with ms_disable
417 */
418 if (ms->ms_ready)
419 callout_reset(&ms->ms_intr_ch, 2, msintr, ms);
420 else
421 wakeup(ms);
422 }
423
424 int
425 msopen(dev_t dev, int flags, int mode, struct lwp *l)
426 {
427 struct ms_softc *sc;
428 struct ms_port *ms;
429 int unit, port;
430
431 unit = MS_UNIT(dev);
432 sc = (struct ms_softc *)getsoftc(ms_cd, unit);
433
434 if (sc == NULL)
435 return(EXDEV);
436
437 port = MS_PORT(dev);
438 ms = &sc->sc_ports[port];
439
440 if (ms->ms_events.ev_io)
441 return(EBUSY);
442
443 #if NWSMOUSE > 0
444 /* don't allow opening when sending events to wsmouse */
445 if (ms->ms_wsenabled)
446 return EBUSY;
447 #endif
448 /* initialize potgo bits for mouse mode */
449 custom.potgo = custom.potgor | (0xf00 << (port * 4));
450
451 ms->ms_events.ev_io = l->l_proc;
452 ev_init(&ms->ms_events); /* may cause sleep */
453 ms_enable(ms);
454 return(0);
455 }
456
457 int
458 msclose(dev_t dev, int flags, int mode, struct lwp *l)
459 {
460 struct ms_port *ms;
461
462 ms = MS_DEV2MSPORT(dev);
463
464 ms_disable(ms);
465 ev_fini(&ms->ms_events);
466 ms->ms_events.ev_io = NULL;
467 return(0);
468 }
469
470 int
471 msread(dev_t dev, struct uio *uio, int flags)
472 {
473 struct ms_port *ms;
474
475 ms = MS_DEV2MSPORT(dev);
476
477 return(ev_read(&ms->ms_events, uio, flags));
478 }
479
480 int
481 msioctl(dev_t dev, u_long cmd, register void *data, int flag,
482 struct lwp *l)
483 {
484 struct ms_port *ms;
485
486 ms = MS_DEV2MSPORT(dev);
487
488 switch (cmd) {
489 case FIONBIO: /* we will remove this someday (soon???) */
490 return(0);
491 case FIOASYNC:
492 ms->ms_events.ev_async = *(int *)data != 0;
493 return(0);
494 case FIOSETOWN:
495 if (-*(int *)data != ms->ms_events.ev_io->p_pgid
496 && *(int *)data != ms->ms_events.ev_io->p_pid)
497 return(EPERM);
498 return(0);
499 case TIOCSPGRP:
500 if (*(int *)data != ms->ms_events.ev_io->p_pgid)
501 return(EPERM);
502 return(0);
503 case VUIDGFORMAT: /* we only do firm_events */
504 *(int *)data = VUID_FIRM_EVENT;
505 return(0);
506 case VUIDSFORMAT:
507 if (*(int *)data != VUID_FIRM_EVENT)
508 return(EINVAL);
509 return(0);
510 }
511 return(ENOTTY);
512 }
513
514 int
515 mspoll(dev_t dev, int events, struct lwp *l)
516 {
517 struct ms_port *ms;
518
519 ms = MS_DEV2MSPORT(dev);
520
521 return(ev_poll(&ms->ms_events, events, l));
522 }
523
524 int
525 mskqfilter(dev_t dev, struct knote *kn)
526 {
527 struct ms_port *ms;
528
529 ms = MS_DEV2MSPORT(dev);
530
531 return (ev_kqfilter(&ms->ms_events, kn));
532 }
533
534 #if NWSMOUSE > 0
535
536 static int
537 ms_wscons_ioctl(void *cookie, u_long cmd, void *data, int flag,
538 struct lwp *l)
539 {
540 switch(cmd) {
541 case WSMOUSEIO_GTYPE:
542 *(u_int*)data = WSMOUSE_TYPE_AMIGA;
543 return (0);
544 }
545
546 return -1;
547 }
548
549 static int
550 ms_wscons_enable(void *cookie)
551 {
552 struct ms_port *port = cookie;
553
554 /* somebody reading events from us directly? */
555 if (port->ms_events.ev_io)
556 return EBUSY;
557
558 port->ms_wsenabled = 1;
559 ms_enable(port);
560
561 return 0;
562 }
563
564 static void
565 ms_wscons_disable(void *cookie)
566 {
567 struct ms_port *port = cookie;
568
569 if (port->ms_wsenabled)
570 ms_disable(port);
571 port->ms_wsenabled = 0;
572 }
573
574 #endif
575
576