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