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