Home | History | Annotate | Line # | Download | only in isa
lms.c revision 1.7
      1 /*-
      2  * Copyright (c) 1992, 1993 Erik Forsberg.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *
     11  * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
     12  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
     14  * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     15  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     16  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     17  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     18  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     19  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     20  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     21  *
     22  *	$Id: lms.c,v 1.7 1993/12/20 09:06:17 mycroft Exp $
     23  */
     24 
     25 #include "lms.h"
     26 #if NLMS > 0
     27 
     28 #include <sys/param.h>
     29 #include <sys/kernel.h>
     30 #include <sys/systm.h>
     31 #include <sys/buf.h>
     32 #include <sys/malloc.h>
     33 #include <sys/ioctl.h>
     34 #include <sys/tty.h>
     35 #include <sys/file.h>
     36 #ifdef NetBSD
     37 #include <sys/select.h>
     38 #endif
     39 #include <sys/proc.h>
     40 #include <sys/vnode.h>
     41 
     42 #include <machine/mouse.h>
     43 #include <machine/pio.h>
     44 
     45 #include <i386/isa/isa_device.h>
     46 
     47 #define DATA	0       /* Offset for data port, read-only */
     48 #define SIGN	1       /* Offset for signature port, read-write */
     49 #define INTR	2       /* Offset for interrupt port, read-only */
     50 #define CNTRL	2       /* Offset for control port, write-only */
     51 #define CONFIG	3	/* for configuration port, read-write */
     52 
     53 #define LMSUNIT(dev)	(minor(dev) >> 1)
     54 
     55 #ifndef min
     56 #define min(x,y) (x < y ? x : y)
     57 #endif  min
     58 
     59 int lmsprobe (struct isa_device *);
     60 int lmsattach (struct isa_device *);
     61 
     62 static int lmsaddr[NLMS];	/* Base I/O port addresses per unit */
     63 
     64 #define MSBSZ	1024		/* Output queue size (pwr of 2 is best) */
     65 
     66 struct ringbuf {
     67 	int count, first, last;
     68 	char queue[MSBSZ];
     69 };
     70 
     71 static struct lms_softc {	/* Driver status information */
     72 	struct ringbuf inq;	/* Input queue */
     73 #ifdef NetBSD
     74 	struct selinfo rsel;
     75 #else
     76 	pid_t	rsel;		/* Process selecting for Input */
     77 #endif
     78 	unsigned char state;	/* Mouse driver state */
     79 	unsigned char status;	/* Mouse button status */
     80 	unsigned char button;	/* Previous mouse button status bits */
     81 	int x, y;		/* accumulated motion in the X,Y axis */
     82 } lms_softc[NLMS];
     83 
     84 #define OPEN	1		/* Device is open */
     85 #define ASLP	2		/* Waiting for mouse data */
     86 
     87 struct isa_driver lmsdriver = { lmsprobe, lmsattach, "lms" };
     88 
     89 int lmsprobe(struct isa_device *dvp)
     90 {
     91 	int ioport = dvp->id_iobase;
     92 	int val;
     93 
     94 	/* Configure and check for port present */
     95 
     96 	outb(ioport+CONFIG, 0x91);
     97 	DELAY(10);
     98 	outb(ioport+SIGN, 0x0C);
     99 	DELAY(10);
    100 	val = inb(ioport+SIGN);
    101 	DELAY(10);
    102 	outb(ioport+SIGN, 0x50);
    103 
    104 	/* Check if something is out there */
    105 
    106 	if (val == 0x0C && inb(ioport+SIGN) == 0x50)
    107 		return(4);
    108 
    109 	/* Not present */
    110 
    111 	return(0);
    112 }
    113 
    114 int lmsattach(struct isa_device *dvp)
    115 {
    116 	int unit = dvp->id_unit;
    117 	int ioport = dvp->id_iobase;
    118 	struct lms_softc *sc = &lms_softc[unit];
    119 
    120 	/* Save I/O base address */
    121 
    122 	lmsaddr[unit] = ioport;
    123 
    124 	/* Disable mouse interrupts */
    125 
    126 	outb(ioport+CNTRL, 0x10);
    127 
    128 	/* Setup initial state */
    129 
    130 	sc->state = 0;
    131 
    132 	/* Done */
    133 
    134 	return(0);
    135 }
    136 
    137 int lmsopen(dev_t dev, int flag, int fmt, struct proc *p)
    138 {
    139 	int unit = LMSUNIT(dev);
    140 	struct lms_softc *sc;
    141 	int ioport;
    142 
    143 	/* Validate unit number */
    144 
    145 	if (unit >= NLMS)
    146 		return(ENXIO);
    147 
    148 	/* Get device data */
    149 
    150 	sc = &lms_softc[unit];
    151 	ioport = lmsaddr[unit];
    152 
    153 	/* If device does not exist */
    154 
    155 	if (ioport == 0)
    156 		return(ENXIO);
    157 
    158 	/* Disallow multiple opens */
    159 
    160 	if (sc->state & OPEN)
    161 		return(EBUSY);
    162 
    163 	/* Initialize state */
    164 
    165 	sc->state |= OPEN;
    166 #ifdef NetBSD
    167 	sc->rsel.si_pid = 0;
    168 	sc->rsel.si_coll = 0;
    169 #else
    170 	sc->rsel = 0;
    171 #endif
    172 	sc->status = 0;
    173 	sc->button = 0;
    174 	sc->x = 0;
    175 	sc->y = 0;
    176 
    177 	/* Allocate and initialize a ring buffer */
    178 
    179 	sc->inq.count = sc->inq.first = sc->inq.last = 0;
    180 
    181 	/* Enable Bus Mouse interrupts */
    182 
    183 	outb(ioport+CNTRL, 0);
    184 
    185 	/* Successful open */
    186 
    187 	return(0);
    188 }
    189 
    190 int lmsclose(dev_t dev, int flag, int fmt, struct proc *p)
    191 {
    192 	int unit, ioport;
    193 	struct lms_softc *sc;
    194 
    195 	/* Get unit and associated info */
    196 
    197 	unit = LMSUNIT(dev);
    198 	sc = &lms_softc[unit];
    199 	ioport = lmsaddr[unit];
    200 
    201 	/* Disable further mouse interrupts */
    202 
    203 	outb(ioport+CNTRL, 0x10);
    204 
    205 	/* Complete the close */
    206 
    207 	sc->state &= ~OPEN;
    208 
    209 	/* close is almost always successful */
    210 
    211 	return(0);
    212 }
    213 
    214 int lmsread(dev_t dev, struct uio *uio, int flag)
    215 {
    216 	int s;
    217 	int error = 0;	/* keep compiler quiet, even though initialisation
    218 			   is unnecessary */
    219 	unsigned length;
    220 	struct lms_softc *sc;
    221 	unsigned char buffer[100];
    222 
    223 	/* Get device information */
    224 
    225 	sc = &lms_softc[LMSUNIT(dev)];
    226 
    227 	/* Block until mouse activity occured */
    228 
    229 	s = spltty();
    230 	while (sc->inq.count == 0) {
    231 		if (minor(dev) & 0x1) {
    232 			splx(s);
    233 			return(EWOULDBLOCK);
    234 		}
    235 		sc->state |= ASLP;
    236 		error = tsleep((caddr_t)sc, PZERO | PCATCH, "lmsrea", 0);
    237 		if (error != 0) {
    238 			splx(s);
    239 			return(error);
    240 		}
    241 	}
    242 
    243 	/* Transfer as many chunks as possible */
    244 
    245 	while (sc->inq.count > 0 && uio->uio_resid > 0) {
    246 		length = min(sc->inq.count, uio->uio_resid);
    247 		if (length > sizeof(buffer))
    248 			length = sizeof(buffer);
    249 
    250 		/* Remove a small chunk from input queue */
    251 
    252 		if (sc->inq.first + length >= MSBSZ) {
    253 			bcopy(&sc->inq.queue[sc->inq.first],
    254 		 	      buffer, MSBSZ - sc->inq.first);
    255 			bcopy(sc->inq.queue, &buffer[MSBSZ-sc->inq.first],
    256 			      length - (MSBSZ - sc->inq.first));
    257 		}
    258 		else
    259 			bcopy(&sc->inq.queue[sc->inq.first], buffer, length);
    260 
    261 		sc->inq.first = (sc->inq.first + length) % MSBSZ;
    262 		sc->inq.count -= length;
    263 
    264 		/* Copy data to user process */
    265 
    266 		error = uiomove(buffer, length, uio);
    267 		if (error)
    268 			break;
    269 	}
    270 
    271 	sc->x = sc->y = 0;
    272 
    273 	/* Allow interrupts again */
    274 
    275 	splx(s);
    276 	return(error);
    277 }
    278 
    279 int lmsioctl(dev_t dev, caddr_t addr, int cmd, int flag, struct proc *p)
    280 {
    281 	struct lms_softc *sc;
    282 	struct mouseinfo info;
    283 	int s, error;
    284 
    285 	/* Get device information */
    286 
    287 	sc = &lms_softc[LMSUNIT(dev)];
    288 
    289 	/* Perform IOCTL command */
    290 
    291 	switch (cmd) {
    292 
    293 	case MOUSEIOCREAD:
    294 
    295 		/* Don't modify info while calculating */
    296 
    297 		s = spltty();
    298 
    299 		/* Build mouse status octet */
    300 
    301 		info.status = sc->status;
    302 		if (sc->x || sc->y)
    303 			info.status |= MOVEMENT;
    304 
    305 		/* Encode X and Y motion as good as we can */
    306 
    307 		if (sc->x > 127)
    308 			info.xmotion = 127;
    309 		else if (sc->x < -127)
    310 			info.xmotion = -127;
    311 		else
    312 			info.xmotion = sc->x;
    313 
    314 		if (sc->y > 127)
    315 			info.ymotion = 127;
    316 		else if (sc->y < -127)
    317 			info.ymotion = -127;
    318 		else
    319 			info.ymotion = sc->y;
    320 
    321 		/* Reset historical information */
    322 
    323 		sc->x = 0;
    324 		sc->y = 0;
    325 		sc->status &= ~BUTCHNGMASK;
    326 
    327 		/* Allow interrupts and copy result buffer */
    328 
    329 		splx(s);
    330 		error = copyout(&info, addr, sizeof(struct mouseinfo));
    331 		break;
    332 
    333 	default:
    334 		error = EINVAL;
    335 		break;
    336 		}
    337 
    338 	/* Return error code */
    339 
    340 	return(error);
    341 }
    342 
    343 void lmsintr(unit)
    344 	int unit;
    345 {
    346 	struct lms_softc *sc = &lms_softc[unit];
    347 	int ioport = lmsaddr[unit];
    348 	char hi, lo, dx, dy, buttons, changed;
    349 
    350 	outb(ioport+CNTRL, 0xAB);
    351 	hi = inb(ioport+DATA) & 15;
    352 	outb(ioport+CNTRL, 0x90);
    353 	lo = inb(ioport+DATA) & 15;
    354 	dx = (hi << 4) | lo;
    355 	dx = (dx == -128) ? -127 : dx;
    356 
    357 	outb(ioport+CNTRL, 0xF0);
    358 	hi = inb(ioport+DATA);
    359 	outb(ioport+CNTRL, 0xD0);
    360 	lo = inb(ioport+DATA);
    361 	outb(ioport+CNTRL, 0);
    362 	dy = ((hi & 15) << 4) | (lo & 15);
    363 	dy = (dy == -128) ? 127 : -dy;
    364 
    365 	buttons = (~hi >> 5) & 7;
    366 	changed = buttons ^ sc->button;
    367 	sc->button = buttons;
    368 	sc->status = buttons | (sc->status & ~BUTSTATMASK) | (changed << 3);
    369 
    370 	/* Update accumulated movements */
    371 
    372 	sc->x += dx;
    373 	sc->y += dy;
    374 
    375 	/* If device in use and a change occurred... */
    376 
    377 	if (sc->state & OPEN && (dx || dy || changed)) {
    378 		sc->inq.queue[sc->inq.last++] = 0x80 | (buttons ^ BUTSTATMASK);
    379 		sc->inq.queue[sc->inq.last++ % MSBSZ] = dx;
    380 		sc->inq.queue[sc->inq.last++ % MSBSZ] = dy;
    381 		sc->inq.queue[sc->inq.last++ % MSBSZ] = 0;
    382 		sc->inq.queue[sc->inq.last++ % MSBSZ] = 0;
    383 		sc->inq.last = sc->inq.last % MSBSZ;
    384 		sc->inq.count += 5;
    385 
    386 		if (sc->state & ASLP) {
    387 			sc->state &= ~ASLP;
    388 			wakeup((caddr_t)sc);
    389 		}
    390 #ifdef NetBSD
    391 		selwakeup(&sc->rsel);
    392 #else
    393 		if (sc->rsel) {
    394 			selwakeup(sc->rsel, 0);
    395 			sc->rsel = 0;
    396 		}
    397 #endif
    398 	}
    399 }
    400 
    401 int lmsselect(dev_t dev, int rw, struct proc *p)
    402 {
    403 	int s, ret;
    404 	struct lms_softc *sc = &lms_softc[LMSUNIT(dev)];
    405 
    406 	/* Silly to select for output */
    407 
    408 	if (rw == FWRITE)
    409 		return(0);
    410 
    411 	/* Return true if a mouse event available */
    412 
    413 	s = spltty();
    414 	if (sc->inq.count)
    415 		ret = 1;
    416 	else {
    417 #ifdef NetBSD
    418 		selrecord(p, &sc->rsel);
    419 #else
    420 		sc->rsel = p->p_pid;
    421 #endif
    422 		ret = 0;
    423 	}
    424 	splx(s);
    425 
    426 	return(ret);
    427 }
    428 #endif
    429