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