1 /* $NetBSD: ms.c,v 1.26 2003/08/07 16:26:42 agc 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.26 2003/08/07 16:26:42 agc 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 TIOCSPGRP: 423 if (*(int *)data != ms->ms_events.ev_io->p_pgid) 424 return(EPERM); 425 return(0); 426 case VUIDGFORMAT: /* we only do firm_events */ 427 *(int *)data = VUID_FIRM_EVENT; 428 return(0); 429 case VUIDSFORMAT: 430 if (*(int *)data != VUID_FIRM_EVENT) 431 return(EINVAL); 432 return(0); 433 } 434 return(ENOTTY); 435 } 436 437 int 438 mspoll(dev_t dev, int events, struct proc *p) 439 { 440 struct ms_port *ms; 441 442 ms = MS_DEV2MSPORT(dev); 443 444 return(ev_poll(&ms->ms_events, events, p)); 445 } 446 447 int 448 mskqfilter(dev, kn) 449 dev_t dev; 450 struct knote *kn; 451 { 452 struct ms_port *ms; 453 454 ms = MS_DEV2MSPORT(dev); 455 456 return (ev_kqfilter(&ms->ms_events, kn)); 457 } 458