mse.c revision 166901
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: head/sys/dev/mse/mse.c 166901 2007-02-23 12:19:07Z piso $ 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_flags = D_NEEDGIANT, 103 .d_open = mseopen, 104 .d_close = mseclose, 105 .d_read = mseread, 106 .d_ioctl = mseioctl, 107 .d_poll = msepoll, 108 .d_name = "mse", 109}; 110 111static void mseintr(void *); 112static timeout_t msetimeout; 113 114#define MSE_UNIT(dev) (minor(dev) >> 1) 115#define MSE_NBLOCKIO(dev) (minor(dev) & 0x1) 116 117#define MSEPRI (PZERO + 3) 118 119int 120mse_common_attach(device_t dev) 121{ 122 mse_softc_t *sc; 123 int unit, flags, rid; 124 125 sc = device_get_softc(dev); 126 unit = device_get_unit(dev); 127 128 rid = 0; 129 sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 130 RF_ACTIVE); 131 if (sc->sc_intr == NULL) { 132 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); 133 return ENXIO; 134 } 135 136 if (bus_setup_intr(dev, sc->sc_intr, 137 INTR_TYPE_TTY, NULL, mseintr, sc, &sc->sc_ih)) { 138 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); 139 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr); 140 return ENXIO; 141 } 142 flags = device_get_flags(dev); 143 sc->mode.accelfactor = (flags & MSE_CONFIG_ACCEL) >> 4; 144 callout_handle_init(&sc->sc_callout); 145 146 sc->sc_dev = make_dev(&mse_cdevsw, unit << 1, 0, 0, 0600, 147 "mse%d", unit); 148 sc->sc_ndev = make_dev(&mse_cdevsw, (unit<<1)+1, 0, 0, 0600, 149 "nmse%d", unit); 150 return 0; 151} 152 153/* 154 * Exclusive open the mouse, initialize it and enable interrupts. 155 */ 156static int 157mseopen(struct cdev *dev, int flags, int fmt, struct thread *td) 158{ 159 mse_softc_t *sc; 160 int s; 161 162 sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); 163 if (sc == NULL) 164 return (ENXIO); 165 if (sc->sc_mousetype == MSE_NONE) 166 return (ENXIO); 167 if (sc->sc_flags & MSESC_OPEN) 168 return (EBUSY); 169 sc->sc_flags |= MSESC_OPEN; 170 sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS; 171 sc->sc_deltax = sc->sc_deltay = 0; 172 sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; 173 sc->sc_watchdog = FALSE; 174 sc->sc_callout = timeout(msetimeout, dev, hz*2); 175 sc->mode.level = 0; 176 sc->status.flags = 0; 177 sc->status.button = sc->status.obutton = 0; 178 sc->status.dx = sc->status.dy = sc->status.dz = 0; 179 180 /* 181 * Initialize mouse interface and enable interrupts. 182 */ 183 s = spltty(); 184 (*sc->sc_enablemouse)(sc->sc_iot, sc->sc_ioh); 185 splx(s); 186 return (0); 187} 188 189/* 190 * mseclose: just turn off mouse innterrupts. 191 */ 192static int 193mseclose(struct cdev *dev, int flags, int fmt, struct thread *td) 194{ 195 mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); 196 int s; 197 198 untimeout(msetimeout, dev, sc->sc_callout); 199 callout_handle_init(&sc->sc_callout); 200 s = spltty(); 201 (*sc->sc_disablemouse)(sc->sc_iot, sc->sc_ioh); 202 sc->sc_flags &= ~MSESC_OPEN; 203 splx(s); 204 return(0); 205} 206 207/* 208 * mseread: return mouse info using the MSC serial protocol, but without 209 * using bytes 4 and 5. 210 * (Yes this is cheesy, but it makes the X386 server happy, so...) 211 */ 212static int 213mseread(struct cdev *dev, struct uio *uio, int ioflag) 214{ 215 mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); 216 int xfer, s, error; 217 218 /* 219 * If there are no protocol bytes to be read, set up a new protocol 220 * packet. 221 */ 222 s = spltty(); /* XXX Should be its own spl, but where is imlXX() */ 223 if (sc->sc_bytesread >= sc->mode.packetsize) { 224 while (sc->sc_deltax == 0 && sc->sc_deltay == 0 && 225 (sc->sc_obuttons ^ sc->sc_buttons) == 0) { 226 if (MSE_NBLOCKIO(dev)) { 227 splx(s); 228 return (0); 229 } 230 sc->sc_flags |= MSESC_WANT; 231 error = tsleep(sc, MSEPRI | PCATCH, 232 "mseread", 0); 233 if (error) { 234 splx(s); 235 return (error); 236 } 237 } 238 239 /* 240 * Generate protocol bytes. 241 * For some reason X386 expects 5 bytes but never uses 242 * the fourth or fifth? 243 */ 244 sc->sc_bytes[0] = sc->mode.syncmask[1] 245 | (sc->sc_buttons & ~sc->mode.syncmask[0]); 246 if (sc->sc_deltax > 127) 247 sc->sc_deltax = 127; 248 if (sc->sc_deltax < -127) 249 sc->sc_deltax = -127; 250 sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */ 251 if (sc->sc_deltay > 127) 252 sc->sc_deltay = 127; 253 if (sc->sc_deltay < -127) 254 sc->sc_deltay = -127; 255 sc->sc_bytes[1] = sc->sc_deltax; 256 sc->sc_bytes[2] = sc->sc_deltay; 257 sc->sc_bytes[3] = sc->sc_bytes[4] = 0; 258 sc->sc_bytes[5] = sc->sc_bytes[6] = 0; 259 sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS; 260 sc->sc_obuttons = sc->sc_buttons; 261 sc->sc_deltax = sc->sc_deltay = 0; 262 sc->sc_bytesread = 0; 263 } 264 splx(s); 265 xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread); 266 error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio); 267 if (error) 268 return (error); 269 sc->sc_bytesread += xfer; 270 return(0); 271} 272 273/* 274 * mseioctl: process ioctl commands. 275 */ 276static int 277mseioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 278{ 279 mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); 280 mousestatus_t status; 281 int err = 0; 282 int s; 283 284 switch (cmd) { 285 286 case MOUSE_GETHWINFO: 287 s = spltty(); 288 *(mousehw_t *)addr = sc->hw; 289 if (sc->mode.level == 0) 290 ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; 291 splx(s); 292 break; 293 294 case MOUSE_GETMODE: 295 s = spltty(); 296 *(mousemode_t *)addr = sc->mode; 297 switch (sc->mode.level) { 298 case 0: 299 break; 300 case 1: 301 ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; 302 ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; 303 ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; 304 break; 305 } 306 splx(s); 307 break; 308 309 case MOUSE_SETMODE: 310 switch (((mousemode_t *)addr)->level) { 311 case 0: 312 case 1: 313 break; 314 default: 315 return (EINVAL); 316 } 317 if (((mousemode_t *)addr)->accelfactor < -1) 318 return (EINVAL); 319 else if (((mousemode_t *)addr)->accelfactor >= 0) 320 sc->mode.accelfactor = 321 ((mousemode_t *)addr)->accelfactor; 322 sc->mode.level = ((mousemode_t *)addr)->level; 323 switch (sc->mode.level) { 324 case 0: 325 sc->sc_bytesread = sc->mode.packetsize 326 = MOUSE_MSC_PACKETSIZE; 327 break; 328 case 1: 329 sc->sc_bytesread = sc->mode.packetsize 330 = MOUSE_SYS_PACKETSIZE; 331 break; 332 } 333 break; 334 335 case MOUSE_GETLEVEL: 336 *(int *)addr = sc->mode.level; 337 break; 338 339 case MOUSE_SETLEVEL: 340 switch (*(int *)addr) { 341 case 0: 342 sc->mode.level = *(int *)addr; 343 sc->sc_bytesread = sc->mode.packetsize 344 = MOUSE_MSC_PACKETSIZE; 345 break; 346 case 1: 347 sc->mode.level = *(int *)addr; 348 sc->sc_bytesread = sc->mode.packetsize 349 = MOUSE_SYS_PACKETSIZE; 350 break; 351 default: 352 return (EINVAL); 353 } 354 break; 355 356 case MOUSE_GETSTATUS: 357 s = spltty(); 358 status = sc->status; 359 sc->status.flags = 0; 360 sc->status.obutton = sc->status.button; 361 sc->status.button = 0; 362 sc->status.dx = 0; 363 sc->status.dy = 0; 364 sc->status.dz = 0; 365 splx(s); 366 *(mousestatus_t *)addr = status; 367 break; 368 369 case MOUSE_READSTATE: 370 case MOUSE_READDATA: 371 return (ENODEV); 372 373#if (defined(MOUSE_GETVARS)) 374 case MOUSE_GETVARS: 375 case MOUSE_SETVARS: 376 return (ENODEV); 377#endif 378 379 default: 380 return (ENOTTY); 381 } 382 return (err); 383} 384 385/* 386 * msepoll: check for mouse input to be processed. 387 */ 388static int 389msepoll(struct cdev *dev, int events, struct thread *td) 390{ 391 mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); 392 int s; 393 int revents = 0; 394 395 s = spltty(); 396 if (events & (POLLIN | POLLRDNORM)) { 397 if (sc->sc_bytesread != sc->mode.packetsize || 398 sc->sc_deltax != 0 || sc->sc_deltay != 0 || 399 (sc->sc_obuttons ^ sc->sc_buttons) != 0) 400 revents |= events & (POLLIN | POLLRDNORM); 401 else { 402 /* 403 * Since this is an exclusive open device, any previous 404 * proc pointer is trash now, so we can just assign it. 405 */ 406 selrecord(td, &sc->sc_selp); 407 } 408 } 409 splx(s); 410 return (revents); 411} 412 413/* 414 * msetimeout: watchdog timer routine. 415 */ 416static void 417msetimeout(void *arg) 418{ 419 struct cdev *dev; 420 mse_softc_t *sc; 421 422 dev = (struct cdev *)arg; 423 sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); 424 if (sc->sc_watchdog) { 425 if (bootverbose) 426 printf("mse%d: lost interrupt?\n", MSE_UNIT(dev)); 427 mseintr(sc); 428 } 429 sc->sc_watchdog = TRUE; 430 sc->sc_callout = timeout(msetimeout, dev, hz); 431} 432 433/* 434 * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative. 435 */ 436static void 437mseintr(void *arg) 438{ 439 /* 440 * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP) 441 * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). 442 */ 443 static int butmap[8] = { 444 0, 445 MOUSE_BUTTON3DOWN, 446 MOUSE_BUTTON2DOWN, 447 MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, 448 MOUSE_BUTTON1DOWN, 449 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, 450 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, 451 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN 452 }; 453 mse_softc_t *sc = arg; 454 int dx, dy, but; 455 int sign; 456 457#ifdef DEBUG 458 static int mse_intrcnt = 0; 459 if((mse_intrcnt++ % 10000) == 0) 460 printf("mseintr\n"); 461#endif /* DEBUG */ 462 if ((sc->sc_flags & MSESC_OPEN) == 0) 463 return; 464 465 (*sc->sc_getmouse)(sc->sc_iot, sc->sc_ioh, &dx, &dy, &but); 466 if (sc->mode.accelfactor > 0) { 467 sign = (dx < 0); 468 dx = dx * dx / sc->mode.accelfactor; 469 if (dx == 0) 470 dx = 1; 471 if (sign) 472 dx = -dx; 473 sign = (dy < 0); 474 dy = dy * dy / sc->mode.accelfactor; 475 if (dy == 0) 476 dy = 1; 477 if (sign) 478 dy = -dy; 479 } 480 sc->sc_deltax += dx; 481 sc->sc_deltay += dy; 482 sc->sc_buttons = but; 483 484 but = butmap[~but & MOUSE_MSC_BUTTONS]; 485 sc->status.dx += dx; 486 sc->status.dy += dy; 487 sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0) 488 | (sc->status.button ^ but); 489 sc->status.button = but; 490 491 sc->sc_watchdog = FALSE; 492 493 /* 494 * If mouse state has changed, wake up anyone wanting to know. 495 */ 496 if (sc->sc_deltax != 0 || sc->sc_deltay != 0 || 497 (sc->sc_obuttons ^ sc->sc_buttons) != 0) { 498 if (sc->sc_flags & MSESC_WANT) { 499 sc->sc_flags &= ~MSESC_WANT; 500 wakeup(sc); 501 } 502 selwakeuppri(&sc->sc_selp, MSEPRI); 503 } 504} 505