Home | History | Annotate | Line # | Download | only in iomd
qms.c revision 1.3
      1 /*	$NetBSD: qms.c,v 1.3 2002/10/23 09:10:43 jdolecek 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.si_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.si_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