1 /* $NetBSD: qms.c,v 1.4 2002/11/26 19:50:22 christos Exp $ */ 2 3 /* 4 * Copyright (c) Scott Stevens 1995 All rights reserved 5 * Copyright (c) Melvin Tang-Richardson 1995 All rights reserved 6 * Copyright (c) Mark Brinicombe 1995 All rights reserved 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed for the NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * Quadrature mouse driver 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/conf.h> 41 #include <sys/ioctl.h> 42 #include <sys/tty.h> 43 #include <sys/kernel.h> 44 #include <sys/types.h> 45 #include <sys/device.h> 46 #include <sys/proc.h> 47 #include <sys/time.h> 48 #include <sys/errno.h> 49 #include <dev/cons.h> 50 #include <sys/fcntl.h> 51 #include <sys/signalvar.h> 52 #include <sys/vnode.h> 53 #include <sys/time.h> 54 #include <sys/poll.h> 55 56 #include <machine/bus.h> 57 #include <machine/mouse.h> 58 #include <arm/iomd/qmsvar.h> 59 60 #define MOUSE_IOC_ACK 61 62 #define QMOUSE_BSIZE 12*64 63 64 #ifdef MOUSE_IOC_ACK 65 static void qmsputbuffer __P((struct qms_softc *sc, struct mousebufrec *buf)); 66 #endif 67 68 extern struct cfdriver qms_cd; 69 70 dev_type_open(qmsopen); 71 dev_type_close(qmsclose); 72 dev_type_read(qmsread); 73 dev_type_ioctl(qmsioctl); 74 dev_type_poll(qmspoll); 75 dev_type_kqfilter(qmskqfilter); 76 77 const struct cdevsw qms_cdevsw = { 78 qmsopen, qmsclose, qmsread, nowrite, qmsioctl, 79 nostop, notty, qmspoll, nommap, qmskqfilter, 80 }; 81 82 /* qms device structure */ 83 84 /* Offsets of hardware registers */ 85 #define QMS_MOUSEX 0 /* 16 bit X register */ 86 #define QMS_MOUSEY 1 /* 16 bit Y register */ 87 88 #define QMS_BUTTONS 0 /* mouse buttons register */ 89 90 /* 91 * generic attach routine. This does the generic part of the driver 92 * attachment and is called from the bus specific attach routine. 93 */ 94 95 void 96 qmsattach(sc) 97 struct qms_softc *sc; 98 { 99 /* Set up origin and multipliers */ 100 sc->origx = 0; 101 sc->origy = 0; 102 sc->xmult = 2; 103 sc->ymult = 2; 104 105 /* Set up bounding box */ 106 sc->boundx = -4095; 107 sc->boundy = -4095; 108 sc->bounda = 4096; 109 sc->boundb = 4096; 110 111 sc->sc_state = 0; 112 113 /* Set the mouse X & Y registers to a known state */ 114 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, sc->origx); 115 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, sc->origx); 116 } 117 118 int 119 qmsopen(dev, flag, mode, p) 120 dev_t dev; 121 int flag; 122 int mode; 123 struct proc *p; 124 { 125 struct qms_softc *sc; 126 int unit = minor(dev); 127 128 /* validate the unit and softc */ 129 if (unit >= qms_cd.cd_ndevs) 130 return(ENXIO); 131 132 sc = qms_cd.cd_devs[unit]; 133 134 if (!sc) return(ENXIO); 135 136 /* check if we are already open */ 137 if (sc->sc_state & QMOUSE_OPEN) return(EBUSY); 138 139 /* update softc */ 140 sc->sc_proc = p; 141 142 sc->lastx = -1; 143 sc->lasty = -1; 144 sc->lastb = -1; 145 146 /* initialise buffer */ 147 if (clalloc(&sc->sc_buffer, QMOUSE_BSIZE, 0) == -1) 148 return(ENOMEM); 149 150 /* set mode and state */ 151 sc->sc_mode = MOUSEMODE_ABS; 152 sc->sc_state |= QMOUSE_OPEN; 153 154 /* enable interrupts */ 155 sc->sc_intenable(sc, 1); 156 157 return(0); 158 } 159 160 161 int 162 qmsclose(dev, flag, mode, p) 163 dev_t dev; 164 int flag; 165 int mode; 166 struct proc *p; 167 { 168 int unit = minor(dev); 169 struct qms_softc *sc = qms_cd.cd_devs[unit]; 170 171 /* disable interrupts */ 172 sc->sc_intenable(sc, 0); 173 174 /* clean up */ 175 sc->sc_proc = NULL; 176 sc->sc_state = 0; 177 178 clfree(&sc->sc_buffer); 179 180 return(0); 181 } 182 183 int 184 qmsread(dev, uio, flag) 185 dev_t dev; 186 struct uio *uio; 187 int flag; 188 { 189 int unit = minor(dev); 190 struct qms_softc *sc = qms_cd.cd_devs[unit]; 191 int error; 192 int s; 193 int length; 194 u_char buffer[128]; 195 196 error = 0; 197 s = spltty(); 198 while (sc->sc_buffer.c_cc == 0) { 199 if (flag & IO_NDELAY) { 200 (void)splx(s); 201 return(EWOULDBLOCK); 202 } 203 sc->sc_state |= QMOUSE_ASLEEP; 204 if ((error = tsleep((caddr_t)sc, PZERO | PCATCH, "qmsread", 0))) { 205 sc->sc_state &= ~QMOUSE_ASLEEP; 206 (void)splx(s); 207 return(error); 208 } 209 } 210 211 while (sc->sc_buffer.c_cc > 0 && uio->uio_resid > 0) { 212 length = min(sc->sc_buffer.c_cc, uio->uio_resid); 213 if(length>sizeof(buffer)) 214 length=sizeof(buffer); 215 216 (void)q_to_b(&sc->sc_buffer, buffer, length); 217 218 if ((error = (uiomove(buffer, length, uio)))) 219 break; 220 } 221 (void)splx(s); 222 return(error); 223 } 224 225 226 #define FMT_START \ 227 int x = bus_space_read_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX) & 0xffff; \ 228 int y = bus_space_read_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY) & 0xffff; \ 229 int b = bus_space_read_1(sc->sc_iot, sc->sc_butioh, QMS_BUTTONS) & 0x70;\ 230 if (x & 0x8000) x |= 0xffff0000; \ 231 if (y & 0x8000) y |= 0xffff0000; \ 232 x = (x - sc->origx); \ 233 y = (y - sc->origy); \ 234 if (x < (sc->boundx)) x = sc->boundx; \ 235 if (x > (sc->bounda)) x = sc->bounda; \ 236 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, x + sc->origx); \ 237 if (y < (sc->boundy)) y = sc->boundy; \ 238 if (y > (sc->boundb)) y = sc->boundb; \ 239 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY, y + sc->origy); \ 240 x = x * sc->xmult; \ 241 y = y * sc->ymult; 242 243 #define FMT_END 244 245 246 int 247 qmsioctl(dev, cmd, data, flag, p) 248 dev_t dev; 249 u_long cmd; 250 caddr_t data; 251 int flag; 252 struct proc *p; 253 { 254 struct qms_softc *sc = qms_cd.cd_devs[minor(dev)]; 255 256 switch (cmd) { 257 case MOUSEIOC_WRITEX: 258 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, 259 *(int *)data + sc->origx); 260 return 0; 261 case MOUSEIOC_WRITEY: 262 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY, 263 *(int *)data + sc->origy); 264 return 0; 265 case MOUSEIOC_SETSTATE: 266 { 267 struct mouse_state *co = (void *)data; 268 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, co->x); 269 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY, co->y); 270 return 0; 271 } 272 case MOUSEIOC_SETBOUNDS: 273 { 274 struct mouse_boundingbox *bo = (void *)data; 275 struct mousebufrec buffer; 276 #ifdef MOUSE_IOC_ACK 277 int s; 278 279 s = spltty(); 280 #endif 281 282 sc->boundx = bo->x; 283 sc->boundy = bo->y; 284 sc->bounda = bo->a; 285 sc->boundb = bo->b; 286 287 buffer.status = IOC_ACK; 288 buffer.x = sc->origx; 289 buffer.y = sc->origy; 290 #ifdef MOUSE_IOC_ACK 291 if (sc->sc_buffer.c_cc > 0) 292 printf("%s: setting bounding with non empty buffer (%d)\n", 293 sc->sc_device.dv_xname, sc->sc_buffer.c_cc); 294 qmsputbuffer(sc, &buffer); 295 (void)splx(s); 296 #endif 297 return 0; 298 } 299 case MOUSEIOC_SETMODE: 300 { 301 struct mousebufrec buffer; 302 #ifdef MOUSE_IOC_ACK 303 int s; 304 305 s = spltty(); 306 #endif 307 sc->sc_mode = *(int *)data; 308 309 buffer.status = IOC_ACK; 310 buffer.x = sc->origx; 311 buffer.y = sc->origy; 312 #ifdef MOUSE_IOC_ACK 313 if (sc->sc_buffer.c_cc > 0) 314 printf("%s: setting mode with non empty buffer (%d)\n", 315 sc->sc_device.dv_xname, sc->sc_buffer.c_cc); 316 qmsputbuffer(sc, &buffer); 317 (void)splx(s); 318 #endif 319 return 0; 320 } 321 case MOUSEIOC_SETORIGIN: 322 { 323 struct mouse_origin *oo = (void *)data; 324 struct mousebufrec buffer; 325 #ifdef MOUSE_IOC_ACK 326 int s; 327 328 s = spltty(); 329 #endif 330 /* Need to fix up! */ 331 sc->origx = oo->x; 332 sc->origy = oo->y; 333 334 buffer.status = IOC_ACK; 335 buffer.x = sc->origx; 336 buffer.y = sc->origy; 337 #ifdef MOUSE_IOC_ACK 338 if (sc->sc_buffer.c_cc > 0) 339 printf("%s: setting origin with non empty buffer (%d)\n", 340 sc->sc_device.dv_xname, sc->sc_buffer.c_cc); 341 qmsputbuffer(sc, &buffer); 342 (void)splx(s); 343 #endif 344 return 0; 345 } 346 case MOUSEIOC_GETSTATE: 347 { 348 struct mouse_state *co = (void *)data; 349 FMT_START 350 co->x = x; 351 co->y = y; 352 co->buttons = b ^ 0x70; 353 FMT_END 354 return 0; 355 } 356 case MOUSEIOC_GETBOUNDS: 357 { 358 struct mouse_boundingbox *bo = (void *)data; 359 bo->x = sc->boundx; 360 bo->y = sc->boundy; 361 bo->a = sc->bounda; 362 bo->b = sc->boundb; 363 return 0; 364 } 365 case MOUSEIOC_GETORIGIN: 366 { 367 struct mouse_origin *oo = (void *)data; 368 oo->x = sc->origx; 369 oo->y = sc->origy; 370 return 0; 371 } 372 } 373 374 return (EINVAL); 375 } 376 377 378 int 379 qmsintr(arg) 380 void *arg; 381 { 382 struct qms_softc *sc = arg; 383 int s; 384 struct mousebufrec buffer; 385 int dosignal=0; 386 387 FMT_START 388 389 b &= 0x70; 390 b >>= 4; 391 if (x != sc->lastx || y != sc->lasty || b != sc->lastb) { 392 /* Mouse state changed */ 393 buffer.status = b | ( b ^ sc->lastb) << 3 | (((x==sc->lastx) && (y==sc->lasty))?0:MOVEMENT); 394 if(sc->sc_mode == MOUSEMODE_REL) { 395 sc->origx = sc->origy = 0; 396 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, sc->origx); 397 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY, sc->origy); 398 } 399 buffer.x = x; 400 buffer.y = y; 401 microtime(&buffer.event_time); 402 403 if (sc->sc_buffer.c_cc == 0) 404 dosignal = 1; 405 406 s = spltty(); 407 (void)b_to_q((char *)&buffer, sizeof(buffer), &sc->sc_buffer); 408 (void)splx(s); 409 selwakeup(&sc->sc_rsel); 410 411 if (sc->sc_state & QMOUSE_ASLEEP) { 412 sc->sc_state &= ~QMOUSE_ASLEEP; 413 wakeup((caddr_t)sc); 414 } 415 416 /* if (dosignal)*/ 417 psignal(sc->sc_proc, SIGIO); 418 419 sc->lastx = x; 420 sc->lasty = y; 421 sc->lastb = b; 422 } 423 424 FMT_END 425 return(0); /* Pass interrupt on down the chain */ 426 } 427 428 429 int 430 qmspoll(dev, events, p) 431 dev_t dev; 432 int events; 433 struct proc *p; 434 { 435 struct qms_softc *sc = qms_cd.cd_devs[minor(dev)]; 436 int revents = 0; 437 int s = spltty(); 438 439 if (events & (POLLIN | POLLRDNORM)) { 440 if (sc->sc_buffer.c_cc > 0) 441 revents |= events & (POLLIN | POLLRDNORM); 442 else 443 selrecord(p, &sc->sc_rsel); 444 } 445 446 (void)splx(s); 447 return (revents); 448 } 449 450 451 #ifdef MOUSE_IOC_ACK 452 static void 453 qmsputbuffer(sc, buffer) 454 struct qms_softc *sc; 455 struct mousebufrec *buffer; 456 { 457 int s; 458 int dosignal = 0; 459 460 /* Time stamp the buffer */ 461 microtime(&buffer->event_time); 462 463 if (sc->sc_buffer.c_cc == 0) 464 dosignal=1; 465 466 s = spltty(); 467 (void)b_to_q((char *)buffer, sizeof(*buffer), &sc->sc_buffer); 468 (void)splx(s); 469 selwakeup(&sc->sc_rsel); 470 471 if (sc->sc_state & QMOUSE_ASLEEP) { 472 sc->sc_state &= ~QMOUSE_ASLEEP; 473 wakeup((caddr_t)sc); 474 } 475 476 if (dosignal) 477 psignal(sc->sc_proc, SIGIO); 478 } 479 #endif 480 481 /* XXXLUKEM (jdolecek) kqueue hooks not tested */ 482 static void 483 filt_qmsrdetach(struct knote *kn) 484 { 485 struct qms_softc *sc = kn->kn_hook; 486 int s; 487 488 s = spltty(); 489 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext); 490 splx(s); 491 } 492 493 static int 494 filt_qmsread(struct knote *kn, long hint) 495 { 496 struct qms_softc *sc = kn->kn_hook; 497 498 kn->kn_data = sc->sc_buffer.c_cc; 499 return (kn->kn_data > 0); 500 } 501 502 static const struct filterops qmsread_filtops = 503 { 1, NULL, filt_qmsrdetach, filt_qmsread }; 504 505 int 506 qmskqfilter(dev_t dev, struct knote *kn) 507 { 508 struct qms_softc *sc = qms_cd.cd_devs[minor(dev)]; 509 struct klist *klist; 510 int s; 511 512 switch (kn->kn_filter) { 513 case EVFILT_READ: 514 klist = &sc->sc_rsel.sel_klist; 515 kn->kn_fop = &qmsread_filtops; 516 break; 517 518 default: 519 return (1); 520 } 521 522 kn->kn_hook = sc; 523 524 s = spltty(); 525 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 526 splx(s); 527 528 return (0); 529 } 530 531 /* End of qms.c */ 532