Home | History | Annotate | Line # | Download | only in isa
mms.c revision 1.6.2.2
      1 /*-
      2  * Copyright (c) 1993 Charles Hannum
      3  * Copyright (c) 1992, 1993 Erik Forsberg.
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  *
     12  * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
     13  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     14  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
     15  * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     16  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     17  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     18  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     19  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     20  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     21  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     22  *
     23  *	$Id: mms.c,v 1.6.2.2 1993/09/30 17:33:03 mycroft Exp $
     24  */
     25 
     26 #include "param.h"
     27 #include "kernel.h"
     28 #include "systm.h"
     29 #include "buf.h"
     30 #include "malloc.h"
     31 #include "ioctl.h"
     32 #include "tty.h"
     33 #include "file.h"
     34 #include "select.h"
     35 #include "proc.h"
     36 #include "vnode.h"
     37 #include "sys/device.h"
     38 
     39 #include "i386/isa/isavar.h"
     40 #include "i386/include/mouse.h"
     41 #include "i386/include/pio.h"
     42 
     43 #define MMS_ADDR	0	/* Offset for register select */
     44 #define MMS_DATA	1	/* Offset for InPort data */
     45 #define MMS_IDENT	2	/* Offset for identification register */
     46 #define	MMS_NPORTS	4
     47 
     48 #define	MMS_CHUNK	128	/* chunk size for read */
     49 #define	MMS_BSIZE	1024	/* buffer size */
     50 
     51 struct mms_softc {		/* Driver status information */
     52 	struct	device sc_dev;
     53 	struct	isadev sc_id;
     54 	struct	intrhand sc_ih;
     55 
     56 	struct	ringbuf {	/* Input queue */
     57 		int rb_count, rb_first, rb_last;
     58 		char rb_data[MMS_BSIZE];
     59 	} sc_q;
     60 	struct	selinfo sc_rsel;
     61 	u_short	sc_iobase;	/* I/O port base */
     62 	u_char	sc_flags;	/* Driver flags */
     63 #define	MMS_NOBLOCK	0x01
     64 	u_char	sc_state;	/* Mouse driver state */
     65 #define MMS_OPEN	0x01	/* Device is open */
     66 #define MMS_ASLP	0x02	/* Waiting for mouse data */
     67 	u_char	sc_status;	/* Mouse button status */
     68 	int	sc_x, sc_y;	/* accumulated motion in the X,Y axis */
     69 };
     70 
     71 static int mmsprobe __P((struct device *, struct cfdata *, void *));
     72 static void mmsforceintr __P((void *));
     73 static void mmsattach __P((struct device *, struct device *, void *));
     74 static int mmsintr __P((void *));
     75 
     76 struct cfdriver mmscd =
     77 { NULL, "mms", mmsprobe, mmsattach, sizeof (struct mms_softc) };
     78 
     79 #define MMSUNIT(dev)	(minor(dev) >> 1)
     80 #define	MMSFLAGS(dev)	(minor(dev) & 0x01)
     81 
     82 static int
     83 mmsprobe(parent, cf, aux)
     84 	struct device *parent;
     85 	struct cfdata *cf;
     86 	void *aux;
     87 {
     88 	struct	isa_attach_args *ia = aux;
     89 	u_short	iobase = ia->ia_iobase;
     90 
     91 	/* Read identification register to see if present */
     92 	if (inb(iobase + MMS_IDENT) != 0xde)
     93 		return 0;
     94 
     95 	/* Seems it was there; reset */
     96 	outb(iobase + MMS_ADDR, 0x87);
     97 
     98 	/* XXXX isa_discoverintr */
     99 
    100 	/* reset again to disable interrupts */
    101 	outb(iobase + MMS_ADDR, 0x87);
    102 
    103 	ia->ia_iosize = MMS_NPORTS;
    104 	ia->ia_drq = DRQUNK;
    105 	ia->ia_msize = 0;
    106 	return 1;
    107 }
    108 
    109 static void
    110 mmsforceintr(aux)
    111 	void *aux;
    112 {
    113 	struct	isa_attach_args *ia = aux;
    114 	u_short	iobase = ia->ia_iobase;
    115 
    116 	/* enable interrupts; expect to get one in 1/60 second */
    117 	outb(iobase + MMS_ADDR, 0x07);
    118 	outb(iobase + MMS_DATA, 0x09);
    119 }
    120 
    121 static void
    122 mmsattach(parent, self, aux)
    123 	struct device *parent, *self;
    124 	void *aux;
    125 {
    126 	struct	mms_softc *sc = (struct mms_softc *)self;
    127 	struct	isa_attach_args *ia = aux;
    128 	u_short	iobase = ia->ia_iobase;
    129 
    130 	/* other initialization done by mmsprobe */
    131 	sc->sc_iobase = iobase;
    132 	sc->sc_state = 0;
    133 
    134 	printf(": Microsoft mouse\n");
    135 
    136 	/* XXXX isa_establishintr */
    137 }
    138 
    139 int
    140 mmsopen(dev, flag)
    141 	dev_t dev;
    142 	int flag;
    143 {
    144 	int	unit = MMSUNIT(dev);
    145 	struct	mms_softc *sc;
    146 	u_short	iobase;
    147 
    148 	if (unit >= mmscd.cd_ndevs)
    149 		return ENXIO;
    150 	sc = mmscd.cd_devs[unit];
    151 	if (!sc)
    152 		return ENXIO;
    153 
    154 	if (sc->sc_state & MMS_OPEN)
    155 		return EBUSY;
    156 
    157 	sc->sc_state |= MMS_OPEN;
    158 	sc->sc_status = 0;
    159 	sc->sc_x = sc->sc_y = 0;
    160 	sc->sc_q.rb_count = sc->sc_q.rb_first = sc->sc_q.rb_last = 0;
    161 
    162 	/* enable interrupts */
    163 	iobase = sc->sc_iobase;
    164 	outb(iobase + MMS_ADDR, 0x07);
    165 	outb(iobase + MMS_DATA, 0x09);
    166 
    167 	return 0;
    168 }
    169 
    170 int
    171 mmsclose(dev, flag)
    172 	dev_t dev;
    173 	int flag;
    174 {
    175 	int	unit = MMSUNIT(dev);
    176 	struct	mms_softc *sc = mmscd.cd_devs[unit];
    177 
    178 	/* disable interrupts */
    179 	outb(sc->sc_iobase + MMS_ADDR, 0x87);
    180 
    181 	sc->sc_state &= ~MMS_OPEN;
    182 
    183 	return 0;
    184 }
    185 
    186 int
    187 mmsread(dev, uio, flag)
    188 	dev_t dev;
    189 	struct uio *uio;
    190 	int flag;
    191 {
    192 	int	unit = MMSUNIT(dev);
    193 	struct	mms_softc *sc = mmscd.cd_devs[unit];
    194 	int	s;
    195 	int	error;
    196 	size_t	length;
    197 	u_char	buffer[MMS_CHUNK];
    198 
    199 	/* Block until mouse activity occured */
    200 
    201 	s = spltty();
    202 	while (sc->sc_q.rb_count == 0) {
    203 		if (sc->sc_flags & MMS_NOBLOCK) {
    204 			splx(s);
    205 			return EWOULDBLOCK;
    206 		}
    207 		sc->sc_state |= MMS_ASLP;
    208 		if (error = tsleep((caddr_t)sc, PZERO | PCATCH, "mmsrea", 0)) {
    209 			sc->sc_state &= ~MMS_ASLP;
    210 			splx(s);
    211 			return error;
    212 		}
    213 	}
    214 
    215 	/* Transfer as many chunks as possible */
    216 
    217 	while (sc->sc_q.rb_count > 0 && uio->uio_resid > 0) {
    218 		length = min(sc->sc_q.rb_count, uio->uio_resid);
    219 		if (length > sizeof(buffer))
    220 			length = sizeof(buffer);
    221 
    222 		/* Remove a small chunk from input queue */
    223 
    224 		if (sc->sc_q.rb_first + length >= MMS_BSIZE) {
    225 			size_t	left = MMS_BSIZE - sc->sc_q.rb_first;
    226 			bcopy(&sc->sc_q.rb_data[sc->sc_q.rb_first], buffer,
    227 			      left);
    228 			bcopy(sc->sc_q.rb_data, &buffer[left], length - left);
    229 		} else
    230 			bcopy(&sc->sc_q.rb_data[sc->sc_q.rb_first], buffer,
    231 			      length);
    232 
    233 		sc->sc_q.rb_first = (sc->sc_q.rb_first + length) % MMS_BSIZE;
    234 		sc->sc_q.rb_count -= length;
    235 
    236 		/* Copy data to user process */
    237 
    238 		if (error = uiomove(buffer, length, uio))
    239 			break;
    240 	}
    241 
    242 	/* reset counters */
    243 	sc->sc_x = sc->sc_y = 0;
    244 
    245 	splx(s);
    246 	return error;
    247 }
    248 
    249 int
    250 mmsioctl(dev, cmd, addr, flag)
    251 	dev_t dev;
    252 	int cmd;
    253 	caddr_t addr;
    254 	int flag;
    255 {
    256 	int	unit = MMSUNIT(dev);
    257 	struct	mms_softc *sc = mmscd.cd_devs[unit];
    258 	struct	mouseinfo info;
    259 	int	s;
    260 	int	error;
    261 
    262 	switch (cmd) {
    263 	    case MOUSEIOCREAD:
    264 		s = spltty();
    265 
    266 		info.status = sc->sc_status;
    267 		if (sc->sc_x || sc->sc_y)
    268 			info.status |= MOVEMENT;
    269 
    270 		if (sc->sc_x > 127)
    271 			info.xmotion = 127;
    272 		else if (sc->sc_x < -127)
    273 			/* bounding at -127 avoids a bug in XFree86 */
    274 			info.xmotion = -127;
    275 		else
    276 			info.xmotion = sc->sc_x;
    277 
    278 		if (sc->sc_y > 127)
    279 			info.ymotion = 127;
    280 		else if (sc->sc_y < -127)
    281 			info.ymotion = -127;
    282 		else
    283 			info.ymotion = sc->sc_y;
    284 
    285 		sc->sc_x = 0;
    286 		sc->sc_y = 0;
    287 		sc->sc_status &= ~BUTCHNGMASK;
    288 
    289 		splx(s);
    290 		error = copyout(&info, addr, sizeof(struct mouseinfo));
    291 		break;
    292 
    293 	    default:
    294 		error = EINVAL;
    295 		break;
    296 	}
    297 
    298 	return error;
    299 }
    300 
    301 int
    302 mmsintr(arg)
    303 	void *arg;
    304 {
    305 	struct	mms_softc *sc = (struct mms_softc *)arg;
    306 	u_short	iobase = sc->sc_iobase;
    307 	u_char	buttons, changed, status;
    308 	char	dx, dy;
    309 
    310 	if ((sc->sc_state & MMS_OPEN) == 0)
    311 		/* interrupts not expected */
    312 		return 0;
    313 
    314 	/* Freeze InPort registers (disabling interrupts) */
    315 	outb(iobase + MMS_ADDR, 0x07);
    316 	outb(iobase + MMS_DATA, 0x29);
    317 
    318 	outb(iobase + MMS_ADDR, 0);
    319 	status = inb(iobase + MMS_DATA);
    320 
    321 	if (status & 0x40) {
    322 		outb(iobase + MMS_ADDR, 1);
    323 		dx = inb(iobase + MMS_DATA);
    324 		dx = (dx == -128) ? -127 : dx;
    325 		outb(iobase + MMS_ADDR, 2);
    326 		dy = inb(iobase + MMS_DATA);
    327 		dy = (dy == -128) ? 127 : -dy;
    328 	} else
    329 		dx = dy = 0;
    330 
    331 	/* Unfreeze InPort Registers (re-enables interrupts) */
    332 	outb(iobase + MMS_ADDR, 0x07);
    333 	outb(iobase + MMS_DATA, 0x09);
    334 
    335 	buttons = status & BUTSTATMASK;
    336 	changed = status & BUTCHNGMASK;
    337 	sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed;
    338 
    339 	if (dx || dy || changed) {
    340 		int	last = sc->sc_q.rb_last;
    341 		char	*cp = &sc->sc_q.rb_data[last];
    342 		int	count = sc->sc_q.rb_count;
    343 
    344 		/* Update accumulated movements */
    345 		sc->sc_x += dx;
    346 		sc->sc_y += dy;
    347 
    348 		if ((count += 5) > MMS_BSIZE)
    349 			return 1;
    350 		sc->sc_q.rb_count = count;
    351 
    352 #define	next() \
    353 		if (++last >= MMS_BSIZE) {	\
    354 			last = 0;		\
    355 			cp = sc->sc_q.rb_data;	\
    356 		} else				\
    357 			cp++;
    358 		*cp = 0x80 | (~status & BUTSTATMASK);
    359 		next();
    360 		*cp = dx;
    361 		next();
    362 		*cp = dy;
    363 		next();
    364 		*cp = 0;
    365 		next();
    366 		*cp = 0;
    367 		next();
    368 		sc->sc_q.rb_last = last;
    369 
    370 		if (sc->sc_state & MMS_ASLP) {
    371 			sc->sc_state &= ~MMS_ASLP;
    372 			wakeup((caddr_t)sc);
    373 		}
    374 		selwakeup(&sc->sc_rsel);
    375 	}
    376 
    377 	return 1;
    378 }
    379 
    380 int
    381 mmsselect(dev, rw, p)
    382 	dev_t dev;
    383 	int rw;
    384 	struct proc *p;
    385 {
    386 	int	unit = MMSUNIT(dev);
    387 	struct	mms_softc *sc = mmscd.cd_devs[unit];
    388 	int	s;
    389 	int	ret;
    390 
    391 	if (rw == FWRITE)
    392 		return 0;
    393 
    394 	s = spltty();
    395 	if (sc->sc_q.rb_count)
    396 		ret = 1;
    397 	else {
    398 		selrecord(p, &sc->sc_rsel);
    399 		ret = 0;
    400 	}
    401 	splx(s);
    402 
    403 	return ret;
    404 }
    405