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