| 1 | /* |
| 2 | * Copyright 1992 by the University of Guelph |
| 3 | * |
| 4 | * Permission to use, copy and modify this |
| 5 | * software and its documentation for any purpose and without |
| 6 | * fee is hereby granted, provided that the above copyright |
| 7 | * notice appear in all copies and that both that copyright |
| 8 | * notice and this permission notice appear in supporting |
| 9 | * documentation. |
| 10 | * University of Guelph makes no representations about the suitability of |
| 11 | * this software for any purpose. It is provided "as is" |
| 12 | * without express or implied warranty. |
| 13 | * |
| 14 | * $FreeBSD: src/sys/i386/isa/mse.c,v 1.49.2.1 2000/03/20 13:58:47 yokota Exp $ |
| 15 | */ |
| 16 | /* |
| 17 | * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and |
| 18 | * the X386 port, courtesy of |
| 19 | * Rick Macklem, rick@snowhite.cis.uoguelph.ca |
| 20 | * Caveats: The driver currently uses spltty(), but doesn't use any |
| 21 | * generic tty code. It could use splmse() (that only masks off the |
| 22 | * bus mouse interrupt, but that would require hacking in i386/isa/icu.s. |
| 23 | * (This may be worth the effort, since the Logitech generates 30/60 |
| 24 | * interrupts/sec continuously while it is open.) |
| 25 | * NB: The ATI has NOT been tested yet! |
| 26 | */ |
| 27 | |
| 28 | /* |
| 29 | * Modification history: |
| 30 | * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com) |
| 31 | * improved probe based on input from Logitech. |
| 32 | * |
| 33 | * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu) |
| 34 | * fixes to make it work with Microsoft InPort busmouse |
| 35 | * |
| 36 | * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu) |
| 37 | * added patches for new "select" interface |
| 38 | * |
| 39 | * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu) |
| 40 | * changed position of some spl()'s in mseread |
| 41 | * |
| 42 | * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu) |
| 43 | * limit maximum negative x/y value to -127 to work around XFree problem |
| 44 | * that causes spurious button pushes. |
| 45 | */ |
| 46 | |
| 47 | #include <sys/param.h> |
| 48 | #include <sys/systm.h> |
| 49 | #include <sys/conf.h> |
| 50 | #include <sys/device.h> |
| 51 | #include <sys/kernel.h> |
| 52 | #include <sys/bus.h> |
| 53 | #include <sys/event.h> |
| 54 | #include <sys/uio.h> |
| 55 | #include <sys/rman.h> |
| 56 | #include <sys/thread2.h> |
| 57 | |
| 58 | #include <machine/clock.h> |
| 59 | #include <sys/mouse.h> |
| 60 | |
| 61 | #include <bus/isa/isavar.h> |
| 62 | |
| 63 | /* driver configuration flags (config) */ |
| 64 | #define MSE_CONFIG_ACCEL 0x00f0 /* acceleration factor */ |
| 65 | #define MSE_CONFIG_FLAGS (MSE_CONFIG_ACCEL) |
| 66 | |
| 67 | /* |
| 68 | * Software control structure for mouse. The sc_enablemouse(), |
| 69 | * sc_disablemouse() and sc_getmouse() routines must be called spl'd(). |
| 70 | */ |
| 71 | typedef struct mse_softc { |
| 72 | int sc_flags; |
| 73 | int sc_mousetype; |
| 74 | struct kqinfo sc_kqp; |
| 75 | struct resource *sc_port; |
| 76 | struct resource *sc_intr; |
| 77 | bus_space_tag_t sc_iot; |
| 78 | bus_space_handle_t sc_ioh; |
| 79 | void *sc_ih; |
| 80 | void (*sc_enablemouse) (bus_space_tag_t t, |
| 81 | bus_space_handle_t h); |
| 82 | void (*sc_disablemouse) (bus_space_tag_t t, |
| 83 | bus_space_handle_t h); |
| 84 | void (*sc_getmouse) (bus_space_tag_t t, |
| 85 | bus_space_handle_t h, |
| 86 | int *dx, int *dy, int *but); |
| 87 | int sc_deltax; |
| 88 | int sc_deltay; |
| 89 | int sc_obuttons; |
| 90 | int sc_buttons; |
| 91 | int sc_bytesread; |
| 92 | u_char sc_bytes[MOUSE_SYS_PACKETSIZE]; |
| 93 | struct callout sc_callout; |
| 94 | int sc_watchdog; |
| 95 | mousehw_t hw; |
| 96 | mousemode_t mode; |
| 97 | mousestatus_t status; |
| 98 | } mse_softc_t; |
| 99 | |
| 100 | static devclass_t mse_devclass; |
| 101 | |
| 102 | static int mse_probe (device_t dev); |
| 103 | static int mse_attach (device_t dev); |
| 104 | static int mse_detach (device_t dev); |
| 105 | |
| 106 | static device_method_t mse_methods[] = { |
| 107 | DEVMETHOD(device_probe, mse_probe), |
| 108 | DEVMETHOD(device_attach, mse_attach), |
| 109 | DEVMETHOD(device_detach, mse_detach), |
| 110 | { 0, 0 } |
| 111 | }; |
| 112 | |
| 113 | static driver_t mse_driver = { |
| 114 | "mse", |
| 115 | mse_methods, |
| 116 | sizeof(mse_softc_t), |
| 117 | }; |
| 118 | |
| 119 | DRIVER_MODULE(mse, isa, mse_driver, mse_devclass, NULL, NULL); |
| 120 | |
| 121 | static struct isa_pnp_id mse_ids[] = { |
| 122 | { 0x000fd041, "Bus mouse" }, /* PNP0F00 */ |
| 123 | { 0x020fd041, "InPort mouse" }, /* PNP0F02 */ |
| 124 | { 0x0d0fd041, "InPort mouse compatible" }, /* PNP0F0D */ |
| 125 | { 0x110fd041, "Bus mouse compatible" }, /* PNP0F11 */ |
| 126 | { 0x150fd041, "Logitech bus mouse" }, /* PNP0F15 */ |
| 127 | { 0x180fd041, "Logitech bus mouse compatible" },/* PNP0F18 */ |
| 128 | { 0 } |
| 129 | }; |
| 130 | |
| 131 | static d_open_t mseopen; |
| 132 | static d_close_t mseclose; |
| 133 | static d_read_t mseread; |
| 134 | static d_ioctl_t mseioctl; |
| 135 | static d_kqfilter_t msekqfilter; |
| 136 | |
| 137 | static void msefilter_detach(struct knote *); |
| 138 | static int msefilter(struct knote *, long); |
| 139 | |
| 140 | static struct dev_ops mse_ops = { |
| 141 | { "mse", 0, 0 }, |
| 142 | .d_open = mseopen, |
| 143 | .d_close = mseclose, |
| 144 | .d_read = mseread, |
| 145 | .d_ioctl = mseioctl, |
| 146 | .d_kqfilter = msekqfilter |
| 147 | }; |
| 148 | |
| 149 | static void mseintr (void *); |
| 150 | static timeout_t msetimeout; |
| 151 | |
| 152 | /* Flags */ |
| 153 | #define MSESC_OPEN 0x1 |
| 154 | #define MSESC_WANT 0x2 |
| 155 | |
| 156 | /* and Mouse Types */ |
| 157 | #define MSE_NONE 0 /* don't move this! */ |
| 158 | #define MSE_LOGITECH 0x1 |
| 159 | #define MSE_ATIINPORT 0x2 |
| 160 | #define MSE_LOGI_SIG 0xA5 |
| 161 | |
| 162 | #define MSE_PORTA 0 |
| 163 | #define MSE_PORTB 1 |
| 164 | #define MSE_PORTC 2 |
| 165 | #define MSE_PORTD 3 |
| 166 | #define MSE_IOSIZE 4 |
| 167 | |
| 168 | #define MSE_UNIT(dev) (minor(dev) >> 1) |
| 169 | #define MSE_NBLOCKIO(dev) (minor(dev) & 0x1) |
| 170 | |
| 171 | /* |
| 172 | * Logitech bus mouse definitions |
| 173 | */ |
| 174 | #define MSE_SETUP 0x91 /* What does this mean? */ |
| 175 | /* The definition for the control port */ |
| 176 | /* is as follows: */ |
| 177 | |
| 178 | /* D7 = Mode set flag (1 = active) */ |
| 179 | /* D6,D5 = Mode selection (port A) */ |
| 180 | /* 00 = Mode 0 = Basic I/O */ |
| 181 | /* 01 = Mode 1 = Strobed I/O */ |
| 182 | /* 10 = Mode 2 = Bi-dir bus */ |
| 183 | /* D4 = Port A direction (1 = input)*/ |
| 184 | /* D3 = Port C (upper 4 bits) */ |
| 185 | /* direction. (1 = input) */ |
| 186 | /* D2 = Mode selection (port B & C) */ |
| 187 | /* 0 = Mode 0 = Basic I/O */ |
| 188 | /* 1 = Mode 1 = Strobed I/O */ |
| 189 | /* D1 = Port B direction (1 = input)*/ |
| 190 | /* D0 = Port C (lower 4 bits) */ |
| 191 | /* direction. (1 = input) */ |
| 192 | |
| 193 | /* So 91 means Basic I/O on all 3 ports,*/ |
| 194 | /* Port A is an input port, B is an */ |
| 195 | /* output port, C is split with upper */ |
| 196 | /* 4 bits being an output port and lower*/ |
| 197 | /* 4 bits an input port, and enable the */ |
| 198 | /* sucker. */ |
| 199 | /* Courtesy Intel 8255 databook. Lars */ |
| 200 | #define MSE_HOLD 0x80 |
| 201 | #define MSE_RXLOW 0x00 |
| 202 | #define MSE_RXHIGH 0x20 |
| 203 | #define MSE_RYLOW 0x40 |
| 204 | #define MSE_RYHIGH 0x60 |
| 205 | #define MSE_DISINTR 0x10 |
| 206 | #define MSE_INTREN 0x00 |
| 207 | |
| 208 | static int mse_probelogi (device_t dev, mse_softc_t *sc); |
| 209 | static void mse_disablelogi (bus_space_tag_t t, |
| 210 | bus_space_handle_t h); |
| 211 | static void mse_getlogi (bus_space_tag_t t, |
| 212 | bus_space_handle_t h, |
| 213 | int *dx, int *dy, int *but); |
| 214 | static void mse_enablelogi (bus_space_tag_t t, |
| 215 | bus_space_handle_t h); |
| 216 | |
| 217 | /* |
| 218 | * ATI Inport mouse definitions |
| 219 | */ |
| 220 | #define MSE_INPORT_RESET 0x80 |
| 221 | #define MSE_INPORT_STATUS 0x00 |
| 222 | #define MSE_INPORT_DX 0x01 |
| 223 | #define MSE_INPORT_DY 0x02 |
| 224 | #define MSE_INPORT_MODE 0x07 |
| 225 | #define MSE_INPORT_HOLD 0x20 |
| 226 | #define MSE_INPORT_INTREN 0x09 |
| 227 | |
| 228 | static int mse_probeati (device_t dev, mse_softc_t *sc); |
| 229 | static void mse_enableati (bus_space_tag_t t, |
| 230 | bus_space_handle_t h); |
| 231 | static void mse_disableati (bus_space_tag_t t, |
| 232 | bus_space_handle_t h); |
| 233 | static void mse_getati (bus_space_tag_t t, |
| 234 | bus_space_handle_t h, |
| 235 | int *dx, int *dy, int *but); |
| 236 | |
| 237 | /* |
| 238 | * Table of mouse types. |
| 239 | * Keep the Logitech last, since I haven't figured out how to probe it |
| 240 | * properly yet. (Someday I'll have the documentation.) |
| 241 | */ |
| 242 | static struct mse_types { |
| 243 | int m_type; /* Type of bus mouse */ |
| 244 | int (*m_probe) (device_t dev, mse_softc_t *sc); |
| 245 | /* Probe routine to test for it */ |
| 246 | void (*m_enable) (bus_space_tag_t t, bus_space_handle_t h); |
| 247 | /* Start routine */ |
| 248 | void (*m_disable) (bus_space_tag_t t, bus_space_handle_t h); |
| 249 | /* Disable interrupts routine */ |
| 250 | void (*m_get) (bus_space_tag_t t, bus_space_handle_t h, |
| 251 | int *dx, int *dy, int *but); |
| 252 | /* and get mouse status */ |
| 253 | mousehw_t m_hw; /* buttons iftype type model hwid */ |
| 254 | mousemode_t m_mode; /* proto rate res accel level size mask */ |
| 255 | } mse_types[] = { |
| 256 | { MSE_ATIINPORT, |
| 257 | mse_probeati, mse_enableati, mse_disableati, mse_getati, |
| 258 | { 2, MOUSE_IF_INPORT, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, }, |
| 259 | { MOUSE_PROTO_INPORT, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE, |
| 260 | { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, }, |
| 261 | { MSE_LOGITECH, |
| 262 | mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi, |
| 263 | { 2, MOUSE_IF_BUS, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, }, |
| 264 | { MOUSE_PROTO_BUS, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE, |
| 265 | { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, }, |
| 266 | { 0, }, |
| 267 | }; |
| 268 | |
| 269 | static int |
| 270 | mse_probe(device_t dev) |
| 271 | { |
| 272 | mse_softc_t *sc; |
| 273 | int error; |
| 274 | int rid; |
| 275 | int i; |
| 276 | |
| 277 | /* check PnP IDs */ |
| 278 | error = ISA_PNP_PROBE(device_get_parent(dev), dev, mse_ids); |
| 279 | if (error == ENXIO) |
| 280 | return ENXIO; |
| 281 | |
| 282 | sc = device_get_softc(dev); |
| 283 | rid = 0; |
| 284 | sc->sc_port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, |
| 285 | MSE_IOSIZE, RF_ACTIVE); |
| 286 | if (sc->sc_port == NULL) |
| 287 | return ENXIO; |
| 288 | sc->sc_iot = rman_get_bustag(sc->sc_port); |
| 289 | sc->sc_ioh = rman_get_bushandle(sc->sc_port); |
| 290 | |
| 291 | /* |
| 292 | * Check for each mouse type in the table. |
| 293 | */ |
| 294 | i = 0; |
| 295 | while (mse_types[i].m_type) { |
| 296 | if ((*mse_types[i].m_probe)(dev, sc)) { |
| 297 | sc->sc_mousetype = mse_types[i].m_type; |
| 298 | sc->sc_enablemouse = mse_types[i].m_enable; |
| 299 | sc->sc_disablemouse = mse_types[i].m_disable; |
| 300 | sc->sc_getmouse = mse_types[i].m_get; |
| 301 | sc->hw = mse_types[i].m_hw; |
| 302 | sc->mode = mse_types[i].m_mode; |
| 303 | bus_release_resource(dev, SYS_RES_IOPORT, rid, |
| 304 | sc->sc_port); |
| 305 | device_set_desc(dev, "Bus/InPort Mouse"); |
| 306 | return 0; |
| 307 | } |
| 308 | i++; |
| 309 | } |
| 310 | bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); |
| 311 | return ENXIO; |
| 312 | } |
| 313 | |
| 314 | static int |
| 315 | mse_attach(device_t dev) |
| 316 | { |
| 317 | mse_softc_t *sc; |
| 318 | int flags; |
| 319 | int unit; |
| 320 | int rid; |
| 321 | |
| 322 | sc = device_get_softc(dev); |
| 323 | unit = device_get_unit(dev); |
| 324 | |
| 325 | rid = 0; |
| 326 | sc->sc_port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, |
| 327 | MSE_IOSIZE, RF_ACTIVE); |
| 328 | if (sc->sc_port == NULL) |
| 329 | return ENXIO; |
| 330 | sc->sc_intr = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, |
| 331 | RF_ACTIVE); |
| 332 | if (sc->sc_intr == NULL) { |
| 333 | bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); |
| 334 | return ENXIO; |
| 335 | } |
| 336 | sc->sc_iot = rman_get_bustag(sc->sc_port); |
| 337 | sc->sc_ioh = rman_get_bushandle(sc->sc_port); |
| 338 | |
| 339 | if (BUS_SETUP_INTR(device_get_parent(dev), dev, sc->sc_intr, |
| 340 | 0, mseintr, sc, &sc->sc_ih, NULL)) { |
| 341 | bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); |
| 342 | bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr); |
| 343 | return ENXIO; |
| 344 | } |
| 345 | |
| 346 | flags = device_get_flags(dev); |
| 347 | sc->mode.accelfactor = (flags & MSE_CONFIG_ACCEL) >> 4; |
| 348 | callout_init(&sc->sc_callout); |
| 349 | |
| 350 | make_dev(&mse_ops, unit << 1, 0, 0, 0600, "mse%d", unit); |
| 351 | make_dev(&mse_ops, (unit<<1)+1, 0, 0, 0600, "nmse%d", unit); |
| 352 | |
| 353 | return 0; |
| 354 | } |
| 355 | |
| 356 | static int |
| 357 | mse_detach(device_t dev) |
| 358 | { |
| 359 | mse_softc_t *sc; |
| 360 | int rid; |
| 361 | |
| 362 | sc = device_get_softc(dev); |
| 363 | if (sc->sc_flags & MSESC_OPEN) |
| 364 | return EBUSY; |
| 365 | |
| 366 | rid = 0; |
| 367 | BUS_TEARDOWN_INTR(device_get_parent(dev), dev, sc->sc_intr, sc->sc_ih); |
| 368 | bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr); |
| 369 | bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port); |
| 370 | dev_ops_remove_minor(&mse_ops, device_get_unit(dev) << 1); |
| 371 | |
| 372 | return 0; |
| 373 | } |
| 374 | |
| 375 | /* |
| 376 | * Exclusive open the mouse, initialize it and enable interrupts. |
| 377 | */ |
| 378 | static int |
| 379 | mseopen(struct dev_open_args *ap) |
| 380 | { |
| 381 | cdev_t dev = ap->a_head.a_dev; |
| 382 | mse_softc_t *sc; |
| 383 | |
| 384 | sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); |
| 385 | if (sc == NULL) |
| 386 | return (ENXIO); |
| 387 | if (sc->sc_mousetype == MSE_NONE) |
| 388 | return (ENXIO); |
| 389 | if (sc->sc_flags & MSESC_OPEN) |
| 390 | return (EBUSY); |
| 391 | sc->sc_flags |= MSESC_OPEN; |
| 392 | sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS; |
| 393 | sc->sc_deltax = sc->sc_deltay = 0; |
| 394 | sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; |
| 395 | sc->sc_watchdog = FALSE; |
| 396 | callout_reset(&sc->sc_callout, hz * 2, msetimeout, dev); |
| 397 | sc->mode.level = 0; |
| 398 | sc->status.flags = 0; |
| 399 | sc->status.button = sc->status.obutton = 0; |
| 400 | sc->status.dx = sc->status.dy = sc->status.dz = 0; |
| 401 | |
| 402 | /* |
| 403 | * Initialize mouse interface and enable interrupts. |
| 404 | */ |
| 405 | crit_enter(); |
| 406 | (*sc->sc_enablemouse)(sc->sc_iot, sc->sc_ioh); |
| 407 | crit_exit(); |
| 408 | return (0); |
| 409 | } |
| 410 | |
| 411 | /* |
| 412 | * mseclose: just turn off mouse innterrupts. |
| 413 | */ |
| 414 | static int |
| 415 | mseclose(struct dev_close_args *ap) |
| 416 | { |
| 417 | cdev_t dev = ap->a_head.a_dev; |
| 418 | mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); |
| 419 | |
| 420 | crit_enter(); |
| 421 | callout_stop(&sc->sc_callout); |
| 422 | (*sc->sc_disablemouse)(sc->sc_iot, sc->sc_ioh); |
| 423 | sc->sc_flags &= ~MSESC_OPEN; |
| 424 | crit_exit(); |
| 425 | return(0); |
| 426 | } |
| 427 | |
| 428 | /* |
| 429 | * mseread: return mouse info using the MSC serial protocol, but without |
| 430 | * using bytes 4 and 5. |
| 431 | * (Yes this is cheesy, but it makes the X386 server happy, so...) |
| 432 | */ |
| 433 | static int |
| 434 | mseread(struct dev_read_args *ap) |
| 435 | { |
| 436 | cdev_t dev = ap->a_head.a_dev; |
| 437 | struct uio *uio = ap->a_uio; |
| 438 | mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); |
| 439 | int xfer, error; |
| 440 | |
| 441 | /* |
| 442 | * If there are no protocol bytes to be read, set up a new protocol |
| 443 | * packet. |
| 444 | */ |
| 445 | crit_enter(); /* XXX Should be its own spl, but where is imlXX() */ |
| 446 | if (sc->sc_bytesread >= sc->mode.packetsize) { |
| 447 | while (sc->sc_deltax == 0 && sc->sc_deltay == 0 && |
| 448 | (sc->sc_obuttons ^ sc->sc_buttons) == 0) { |
| 449 | if (MSE_NBLOCKIO(dev)) { |
| 450 | crit_exit(); |
| 451 | return (0); |
| 452 | } |
| 453 | sc->sc_flags |= MSESC_WANT; |
| 454 | error = tsleep((caddr_t)sc, PCATCH, "mseread", 0); |
| 455 | if (error) { |
| 456 | crit_exit(); |
| 457 | return (error); |
| 458 | } |
| 459 | } |
| 460 | |
| 461 | /* |
| 462 | * Generate protocol bytes. |
| 463 | * For some reason X386 expects 5 bytes but never uses |
| 464 | * the fourth or fifth? |
| 465 | */ |
| 466 | sc->sc_bytes[0] = sc->mode.syncmask[1] |
| 467 | | (sc->sc_buttons & ~sc->mode.syncmask[0]); |
| 468 | if (sc->sc_deltax > 127) |
| 469 | sc->sc_deltax = 127; |
| 470 | if (sc->sc_deltax < -127) |
| 471 | sc->sc_deltax = -127; |
| 472 | sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */ |
| 473 | if (sc->sc_deltay > 127) |
| 474 | sc->sc_deltay = 127; |
| 475 | if (sc->sc_deltay < -127) |
| 476 | sc->sc_deltay = -127; |
| 477 | sc->sc_bytes[1] = sc->sc_deltax; |
| 478 | sc->sc_bytes[2] = sc->sc_deltay; |
| 479 | sc->sc_bytes[3] = sc->sc_bytes[4] = 0; |
| 480 | sc->sc_bytes[5] = sc->sc_bytes[6] = 0; |
| 481 | sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS; |
| 482 | sc->sc_obuttons = sc->sc_buttons; |
| 483 | sc->sc_deltax = sc->sc_deltay = 0; |
| 484 | sc->sc_bytesread = 0; |
| 485 | } |
| 486 | crit_exit(); |
| 487 | xfer = (int)szmin(uio->uio_resid, |
| 488 | sc->mode.packetsize - sc->sc_bytesread); |
| 489 | error = uiomove(&sc->sc_bytes[sc->sc_bytesread], (size_t)xfer, uio); |
| 490 | if (error) |
| 491 | return (error); |
| 492 | sc->sc_bytesread += xfer; |
| 493 | return(0); |
| 494 | } |
| 495 | |
| 496 | /* |
| 497 | * mseioctl: process ioctl commands. |
| 498 | */ |
| 499 | static int |
| 500 | mseioctl(struct dev_ioctl_args *ap) |
| 501 | { |
| 502 | cdev_t dev = ap->a_head.a_dev; |
| 503 | caddr_t addr = ap->a_data; |
| 504 | mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); |
| 505 | mousestatus_t status; |
| 506 | int err = 0; |
| 507 | |
| 508 | switch (ap->a_cmd) { |
| 509 | case MOUSE_GETHWINFO: |
| 510 | crit_enter(); |
| 511 | *(mousehw_t *)addr = sc->hw; |
| 512 | if (sc->mode.level == 0) |
| 513 | ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; |
| 514 | crit_exit(); |
| 515 | break; |
| 516 | |
| 517 | case MOUSE_GETMODE: |
| 518 | crit_enter(); |
| 519 | *(mousemode_t *)addr = sc->mode; |
| 520 | switch (sc->mode.level) { |
| 521 | case 0: |
| 522 | break; |
| 523 | case 1: |
| 524 | ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; |
| 525 | ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; |
| 526 | ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; |
| 527 | break; |
| 528 | } |
| 529 | crit_exit(); |
| 530 | break; |
| 531 | |
| 532 | case MOUSE_SETMODE: |
| 533 | switch (((mousemode_t *)addr)->level) { |
| 534 | case 0: |
| 535 | case 1: |
| 536 | break; |
| 537 | default: |
| 538 | return (EINVAL); |
| 539 | } |
| 540 | if (((mousemode_t *)addr)->accelfactor < -1) |
| 541 | return (EINVAL); |
| 542 | else if (((mousemode_t *)addr)->accelfactor >= 0) |
| 543 | sc->mode.accelfactor = |
| 544 | ((mousemode_t *)addr)->accelfactor; |
| 545 | sc->mode.level = ((mousemode_t *)addr)->level; |
| 546 | switch (sc->mode.level) { |
| 547 | case 0: |
| 548 | sc->sc_bytesread = sc->mode.packetsize |
| 549 | = MOUSE_MSC_PACKETSIZE; |
| 550 | break; |
| 551 | case 1: |
| 552 | sc->sc_bytesread = sc->mode.packetsize |
| 553 | = MOUSE_SYS_PACKETSIZE; |
| 554 | break; |
| 555 | } |
| 556 | break; |
| 557 | |
| 558 | case MOUSE_GETLEVEL: |
| 559 | *(int *)addr = sc->mode.level; |
| 560 | break; |
| 561 | |
| 562 | case MOUSE_SETLEVEL: |
| 563 | switch (*(int *)addr) { |
| 564 | case 0: |
| 565 | sc->mode.level = *(int *)addr; |
| 566 | sc->sc_bytesread = sc->mode.packetsize |
| 567 | = MOUSE_MSC_PACKETSIZE; |
| 568 | break; |
| 569 | case 1: |
| 570 | sc->mode.level = *(int *)addr; |
| 571 | sc->sc_bytesread = sc->mode.packetsize |
| 572 | = MOUSE_SYS_PACKETSIZE; |
| 573 | break; |
| 574 | default: |
| 575 | return (EINVAL); |
| 576 | } |
| 577 | break; |
| 578 | |
| 579 | case MOUSE_GETSTATUS: |
| 580 | crit_enter(); |
| 581 | status = sc->status; |
| 582 | sc->status.flags = 0; |
| 583 | sc->status.obutton = sc->status.button; |
| 584 | sc->status.button = 0; |
| 585 | sc->status.dx = 0; |
| 586 | sc->status.dy = 0; |
| 587 | sc->status.dz = 0; |
| 588 | crit_exit(); |
| 589 | *(mousestatus_t *)addr = status; |
| 590 | break; |
| 591 | |
| 592 | case MOUSE_READSTATE: |
| 593 | case MOUSE_READDATA: |
| 594 | return (ENODEV); |
| 595 | |
| 596 | #if (defined(MOUSE_GETVARS)) |
| 597 | case MOUSE_GETVARS: |
| 598 | case MOUSE_SETVARS: |
| 599 | return (ENODEV); |
| 600 | #endif |
| 601 | |
| 602 | default: |
| 603 | return (ENOTTY); |
| 604 | } |
| 605 | return (err); |
| 606 | } |
| 607 | |
| 608 | static struct filterops msefiltops = |
| 609 | { FILTEROP_ISFD, NULL, msefilter_detach, msefilter }; |
| 610 | |
| 611 | static int |
| 612 | msekqfilter(struct dev_kqfilter_args *ap) |
| 613 | { |
| 614 | cdev_t dev = ap->a_head.a_dev; |
| 615 | mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); |
| 616 | struct knote *kn = ap->a_kn; |
| 617 | struct klist *klist; |
| 618 | |
| 619 | ap->a_result = 0; |
| 620 | |
| 621 | switch (kn->kn_filter) { |
| 622 | case EVFILT_READ: |
| 623 | kn->kn_fop = &msefiltops; |
| 624 | kn->kn_hook = (caddr_t)sc; |
| 625 | break; |
| 626 | default: |
| 627 | ap->a_result = EOPNOTSUPP; |
| 628 | return (0); |
| 629 | } |
| 630 | |
| 631 | klist = &sc->sc_kqp.ki_note; |
| 632 | knote_insert(klist, kn); |
| 633 | |
| 634 | return (0); |
| 635 | } |
| 636 | |
| 637 | static void |
| 638 | msefilter_detach(struct knote *kn) |
| 639 | { |
| 640 | mse_softc_t *sc = (mse_softc_t *)kn->kn_hook; |
| 641 | struct klist *klist; |
| 642 | |
| 643 | klist = &sc->sc_kqp.ki_note; |
| 644 | knote_remove(klist, kn); |
| 645 | } |
| 646 | |
| 647 | static int |
| 648 | msefilter(struct knote *kn, long hint) |
| 649 | { |
| 650 | mse_softc_t *sc = (mse_softc_t *)kn->kn_hook; |
| 651 | int ready = 0; |
| 652 | |
| 653 | crit_enter(); |
| 654 | if (sc->sc_bytesread != sc->mode.packetsize || |
| 655 | sc->sc_deltax != 0 || sc->sc_deltay != 0 || |
| 656 | (sc->sc_obuttons ^ sc->sc_buttons) != 0) |
| 657 | ready = 1; |
| 658 | |
| 659 | crit_exit(); |
| 660 | |
| 661 | return (ready); |
| 662 | } |
| 663 | |
| 664 | /* |
| 665 | * msetimeout: watchdog timer routine. |
| 666 | */ |
| 667 | static void |
| 668 | msetimeout(void *arg) |
| 669 | { |
| 670 | cdev_t dev; |
| 671 | mse_softc_t *sc; |
| 672 | |
| 673 | dev = (cdev_t)arg; |
| 674 | sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev)); |
| 675 | if (sc->sc_watchdog) { |
| 676 | if (bootverbose) |
| 677 | kprintf("mse%d: lost interrupt?\n", MSE_UNIT(dev)); |
| 678 | mseintr(sc); |
| 679 | } |
| 680 | sc->sc_watchdog = TRUE; |
| 681 | callout_reset(&sc->sc_callout, hz, msetimeout, dev); |
| 682 | } |
| 683 | |
| 684 | /* |
| 685 | * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative. |
| 686 | */ |
| 687 | static void |
| 688 | mseintr(void *arg) |
| 689 | { |
| 690 | /* |
| 691 | * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP) |
| 692 | * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). |
| 693 | */ |
| 694 | static int butmap[8] = { |
| 695 | 0, |
| 696 | MOUSE_BUTTON3DOWN, |
| 697 | MOUSE_BUTTON2DOWN, |
| 698 | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, |
| 699 | MOUSE_BUTTON1DOWN, |
| 700 | MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, |
| 701 | MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, |
| 702 | MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN |
| 703 | }; |
| 704 | mse_softc_t *sc = arg; |
| 705 | int dx, dy, but; |
| 706 | int sign; |
| 707 | |
| 708 | #ifdef DEBUG |
| 709 | static int mse_intrcnt = 0; |
| 710 | if((mse_intrcnt++ % 10000) == 0) |
| 711 | kprintf("mseintr\n"); |
| 712 | #endif /* DEBUG */ |
| 713 | if ((sc->sc_flags & MSESC_OPEN) == 0) |
| 714 | return; |
| 715 | |
| 716 | (*sc->sc_getmouse)(sc->sc_iot, sc->sc_ioh, &dx, &dy, &but); |
| 717 | if (sc->mode.accelfactor > 0) { |
| 718 | sign = (dx < 0); |
| 719 | dx = dx * dx / sc->mode.accelfactor; |
| 720 | if (dx == 0) |
| 721 | dx = 1; |
| 722 | if (sign) |
| 723 | dx = -dx; |
| 724 | sign = (dy < 0); |
| 725 | dy = dy * dy / sc->mode.accelfactor; |
| 726 | if (dy == 0) |
| 727 | dy = 1; |
| 728 | if (sign) |
| 729 | dy = -dy; |
| 730 | } |
| 731 | sc->sc_deltax += dx; |
| 732 | sc->sc_deltay += dy; |
| 733 | sc->sc_buttons = but; |
| 734 | |
| 735 | but = butmap[~but & MOUSE_MSC_BUTTONS]; |
| 736 | sc->status.dx += dx; |
| 737 | sc->status.dy += dy; |
| 738 | sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0) |
| 739 | | (sc->status.button ^ but); |
| 740 | sc->status.button = but; |
| 741 | |
| 742 | sc->sc_watchdog = FALSE; |
| 743 | |
| 744 | /* |
| 745 | * If mouse state has changed, wake up anyone wanting to know. |
| 746 | */ |
| 747 | if (sc->sc_deltax != 0 || sc->sc_deltay != 0 || |
| 748 | (sc->sc_obuttons ^ sc->sc_buttons) != 0) { |
| 749 | if (sc->sc_flags & MSESC_WANT) { |
| 750 | sc->sc_flags &= ~MSESC_WANT; |
| 751 | wakeup((caddr_t)sc); |
| 752 | } |
| 753 | KNOTE(&sc->sc_kqp.ki_note, 0); |
| 754 | } |
| 755 | } |
| 756 | |
| 757 | /* |
| 758 | * Routines for the Logitech mouse. |
| 759 | */ |
| 760 | /* |
| 761 | * Test for a Logitech bus mouse and return 1 if it is. |
| 762 | * (until I know how to use the signature port properly, just disable |
| 763 | * interrupts and return 1) |
| 764 | */ |
| 765 | static int |
| 766 | mse_probelogi(device_t dev, mse_softc_t *sc) |
| 767 | { |
| 768 | |
| 769 | int sig; |
| 770 | |
| 771 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTD, MSE_SETUP); |
| 772 | /* set the signature port */ |
| 773 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTB, MSE_LOGI_SIG); |
| 774 | |
| 775 | DELAY(30000); /* 30 ms delay */ |
| 776 | sig = bus_space_read_1(sc->sc_iot, sc->sc_ioh, MSE_PORTB) & 0xFF; |
| 777 | if (sig == MSE_LOGI_SIG) { |
| 778 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTC, |
| 779 | MSE_DISINTR); |
| 780 | return(1); |
| 781 | } else { |
| 782 | if (bootverbose) |
| 783 | device_printf(dev, "wrong signature %x\n", sig); |
| 784 | return(0); |
| 785 | } |
| 786 | } |
| 787 | |
| 788 | /* |
| 789 | * Initialize Logitech mouse and enable interrupts. |
| 790 | */ |
| 791 | static void |
| 792 | mse_enablelogi(bus_space_tag_t tag, bus_space_handle_t handle) |
| 793 | { |
| 794 | int dx, dy, but; |
| 795 | |
| 796 | bus_space_write_1(tag, handle, MSE_PORTD, MSE_SETUP); |
| 797 | mse_getlogi(tag, handle, &dx, &dy, &but); |
| 798 | } |
| 799 | |
| 800 | /* |
| 801 | * Disable interrupts for Logitech mouse. |
| 802 | */ |
| 803 | static void |
| 804 | mse_disablelogi(bus_space_tag_t tag, bus_space_handle_t handle) |
| 805 | { |
| 806 | |
| 807 | bus_space_write_1(tag, handle, MSE_PORTC, MSE_DISINTR); |
| 808 | } |
| 809 | |
| 810 | /* |
| 811 | * Get the current dx, dy and button up/down state. |
| 812 | */ |
| 813 | static void |
| 814 | mse_getlogi(bus_space_tag_t tag, bus_space_handle_t handle, int *dx, int *dy, |
| 815 | int *but) |
| 816 | { |
| 817 | char x, y; |
| 818 | |
| 819 | bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RXLOW); |
| 820 | x = bus_space_read_1(tag, handle, MSE_PORTA); |
| 821 | *but = (x >> 5) & MOUSE_MSC_BUTTONS; |
| 822 | x &= 0xf; |
| 823 | bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RXHIGH); |
| 824 | x |= (bus_space_read_1(tag, handle, MSE_PORTA) << 4); |
| 825 | bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RYLOW); |
| 826 | y = (bus_space_read_1(tag, handle, MSE_PORTA) & 0xf); |
| 827 | bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RYHIGH); |
| 828 | y |= (bus_space_read_1(tag, handle, MSE_PORTA) << 4); |
| 829 | *dx = x; |
| 830 | *dy = y; |
| 831 | bus_space_write_1(tag, handle, MSE_PORTC, MSE_INTREN); |
| 832 | } |
| 833 | |
| 834 | /* |
| 835 | * Routines for the ATI Inport bus mouse. |
| 836 | */ |
| 837 | /* |
| 838 | * Test for a ATI Inport bus mouse and return 1 if it is. |
| 839 | * (do not enable interrupts) |
| 840 | */ |
| 841 | static int |
| 842 | mse_probeati(device_t dev, mse_softc_t *sc) |
| 843 | { |
| 844 | int i; |
| 845 | |
| 846 | for (i = 0; i < 2; i++) |
| 847 | if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, MSE_PORTC) == 0xde) |
| 848 | return (1); |
| 849 | return (0); |
| 850 | } |
| 851 | |
| 852 | /* |
| 853 | * Initialize ATI Inport mouse and enable interrupts. |
| 854 | */ |
| 855 | static void |
| 856 | mse_enableati(bus_space_tag_t tag, bus_space_handle_t handle) |
| 857 | { |
| 858 | |
| 859 | bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_RESET); |
| 860 | bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE); |
| 861 | bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_INTREN); |
| 862 | } |
| 863 | |
| 864 | /* |
| 865 | * Disable interrupts for ATI Inport mouse. |
| 866 | */ |
| 867 | static void |
| 868 | mse_disableati(bus_space_tag_t tag, bus_space_handle_t handle) |
| 869 | { |
| 870 | |
| 871 | bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE); |
| 872 | bus_space_write_1(tag, handle, MSE_PORTB, 0); |
| 873 | } |
| 874 | |
| 875 | /* |
| 876 | * Get current dx, dy and up/down button state. |
| 877 | */ |
| 878 | static void |
| 879 | mse_getati(bus_space_tag_t tag, bus_space_handle_t handle, int *dx, int *dy, |
| 880 | int *but) |
| 881 | { |
| 882 | char byte; |
| 883 | |
| 884 | bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE); |
| 885 | bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_HOLD); |
| 886 | bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_STATUS); |
| 887 | *but = ~bus_space_read_1(tag, handle, MSE_PORTB) & MOUSE_MSC_BUTTONS; |
| 888 | bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_DX); |
| 889 | byte = bus_space_read_1(tag, handle, MSE_PORTB); |
| 890 | *dx = byte; |
| 891 | bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_DY); |
| 892 | byte = bus_space_read_1(tag, handle, MSE_PORTB); |
| 893 | *dy = byte; |
| 894 | bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE); |
| 895 | bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_INTREN); |
| 896 | } |