ms.c revision 1.27 1 /* $NetBSD: ms.c,v 1.27 2003/09/21 19:16:49 jdolecek 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.27 2003/09/21 19:16:49 jdolecek Exp $");
49
50 /*
51 * Mouse driver.
52 */
53
54 #include <sys/param.h>
55 #include <sys/device.h>
56 #include <sys/ioctl.h>
57 #include <sys/kernel.h>
58 #include <sys/proc.h>
59 #include <sys/syslog.h>
60 #include <sys/systm.h>
61 #include <sys/callout.h>
62 #include <sys/tty.h>
63 #include <sys/signalvar.h>
64 #include <sys/conf.h>
65
66 #include <amiga/dev/event_var.h>
67 #include <amiga/dev/vuid_event.h>
68
69 #include <amiga/amiga/custom.h>
70 #include <amiga/amiga/cia.h>
71 #include <amiga/amiga/device.h>
72
73 void msattach(struct device *, struct device *, void *);
74 int msmatch(struct device *, struct cfdata *, void *);
75
76 /* per-port state */
77 struct ms_port {
78 int ms_portno; /* which hardware port, for msintr() */
79
80 struct callout ms_intr_ch;
81
82 u_char ms_horc; /* horizontal counter on last scan */
83 u_char ms_verc; /* vertical counter on last scan */
84 char ms_mb; /* mouse button state */
85 char ms_ub; /* user button state */
86 int ms_dx; /* delta-x */
87 int ms_dy; /* delta-y */
88 volatile int ms_ready; /* event queue is ready */
89 struct evvar ms_events; /* event queue state */
90 };
91
92 #define MS_NPORTS 2
93
94 struct ms_softc {
95 struct device sc_dev; /* base device */
96 struct ms_port sc_ports[MS_NPORTS];
97 };
98
99 CFATTACH_DECL(ms, sizeof(struct ms_softc),
100 msmatch, msattach, NULL, NULL);
101
102 void msintr(void *);
103 void ms_enable(struct ms_port *);
104 void ms_disable(struct ms_port *);
105
106 extern struct cfdriver ms_cd;
107
108 dev_type_open(msopen);
109 dev_type_close(msclose);
110 dev_type_read(msread);
111 dev_type_ioctl(msioctl);
112 dev_type_poll(mspoll);
113 dev_type_kqfilter(mskqfilter);
114
115 const struct cdevsw ms_cdevsw = {
116 msopen, msclose, msread, nowrite, msioctl,
117 nostop, notty, mspoll, nommap, mskqfilter,
118 };
119
120 #define MS_UNIT(d) ((minor(d) & ~0x1) >> 1)
121 #define MS_PORT(d) (minor(d) & 0x1)
122
123 /*
124 * Given a dev_t, return a pointer to the port's hardware state.
125 * Assumes the unit to be valid, so do *not* use this in msopen().
126 */
127 #define MS_DEV2MSPORT(d) \
128 (&(((struct ms_softc *)getsoftc(ms_cd, MS_UNIT(d)))->sc_ports[MS_PORT(d)]))
129
130 int
131 msmatch(struct device *pdp, struct cfdata *cfp, void *auxp)
132 {
133 static int ms_matched = 0;
134
135 /* Allow only one instance. */
136 if (!matchname((char *)auxp, "ms") || ms_matched)
137 return 0;
138
139 ms_matched = 1;
140 return 1;
141 }
142
143 void
144 msattach(struct device *pdp, struct device *dp, void *auxp)
145 {
146 struct ms_softc *sc = (void *) dp;
147 int i;
148
149 printf("\n");
150 for (i = 0; i < MS_NPORTS; i++) {
151 sc->sc_ports[i].ms_portno = i;
152 callout_init(&sc->sc_ports[i].ms_intr_ch);
153 }
154 }
155
156 /*
157 * Amiga mice are hooked up to one of the two "game" ports, where
158 * the main mouse is usually on the first port, and port 2 can
159 * be used by a joystick. Nevertheless, we support two mouse
160 * devices, /dev/mouse0 and /dev/mouse1 (with a link of /dev/mouse to
161 * the device that represents the port of the mouse in use).
162 */
163
164 /*
165 * enable scanner, called when someone opens the port.
166 */
167 void
168 ms_enable(struct ms_port *ms)
169 {
170
171 /*
172 * use this as flag to the "interrupt" to tell it when to
173 * shut off (when it's reset to 0).
174 */
175 ms->ms_ready = 1;
176
177 callout_reset(&ms->ms_intr_ch, 2, msintr, ms);
178 }
179
180 /*
181 * disable scanner. Just set ms_ready to 0, and after the next
182 * timeout taken, no further timeouts will be initiated.
183 */
184 void
185 ms_disable(struct ms_port *ms)
186 {
187 int s;
188
189 s = splhigh ();
190 ms->ms_ready = 0;
191 /*
192 * sync with the interrupt
193 */
194 tsleep(ms, PZERO - 1, "mouse-disable", 0);
195 splx(s);
196 }
197
198
199 /*
200 * we're emulating a mousesystems serial mouse here..
201 */
202 void
203 msintr(void *arg)
204 {
205 static const char to_one[] = { 1, 2, 2, 4, 4, 4, 4 };
206 static const int to_id[] = { MS_RIGHT, MS_MIDDLE, 0, MS_LEFT };
207 struct ms_port *ms = arg;
208 struct firm_event *fe;
209 int mb, ub, d, get, put, any, port;
210 u_char pra, *horc, *verc;
211 u_short pot, count;
212 short dx, dy;
213
214 port = ms->ms_portno;
215
216 horc = ((u_char *) &count) + 1;
217 verc = (u_char *) &count;
218
219 /*
220 * first read the three buttons.
221 */
222 pot = custom.potgor;
223 pra = ciaa.pra;
224 pot >>= port == 0 ? 8 : 12; /* contains right and middle button */
225 pra >>= port == 0 ? 6 : 7; /* contains left button */
226 mb = (pot & 4) / 4 + (pot & 1) * 2 + (pra & 1) * 4;
227 mb ^= 0x07;
228
229 /*
230 * read current values of counter registers
231 */
232 if (port == 0)
233 count = custom.joy0dat;
234 else
235 count = custom.joy1dat;
236
237 /*
238 * take care of wraparound
239 */
240 dx = *horc - ms->ms_horc;
241 if (dx < -127)
242 dx += 255;
243 else if (dx > 127)
244 dx -= 255;
245 dy = *verc - ms->ms_verc;
246 if (dy < -127)
247 dy += 255;
248 else if (dy > 127)
249 dy -= 255;
250
251 /*
252 * remember current values for next scan
253 */
254 ms->ms_horc = *horc;
255 ms->ms_verc = *verc;
256
257 ms->ms_dx = dx;
258 ms->ms_dy = dy;
259 ms->ms_mb = mb;
260
261 if (dx || dy || ms->ms_ub != ms->ms_mb) {
262 /*
263 * We have at least one event (mouse button, delta-X, or
264 * delta-Y; possibly all three, and possibly three separate
265 * button events). Deliver these events until we are out of
266 * changes or out of room. As events get delivered, mark them
267 * `unchanged'.
268 */
269 any = 0;
270 get = ms->ms_events.ev_get;
271 put = ms->ms_events.ev_put;
272 fe = &ms->ms_events.ev_q[put];
273
274 mb = ms->ms_mb;
275 ub = ms->ms_ub;
276 while ((d = mb ^ ub) != 0) {
277 /*
278 * Mouse button change. Convert up to three changes
279 * to the `first' change, and drop it into the event
280 * queue.
281 */
282 if ((++put) % EV_QSIZE == get) {
283 put--;
284 goto out;
285 }
286
287 d = to_one[d - 1]; /* from 1..7 to {1,2,4} */
288 fe->id = to_id[d - 1]; /* from {1,2,4} to ID */
289 fe->value = mb & d ? VKEY_DOWN : VKEY_UP;
290 fe->time = time;
291 fe++;
292
293 if (put >= EV_QSIZE) {
294 put = 0;
295 fe = &ms->ms_events.ev_q[0];
296 }
297 any = 1;
298
299 ub ^= d;
300 }
301 if (ms->ms_dx) {
302 if ((++put) % EV_QSIZE == get) {
303 put--;
304 goto out;
305 }
306
307 fe->id = LOC_X_DELTA;
308 fe->value = ms->ms_dx;
309 fe->time = time;
310 fe++;
311
312 if (put >= EV_QSIZE) {
313 put = 0;
314 fe = &ms->ms_events.ev_q[0];
315 }
316 any = 1;
317
318 ms->ms_dx = 0;
319 }
320 if (ms->ms_dy) {
321 if ((++put) % EV_QSIZE == get) {
322 put--;
323 goto out;
324 }
325
326 fe->id = LOC_Y_DELTA;
327 fe->value = ms->ms_dy;
328 fe->time = time;
329 fe++;
330
331 if (put >= EV_QSIZE) {
332 put = 0;
333 fe = &ms->ms_events.ev_q[0];
334 }
335 any = 1;
336
337 ms->ms_dy = 0;
338 }
339 out:
340 if (any) {
341 ms->ms_ub = ub;
342 ms->ms_events.ev_put = put;
343 EV_WAKEUP(&ms->ms_events);
344 }
345 }
346
347 /*
348 * reschedule handler, or if terminating,
349 * handshake with ms_disable
350 */
351 if (ms->ms_ready)
352 callout_reset(&ms->ms_intr_ch, 2, msintr, ms);
353 else
354 wakeup(ms);
355 }
356
357 int
358 msopen(dev_t dev, int flags, int mode, struct proc *p)
359 {
360 struct ms_softc *sc;
361 struct ms_port *ms;
362 int unit, port;
363
364 unit = MS_UNIT(dev);
365 sc = (struct ms_softc *)getsoftc(ms_cd, unit);
366
367 if (sc == NULL)
368 return(EXDEV);
369
370 port = MS_PORT(dev);
371 ms = &sc->sc_ports[port];
372
373 if (ms->ms_events.ev_io)
374 return(EBUSY);
375
376 /* initialize potgo bits for mouse mode */
377 custom.potgo = custom.potgor | (0xf00 << (port * 4));
378
379 ms->ms_events.ev_io = p;
380 ev_init(&ms->ms_events); /* may cause sleep */
381 ms_enable(ms);
382 return(0);
383 }
384
385 int
386 msclose(dev_t dev, int flags, int mode, struct proc *p)
387 {
388 struct ms_port *ms;
389
390 ms = MS_DEV2MSPORT(dev);
391
392 ms_disable(ms);
393 ev_fini(&ms->ms_events);
394 ms->ms_events.ev_io = NULL;
395 return(0);
396 }
397
398 int
399 msread(dev_t dev, struct uio *uio, int flags)
400 {
401 struct ms_port *ms;
402
403 ms = MS_DEV2MSPORT(dev);
404
405 return(ev_read(&ms->ms_events, uio, flags));
406 }
407
408 int
409 msioctl(dev_t dev, u_long cmd, register caddr_t data, int flag,
410 struct proc *p)
411 {
412 struct ms_port *ms;
413
414 ms = MS_DEV2MSPORT(dev);
415
416 switch (cmd) {
417 case FIONBIO: /* we will remove this someday (soon???) */
418 return(0);
419 case FIOASYNC:
420 ms->ms_events.ev_async = *(int *)data != 0;
421 return(0);
422 case FIOSETOWN:
423 if (-*(int *)data != ms->ms_events.ev_io->p_pgid
424 && *(int *)data != ms->ms_events.ev_io->p_pid)
425 return(EPERM);
426 return(0);
427 case TIOCSPGRP:
428 if (*(int *)data != ms->ms_events.ev_io->p_pgid)
429 return(EPERM);
430 return(0);
431 case VUIDGFORMAT: /* we only do firm_events */
432 *(int *)data = VUID_FIRM_EVENT;
433 return(0);
434 case VUIDSFORMAT:
435 if (*(int *)data != VUID_FIRM_EVENT)
436 return(EINVAL);
437 return(0);
438 }
439 return(ENOTTY);
440 }
441
442 int
443 mspoll(dev_t dev, int events, struct proc *p)
444 {
445 struct ms_port *ms;
446
447 ms = MS_DEV2MSPORT(dev);
448
449 return(ev_poll(&ms->ms_events, events, p));
450 }
451
452 int
453 mskqfilter(dev, kn)
454 dev_t dev;
455 struct knote *kn;
456 {
457 struct ms_port *ms;
458
459 ms = MS_DEV2MSPORT(dev);
460
461 return (ev_kqfilter(&ms->ms_events, kn));
462 }
463