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