1 /* $NetBSD: qms.c,v 1.1 2001/10/05 22:27:42 reinoud 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/conf.h> 58 #include <machine/mouse.h> 59 #include <arm/iomd/qmsvar.h> 60 61 #define MOUSE_IOC_ACK 62 63 #define QMOUSE_BSIZE 12*64 64 65 #ifdef MOUSE_IOC_ACK 66 static void qmsputbuffer __P((struct qms_softc *sc, struct mousebufrec *buf)); 67 #endif 68 69 extern struct cfdriver qms_cd; 70 71 /* qms device structure */ 72 73 /* Offsets of hardware registers */ 74 #define QMS_MOUSEX 0 /* 16 bit X register */ 75 #define QMS_MOUSEY 1 /* 16 bit Y register */ 76 77 #define QMS_BUTTONS 0 /* mouse buttons register */ 78 79 /* 80 * generic attach routine. This does the generic part of the driver 81 * attachment and is called from the bus specific attach routine. 82 */ 83 84 void 85 qmsattach(sc) 86 struct qms_softc *sc; 87 { 88 /* Set up origin and multipliers */ 89 sc->origx = 0; 90 sc->origy = 0; 91 sc->xmult = 2; 92 sc->ymult = 2; 93 94 /* Set up bounding box */ 95 sc->boundx = -4095; 96 sc->boundy = -4095; 97 sc->bounda = 4096; 98 sc->boundb = 4096; 99 100 sc->sc_state = 0; 101 102 /* Set the mouse X & Y registers to a known state */ 103 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, sc->origx); 104 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, sc->origx); 105 } 106 107 int 108 qmsopen(dev, flag, mode, p) 109 dev_t dev; 110 int flag; 111 int mode; 112 struct proc *p; 113 { 114 struct qms_softc *sc; 115 int unit = minor(dev); 116 117 /* validate the unit and softc */ 118 if (unit >= qms_cd.cd_ndevs) 119 return(ENXIO); 120 121 sc = qms_cd.cd_devs[unit]; 122 123 if (!sc) return(ENXIO); 124 125 /* check if we are already open */ 126 if (sc->sc_state & QMOUSE_OPEN) return(EBUSY); 127 128 /* update softc */ 129 sc->sc_proc = p; 130 131 sc->lastx = -1; 132 sc->lasty = -1; 133 sc->lastb = -1; 134 135 /* initialise buffer */ 136 if (clalloc(&sc->sc_buffer, QMOUSE_BSIZE, 0) == -1) 137 return(ENOMEM); 138 139 /* set mode and state */ 140 sc->sc_mode = MOUSEMODE_ABS; 141 sc->sc_state |= QMOUSE_OPEN; 142 143 /* enable interrupts */ 144 sc->sc_intenable(sc, 1); 145 146 return(0); 147 } 148 149 150 int 151 qmsclose(dev, flag, mode, p) 152 dev_t dev; 153 int flag; 154 int mode; 155 struct proc *p; 156 { 157 int unit = minor(dev); 158 struct qms_softc *sc = qms_cd.cd_devs[unit]; 159 160 /* disable interrupts */ 161 sc->sc_intenable(sc, 0); 162 163 /* clean up */ 164 sc->sc_proc = NULL; 165 sc->sc_state = 0; 166 167 clfree(&sc->sc_buffer); 168 169 return(0); 170 } 171 172 int 173 qmsread(dev, uio, flag) 174 dev_t dev; 175 struct uio *uio; 176 int flag; 177 { 178 int unit = minor(dev); 179 struct qms_softc *sc = qms_cd.cd_devs[unit]; 180 int error; 181 int s; 182 int length; 183 u_char buffer[128]; 184 185 error = 0; 186 s = spltty(); 187 while (sc->sc_buffer.c_cc == 0) { 188 if (flag & IO_NDELAY) { 189 (void)splx(s); 190 return(EWOULDBLOCK); 191 } 192 sc->sc_state |= QMOUSE_ASLEEP; 193 if ((error = tsleep((caddr_t)sc, PZERO | PCATCH, "qmsread", 0))) { 194 sc->sc_state &= ~QMOUSE_ASLEEP; 195 (void)splx(s); 196 return(error); 197 } 198 } 199 200 while (sc->sc_buffer.c_cc > 0 && uio->uio_resid > 0) { 201 length = min(sc->sc_buffer.c_cc, uio->uio_resid); 202 if(length>sizeof(buffer)) 203 length=sizeof(buffer); 204 205 (void)q_to_b(&sc->sc_buffer, buffer, length); 206 207 if ((error = (uiomove(buffer, length, uio)))) 208 break; 209 } 210 (void)splx(s); 211 return(error); 212 } 213 214 215 #define FMT_START \ 216 int x = bus_space_read_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX) & 0xffff; \ 217 int y = bus_space_read_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY) & 0xffff; \ 218 int b = bus_space_read_1(sc->sc_iot, sc->sc_butioh, QMS_BUTTONS) & 0x70;\ 219 if (x & 0x8000) x |= 0xffff0000; \ 220 if (y & 0x8000) y |= 0xffff0000; \ 221 x = (x - sc->origx); \ 222 y = (y - sc->origy); \ 223 if (x < (sc->boundx)) x = sc->boundx; \ 224 if (x > (sc->bounda)) x = sc->bounda; \ 225 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, x + sc->origx); \ 226 if (y < (sc->boundy)) y = sc->boundy; \ 227 if (y > (sc->boundb)) y = sc->boundb; \ 228 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY, y + sc->origy); \ 229 x = x * sc->xmult; \ 230 y = y * sc->ymult; 231 232 #define FMT_END 233 234 235 int 236 qmsioctl(dev, cmd, data, flag, p) 237 dev_t dev; 238 u_long cmd; 239 caddr_t data; 240 int flag; 241 struct proc *p; 242 { 243 struct qms_softc *sc = qms_cd.cd_devs[minor(dev)]; 244 245 switch (cmd) { 246 case MOUSEIOC_WRITEX: 247 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, 248 *(int *)data + sc->origx); 249 return 0; 250 case MOUSEIOC_WRITEY: 251 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY, 252 *(int *)data + sc->origy); 253 return 0; 254 case MOUSEIOC_SETSTATE: 255 { 256 struct mouse_state *co = (void *)data; 257 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, co->x); 258 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY, co->y); 259 return 0; 260 } 261 case MOUSEIOC_SETBOUNDS: 262 { 263 struct mouse_boundingbox *bo = (void *)data; 264 struct mousebufrec buffer; 265 #ifdef MOUSE_IOC_ACK 266 int s; 267 268 s = spltty(); 269 #endif 270 271 sc->boundx = bo->x; 272 sc->boundy = bo->y; 273 sc->bounda = bo->a; 274 sc->boundb = bo->b; 275 276 buffer.status = IOC_ACK; 277 buffer.x = sc->origx; 278 buffer.y = sc->origy; 279 #ifdef MOUSE_IOC_ACK 280 if (sc->sc_buffer.c_cc > 0) 281 printf("%s: setting bounding with non empty buffer (%d)\n", 282 sc->sc_device.dv_xname, sc->sc_buffer.c_cc); 283 qmsputbuffer(sc, &buffer); 284 (void)splx(s); 285 #endif 286 return 0; 287 } 288 case MOUSEIOC_SETMODE: 289 { 290 struct mousebufrec buffer; 291 #ifdef MOUSE_IOC_ACK 292 int s; 293 294 s = spltty(); 295 #endif 296 sc->sc_mode = *(int *)data; 297 298 buffer.status = IOC_ACK; 299 buffer.x = sc->origx; 300 buffer.y = sc->origy; 301 #ifdef MOUSE_IOC_ACK 302 if (sc->sc_buffer.c_cc > 0) 303 printf("%s: setting mode with non empty buffer (%d)\n", 304 sc->sc_device.dv_xname, sc->sc_buffer.c_cc); 305 qmsputbuffer(sc, &buffer); 306 (void)splx(s); 307 #endif 308 return 0; 309 } 310 case MOUSEIOC_SETORIGIN: 311 { 312 struct mouse_origin *oo = (void *)data; 313 struct mousebufrec buffer; 314 #ifdef MOUSE_IOC_ACK 315 int s; 316 317 s = spltty(); 318 #endif 319 /* Need to fix up! */ 320 sc->origx = oo->x; 321 sc->origy = oo->y; 322 323 buffer.status = IOC_ACK; 324 buffer.x = sc->origx; 325 buffer.y = sc->origy; 326 #ifdef MOUSE_IOC_ACK 327 if (sc->sc_buffer.c_cc > 0) 328 printf("%s: setting origin with non empty buffer (%d)\n", 329 sc->sc_device.dv_xname, sc->sc_buffer.c_cc); 330 qmsputbuffer(sc, &buffer); 331 (void)splx(s); 332 #endif 333 return 0; 334 } 335 case MOUSEIOC_GETSTATE: 336 { 337 struct mouse_state *co = (void *)data; 338 FMT_START 339 co->x = x; 340 co->y = y; 341 co->buttons = b ^ 0x70; 342 FMT_END 343 return 0; 344 } 345 case MOUSEIOC_GETBOUNDS: 346 { 347 struct mouse_boundingbox *bo = (void *)data; 348 bo->x = sc->boundx; 349 bo->y = sc->boundy; 350 bo->a = sc->bounda; 351 bo->b = sc->boundb; 352 return 0; 353 } 354 case MOUSEIOC_GETORIGIN: 355 { 356 struct mouse_origin *oo = (void *)data; 357 oo->x = sc->origx; 358 oo->y = sc->origy; 359 return 0; 360 } 361 } 362 363 return (EINVAL); 364 } 365 366 367 int 368 qmsintr(arg) 369 void *arg; 370 { 371 struct qms_softc *sc = arg; 372 int s; 373 struct mousebufrec buffer; 374 int dosignal=0; 375 376 FMT_START 377 378 b &= 0x70; 379 b >>= 4; 380 if (x != sc->lastx || y != sc->lasty || b != sc->lastb) { 381 /* Mouse state changed */ 382 buffer.status = b | ( b ^ sc->lastb) << 3 | (((x==sc->lastx) && (y==sc->lasty))?0:MOVEMENT); 383 if(sc->sc_mode == MOUSEMODE_REL) { 384 sc->origx = sc->origy = 0; 385 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEX, sc->origx); 386 bus_space_write_4(sc->sc_iot, sc->sc_ioh, QMS_MOUSEY, sc->origy); 387 } 388 buffer.x = x; 389 buffer.y = y; 390 microtime(&buffer.event_time); 391 392 if (sc->sc_buffer.c_cc == 0) 393 dosignal = 1; 394 395 s = spltty(); 396 (void)b_to_q((char *)&buffer, sizeof(buffer), &sc->sc_buffer); 397 (void)splx(s); 398 selwakeup(&sc->sc_rsel); 399 400 if (sc->sc_state & QMOUSE_ASLEEP) { 401 sc->sc_state &= ~QMOUSE_ASLEEP; 402 wakeup((caddr_t)sc); 403 } 404 405 /* if (dosignal)*/ 406 psignal(sc->sc_proc, SIGIO); 407 408 sc->lastx = x; 409 sc->lasty = y; 410 sc->lastb = b; 411 } 412 413 FMT_END 414 return(0); /* Pass interrupt on down the chain */ 415 } 416 417 418 int 419 qmspoll(dev, events, p) 420 dev_t dev; 421 int events; 422 struct proc *p; 423 { 424 struct qms_softc *sc = qms_cd.cd_devs[minor(dev)]; 425 int revents = 0; 426 int s = spltty(); 427 428 if (events & (POLLIN | POLLRDNORM)) { 429 if (sc->sc_buffer.c_cc > 0) 430 revents |= events & (POLLIN | POLLRDNORM); 431 else 432 selrecord(p, &sc->sc_rsel); 433 } 434 435 (void)splx(s); 436 return (revents); 437 } 438 439 440 #ifdef MOUSE_IOC_ACK 441 static void 442 qmsputbuffer(sc, buffer) 443 struct qms_softc *sc; 444 struct mousebufrec *buffer; 445 { 446 int s; 447 int dosignal = 0; 448 449 /* Time stamp the buffer */ 450 microtime(&buffer->event_time); 451 452 if (sc->sc_buffer.c_cc == 0) 453 dosignal=1; 454 455 s = spltty(); 456 (void)b_to_q((char *)buffer, sizeof(*buffer), &sc->sc_buffer); 457 (void)splx(s); 458 selwakeup(&sc->sc_rsel); 459 460 if (sc->sc_state & QMOUSE_ASLEEP) { 461 sc->sc_state &= ~QMOUSE_ASLEEP; 462 wakeup((caddr_t)sc); 463 } 464 465 if (dosignal) 466 psignal(sc->sc_proc, SIGIO); 467 } 468 #endif 469 470 /* End of qms.c */ 471