1/*- 2 * Copyright (c) 2004 M. Warner Losh 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29/* 30 * Copyright 1992 by the University of Guelph 31 * 32 * Permission to use, copy and modify this 33 * software and its documentation for any purpose and without 34 * fee is hereby granted, provided that the above copyright 35 * notice appear in all copies and that both that copyright 36 * notice and this permission notice appear in supporting 37 * documentation. 38 * University of Guelph makes no representations about the suitability of 39 * this software for any purpose. It is provided "as is" 40 * without express or implied warranty. 41 */ 42/* 43 * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and 44 * the X386 port, courtesy of 45 * Rick Macklem, rick@snowhite.cis.uoguelph.ca 46 * Caveats: The driver currently uses spltty(), but doesn't use any 47 * generic tty code. It could use splmse() (that only masks off the 48 * bus mouse interrupt, but that would require hacking in i386/isa/icu.s. 49 * (This may be worth the effort, since the Logitech generates 30/60 50 * interrupts/sec continuously while it is open.) 51 * NB: The ATI has NOT been tested yet! 52 */ 53 54/* 55 * Modification history: 56 * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com) 57 * improved probe based on input from Logitech. 58 * 59 * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu) 60 * fixes to make it work with Microsoft InPort busmouse 61 * 62 * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu) 63 * added patches for new "select" interface 64 * 65 * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu) 66 * changed position of some spl()'s in mseread 67 * 68 * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu) 69 * limit maximum negative x/y value to -127 to work around XFree problem 70 * that causes spurious button pushes. 71 */ 72 73#include <sys/param.h> 74#include <sys/systm.h> 75#include <sys/conf.h> 76#include <sys/kernel.h> 77#include <sys/module.h> 78#include <sys/bus.h> 79#include <sys/poll.h> 80#include <sys/selinfo.h> 81#include <sys/uio.h> 82#include <sys/mouse.h> 83 84#include <machine/bus.h> 85#include <machine/resource.h> 86#include <sys/rman.h> 87 88#include <isa/isavar.h> 89 90#include <dev/mse/msevar.h> 91 92devclass_t mse_devclass; 93 94static d_open_t mseopen; 95static d_close_t mseclose; 96static d_read_t mseread; 97static d_ioctl_t mseioctl; 98static d_poll_t msepoll; 99 100static struct cdevsw mse_cdevsw = { 101 .d_version = D_VERSION, 102 .d_open = mseopen, 103 .d_close = mseclose, 104 .d_read = mseread, 105 .d_ioctl = mseioctl, 106 .d_poll = msepoll, 107 .d_name = "mse", 108}; 109 110static void mseintr(void *); 111static void mseintr_locked(mse_softc_t *sc); 112static void msetimeout(void *); 113 114#define MSE_NBLOCKIO(dev) (dev2unit(dev) != 0) 115 116#define MSEPRI (PZERO + 3) 117 118int 119mse_common_attach(device_t dev) 120{ 121 mse_softc_t *sc; 122 int unit, flags, rid; 123 124 sc = device_get_softc(dev); 125 unit = device_get_unit(dev); 126 mtx_init(&sc->sc_lock, "mse", NULL, MTX_DEF); 127 callout_init_mtx(&sc->sc_callout, &sc->sc_lock, 0); 128 129 rid = 0; 130 sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 131 RF_ACTIVE); 132 if (sc->sc_intr == NULL) { 133 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); 134 mtx_destroy(&sc->sc_lock); 135 return ENXIO; 136 } 137 138 if (bus_setup_intr(dev, sc->sc_intr, INTR_TYPE_TTY | INTR_MPSAFE, 139 NULL, mseintr, sc, &sc->sc_ih)) { 140 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); 141 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr); 142 mtx_destroy(&sc->sc_lock); 143 return ENXIO; 144 } 145 flags = device_get_flags(dev); 146 sc->mode.accelfactor = (flags & MSE_CONFIG_ACCEL) >> 4; 147 148 sc->sc_dev = make_dev(&mse_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 149 "mse%d", unit); 150 sc->sc_dev->si_drv1 = sc; 151 sc->sc_ndev = make_dev(&mse_cdevsw, 1, UID_ROOT, GID_WHEEL, 0600, 152 "nmse%d", unit); 153 sc->sc_ndev->si_drv1 = sc; 154 return 0; 155} 156 157int 158mse_detach(device_t dev) 159{ 160 mse_softc_t *sc; 161 int rid; 162 163 sc = device_get_softc(dev); 164 MSE_LOCK(sc); 165 if (sc->sc_flags & MSESC_OPEN) { 166 MSE_UNLOCK(sc); 167 return EBUSY; 168 } 169 170 /* Sabotage subsequent opens. */ 171 sc->sc_mousetype = MSE_NONE; 172 MSE_UNLOCK(sc); 173 174 destroy_dev(sc->sc_dev); 175 destroy_dev(sc->sc_ndev); 176 177 rid = 0; 178 bus_teardown_intr(dev, sc->sc_intr, sc->sc_ih); 179 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr); 180 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); 181 182 callout_drain(&sc->sc_callout); 183 mtx_destroy(&sc->sc_lock); 184 185 return 0; 186} 187 188/* 189 * Exclusive open the mouse, initialize it and enable interrupts. 190 */ 191static int 192mseopen(struct cdev *dev, int flags, int fmt, struct thread *td) 193{ 194 mse_softc_t *sc = dev->si_drv1; 195 196 MSE_LOCK(sc); 197 if (sc->sc_mousetype == MSE_NONE) { 198 MSE_UNLOCK(sc); 199 return (ENXIO); 200 } 201 if (sc->sc_flags & MSESC_OPEN) { 202 MSE_UNLOCK(sc); 203 return (EBUSY); 204 } 205 sc->sc_flags |= MSESC_OPEN; 206 sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS; 207 sc->sc_deltax = sc->sc_deltay = 0; 208 sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; 209 sc->sc_watchdog = FALSE; 210 callout_reset(&sc->sc_callout, hz * 2, msetimeout, dev); 211 sc->mode.level = 0; 212 sc->status.flags = 0; 213 sc->status.button = sc->status.obutton = 0; 214 sc->status.dx = sc->status.dy = sc->status.dz = 0; 215 216 /* 217 * Initialize mouse interface and enable interrupts. 218 */ 219 (*sc->sc_enablemouse)(sc->sc_port); 220 MSE_UNLOCK(sc); 221 return (0); 222} 223 224/* 225 * mseclose: just turn off mouse innterrupts. 226 */ 227static int 228mseclose(struct cdev *dev, int flags, int fmt, struct thread *td) 229{ 230 mse_softc_t *sc = dev->si_drv1; 231 232 MSE_LOCK(sc); 233 callout_stop(&sc->sc_callout); 234 (*sc->sc_disablemouse)(sc->sc_port); 235 sc->sc_flags &= ~MSESC_OPEN; 236 MSE_UNLOCK(sc); 237 return(0); 238} 239 240/* 241 * mseread: return mouse info using the MSC serial protocol, but without 242 * using bytes 4 and 5. 243 * (Yes this is cheesy, but it makes the X386 server happy, so...) 244 */ 245static int 246mseread(struct cdev *dev, struct uio *uio, int ioflag) 247{ 248 mse_softc_t *sc = dev->si_drv1; 249 int xfer, error; 250 251 /* 252 * If there are no protocol bytes to be read, set up a new protocol 253 * packet. 254 */ 255 MSE_LOCK(sc); 256 while (sc->sc_flags & MSESC_READING) { 257 if (MSE_NBLOCKIO(dev)) { 258 MSE_UNLOCK(sc); 259 return (0); 260 } 261 sc->sc_flags |= MSESC_WANT; 262 error = mtx_sleep(sc, &sc->sc_lock, MSEPRI | PCATCH, "mseread", 263 0); 264 if (error) { 265 MSE_UNLOCK(sc); 266 return (error); 267 } 268 } 269 sc->sc_flags |= MSESC_READING; 270 xfer = 0; 271 if (sc->sc_bytesread >= sc->mode.packetsize) { 272 while (sc->sc_deltax == 0 && sc->sc_deltay == 0 && 273 (sc->sc_obuttons ^ sc->sc_buttons) == 0) { 274 if (MSE_NBLOCKIO(dev)) 275 goto out; 276 sc->sc_flags |= MSESC_WANT; 277 error = mtx_sleep(sc, &sc->sc_lock, MSEPRI | PCATCH, 278 "mseread", 0); 279 if (error) 280 goto out; 281 } 282 283 /* 284 * Generate protocol bytes. 285 * For some reason X386 expects 5 bytes but never uses 286 * the fourth or fifth? 287 */ 288 sc->sc_bytes[0] = sc->mode.syncmask[1] 289 | (sc->sc_buttons & ~sc->mode.syncmask[0]); 290 if (sc->sc_deltax > 127) 291 sc->sc_deltax = 127; 292 if (sc->sc_deltax < -127) 293 sc->sc_deltax = -127; 294 sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */ 295 if (sc->sc_deltay > 127) 296 sc->sc_deltay = 127; 297 if (sc->sc_deltay < -127) 298 sc->sc_deltay = -127; 299 sc->sc_bytes[1] = sc->sc_deltax; 300 sc->sc_bytes[2] = sc->sc_deltay; 301 sc->sc_bytes[3] = sc->sc_bytes[4] = 0; 302 sc->sc_bytes[5] = sc->sc_bytes[6] = 0; 303 sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS; 304 sc->sc_obuttons = sc->sc_buttons; 305 sc->sc_deltax = sc->sc_deltay = 0; 306 sc->sc_bytesread = 0; 307 } 308 xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread); 309 MSE_UNLOCK(sc); 310 error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio); 311 MSE_LOCK(sc); 312out: 313 sc->sc_flags &= ~MSESC_READING; 314 if (error == 0) 315 sc->sc_bytesread += xfer; 316 if (sc->sc_flags & MSESC_WANT) { 317 sc->sc_flags &= ~MSESC_WANT; 318 MSE_UNLOCK(sc); 319 wakeup(sc); 320 } else 321 MSE_UNLOCK(sc); 322 return (error); 323} 324 325/* 326 * mseioctl: process ioctl commands. 327 */ 328static int 329mseioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 330{ 331 mse_softc_t *sc = dev->si_drv1; 332 mousestatus_t status; 333 int err = 0; 334 335 switch (cmd) { 336 337 case MOUSE_GETHWINFO: 338 MSE_LOCK(sc); 339 *(mousehw_t *)addr = sc->hw; 340 if (sc->mode.level == 0) 341 ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; 342 MSE_UNLOCK(sc); 343 break; 344 345 case MOUSE_GETMODE: 346 MSE_LOCK(sc); 347 *(mousemode_t *)addr = sc->mode; 348 switch (sc->mode.level) { 349 case 0: 350 break; 351 case 1: 352 ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; 353 ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; 354 ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; 355 break; 356 } 357 MSE_UNLOCK(sc); 358 break; 359 360 case MOUSE_SETMODE: 361 switch (((mousemode_t *)addr)->level) { 362 case 0: 363 case 1: 364 break; 365 default: 366 return (EINVAL); 367 } 368 MSE_LOCK(sc); 369 if (((mousemode_t *)addr)->accelfactor < -1) { 370 MSE_UNLOCK(sc); 371 return (EINVAL); 372 } else if (((mousemode_t *)addr)->accelfactor >= 0) 373 sc->mode.accelfactor = 374 ((mousemode_t *)addr)->accelfactor; 375 sc->mode.level = ((mousemode_t *)addr)->level; 376 switch (sc->mode.level) { 377 case 0: 378 sc->sc_bytesread = sc->mode.packetsize 379 = MOUSE_MSC_PACKETSIZE; 380 break; 381 case 1: 382 sc->sc_bytesread = sc->mode.packetsize 383 = MOUSE_SYS_PACKETSIZE; 384 break; 385 } 386 MSE_UNLOCK(sc); 387 break; 388 389 case MOUSE_GETLEVEL: 390 MSE_LOCK(sc); 391 *(int *)addr = sc->mode.level; 392 MSE_UNLOCK(sc); 393 break; 394 395 case MOUSE_SETLEVEL: 396 switch (*(int *)addr) { 397 case 0: 398 MSE_LOCK(sc); 399 sc->mode.level = *(int *)addr; 400 sc->sc_bytesread = sc->mode.packetsize 401 = MOUSE_MSC_PACKETSIZE; 402 MSE_UNLOCK(sc); 403 break; 404 case 1: 405 MSE_LOCK(sc); 406 sc->mode.level = *(int *)addr; 407 sc->sc_bytesread = sc->mode.packetsize 408 = MOUSE_SYS_PACKETSIZE; 409 MSE_UNLOCK(sc); 410 break; 411 default: 412 return (EINVAL); 413 } 414 break; 415 416 case MOUSE_GETSTATUS: 417 MSE_LOCK(sc); 418 status = sc->status; 419 sc->status.flags = 0; 420 sc->status.obutton = sc->status.button; 421 sc->status.button = 0; 422 sc->status.dx = 0; 423 sc->status.dy = 0; 424 sc->status.dz = 0; 425 MSE_UNLOCK(sc); 426 *(mousestatus_t *)addr = status; 427 break; 428 429 case MOUSE_READSTATE: 430 case MOUSE_READDATA: 431 return (ENODEV); 432 433#if (defined(MOUSE_GETVARS)) 434 case MOUSE_GETVARS: 435 case MOUSE_SETVARS: 436 return (ENODEV); 437#endif 438 439 default: 440 return (ENOTTY); 441 } 442 return (err); 443} 444 445/* 446 * msepoll: check for mouse input to be processed. 447 */ 448static int 449msepoll(struct cdev *dev, int events, struct thread *td) 450{ 451 mse_softc_t *sc = dev->si_drv1; 452 int revents = 0; 453 454 MSE_LOCK(sc); 455 if (events & (POLLIN | POLLRDNORM)) { 456 if (sc->sc_bytesread != sc->mode.packetsize || 457 sc->sc_deltax != 0 || sc->sc_deltay != 0 || 458 (sc->sc_obuttons ^ sc->sc_buttons) != 0) 459 revents |= events & (POLLIN | POLLRDNORM); 460 else 461 selrecord(td, &sc->sc_selp); 462 } 463 MSE_UNLOCK(sc); 464 return (revents); 465} 466 467/* 468 * msetimeout: watchdog timer routine. 469 */ 470static void 471msetimeout(void *arg) 472{ 473 struct cdev *dev; 474 mse_softc_t *sc; 475 476 dev = (struct cdev *)arg; 477 sc = dev->si_drv1; 478 MSE_ASSERT_LOCKED(sc); 479 if (sc->sc_watchdog) { 480 if (bootverbose) 481 printf("%s: lost interrupt?\n", devtoname(dev)); 482 mseintr_locked(sc); 483 } 484 sc->sc_watchdog = TRUE; 485 callout_schedule(&sc->sc_callout, hz); 486} 487 488/* 489 * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative. 490 */ 491static void 492mseintr(void *arg) 493{ 494 mse_softc_t *sc = arg; 495 496 MSE_LOCK(sc); 497 mseintr_locked(sc); 498 MSE_UNLOCK(sc); 499} 500 501static void 502mseintr_locked(mse_softc_t *sc) 503{ 504 /* 505 * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP) 506 * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). 507 */ 508 static int butmap[8] = { 509 0, 510 MOUSE_BUTTON3DOWN, 511 MOUSE_BUTTON2DOWN, 512 MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, 513 MOUSE_BUTTON1DOWN, 514 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, 515 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, 516 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN 517 }; 518 int dx, dy, but; 519 int sign; 520 521#ifdef DEBUG 522 static int mse_intrcnt = 0; 523 if((mse_intrcnt++ % 10000) == 0) 524 printf("mseintr\n"); 525#endif /* DEBUG */ 526 if ((sc->sc_flags & MSESC_OPEN) == 0) 527 return; 528 529 (*sc->sc_getmouse)(sc->sc_port, &dx, &dy, &but); 530 if (sc->mode.accelfactor > 0) { 531 sign = (dx < 0); 532 dx = dx * dx / sc->mode.accelfactor; 533 if (dx == 0) 534 dx = 1; 535 if (sign) 536 dx = -dx; 537 sign = (dy < 0); 538 dy = dy * dy / sc->mode.accelfactor; 539 if (dy == 0) 540 dy = 1; 541 if (sign) 542 dy = -dy; 543 } 544 sc->sc_deltax += dx; 545 sc->sc_deltay += dy; 546 sc->sc_buttons = but; 547 548 but = butmap[~but & MOUSE_MSC_BUTTONS]; 549 sc->status.dx += dx; 550 sc->status.dy += dy; 551 sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0) 552 | (sc->status.button ^ but); 553 sc->status.button = but; 554 555 sc->sc_watchdog = FALSE; 556 557 /* 558 * If mouse state has changed, wake up anyone wanting to know. 559 */ 560 if (sc->sc_deltax != 0 || sc->sc_deltay != 0 || 561 (sc->sc_obuttons ^ sc->sc_buttons) != 0) { 562 if (sc->sc_flags & MSESC_WANT) { 563 sc->sc_flags &= ~MSESC_WANT; 564 wakeup(sc); 565 } 566 selwakeuppri(&sc->sc_selp, MSEPRI); 567 } 568} 569